OpenSSH < 7.7 - Username Enumeration Exploit

by | Aug 21, 2018 | #_shelIntel, Blog, Penetration Testing

On August 15th, 2018 a vulnerability was posted on the OSS-Security list. This post explained that OpenSSH (all versions prior to and including 7.7) is vulnerable to username enumeration by sending a malformed public key authentication request (SSH2_MSG_USERAUTH_REQUEST with type “publickey”) to the service. Upon receiving this request, the server would validate the user THEN check if the request was well-formed. As a result, if an invalid user was requested the OpenSSH service would return a SSH2_MSG_USERAUTH_FAILURE indicating the server rejected the request due to an invalid username. However, if a valid user was requested the server would simply close the connection because the request was malformed.

This behavior was caused by the following code:

87 static int

88 userauth_pubkey(struct ssh *ssh)

89 {

101         if (!authctxt->valid) {

102                 debug2(“%s: disabled because of invalid user”, __func__);

103                 return 0;

104         }

105         if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0 ||

106             (r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 ||

107             (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0)

108                 fatal(“%s: parse request failed: %s”, __func__, ssh_err(r));

On lines 101-103 you can see that the program checks to see if the user is valid, and if it is not, returns 0, which gets turned into a SSH2_MSG_USERAUTH_FAILURE. However, per lines 105-108, if the server fails to validate the SSH request it then calls “fatal” and exits the process without responding to the client. This allows us to determine whether a user is valid.

I wrote the following code as a means to exploit the aforementioned vulnerability. The code is roughly based off of my own research into the vuln and the POC provided by Matthew Daley.

There were several challenges while writing this exploit. As demonstrated by Matthew Daley in his POC, the easiest way to corrupt the packet is to override the Paramiko Message object’s add_boolean function. However, once the first request is sent the original status of add_boolean must be returned or else calling Transport.start_client() to initiate another connection will fail. According to some messages I received, that stumped several other exploit developers trying to create a similar exploit.

Another challenge was that the OpenSSH sever would occasionally be overwhelmed by the influx of SSH requests causing it to refuse to negotiate the SSH transport. When this occurs, the exploit automatically attempts to retry to a max number of 3 times.

Thanks,

Justin ( @Rhynorater )

GitHub repo: https://github.com/Rhynorater/CVE-2018-15473-Exploit

ExploitDB: https://www.exploit-db.com/exploits/45233/

┌─[justin@parrot]─[~/projects/CVE-2018-15473-Exploit]

└──╼ $python sshUsernameEnumExploit.py –help

usage: sshUsernameEnumExploit.py [-h] [–port PORT] [–threads THREADS]

[–outputFile OUTPUTFILE]

[–outputFormat {list,json,csv}]

(–username USERNAME | –userList USERLIST)

hostname

positional arguments:

hostname              The target hostname or ip address

optional arguments:

-h, –help            show this help message and exit

–port PORT           The target port

–threads THREADS     The number of threads to be used

–outputFile OUTPUTFILE

The output file location

–outputFormat {list,json,csv}

The output file location

–username USERNAME   The single username to validate

–userList USERLIST   The list of usernames (one per line) to enumerate through