CVE-2020-1730: libssh - denial of service when handling AES-CTR (or DES) ciphers
libssh is a multiplatform C library implementing the SSHv2 protocol on client and server side. Recently a security advisory was released for “Denial of service vulnerability” in libssh.
Important information from advisory -
Possible DoS in client and server when handling AES-CTR keys with OpenSSL
Connection hasn't been fully initialized
Issue arises in cleanup of ciphers when closing the connection
The typical libssh session looks like following diagram. This diagram is created for high level overview of process, so it may not be exactly precise.
Let’s go through each point mentioned in advisory -
1. Affected when handling AES-CTR keys with OpenSSL
The AES-CTR cipher related information is defined in libcrypto.c. The exploitation is possible when OPENSSL AES support is enabled and OpenSSL EVP interface is not supported.
For this analysis I used aes128-ctr mode and a connection from client-to-server.
2. Connection hasn't been fully initialized
To trigger this issue, another condition is that connection should not be fully initialized. The typical SSH connection session looks like -
To trigger this condition, connection needs to be disconnected in between session key exchange so that it’s not fully initialized. The session key exchange is handled by ssh_handle_key_exchange().This function internally calls multiple interesting functions.
The ssh_handle_packets_termination() keeps polling the current session for an event and call the appropriate callbacks.
3. Cleanup cipher information
During session, if connection ended abruptly, the termination function fct(user) = -2 is returned and polling is stopped.
Then function ssh_handle_key_exchange() returns without a success.
When connection is closed abruptly and key exchange is failed, application kills the session by calling ssh_disconnect(session).
As the name suggests, function cyrpto_free() starts cleaning of cryptographic parameters used in session.
crypto->in_cipher contains ciphers information received from another party during key exchange. When the connection is closed without successful key exchange, in crypto->in_cipher some cryptographic material remains uninitialized.
The cleanup function is called on this uninitialized cipher information.
The cleanup function which is defined with specific cipher i.e. aes128-ctr is called.
The cryptographic material used for the session contains sensitive information. To clean this sensitive information explicit_bzero() function is called. This function guarantees that compiler optimizations will not remove the erase operation if the compiler deduces that the operation is “unnecessary”.
The explicit_bzero() variant behaves the same, but will not be removed by a compiler’s dead store optimization pass, making it useful for clearing sensitive memory such as a password.
It is assumed that the block which is going to be cleaned i.e. void*__s is not a null pointer. In our scenario exactly the same case triggers, the connection is not fully initialized, so value of cipher->aes_key is null pointer.
When explicit_bzero() function tries to clean the “cipher->aes_key” information, the result is “segmentation fault” error: