SolarWinds has issued a security bulletin that fixes a remote code execution vulnerability (CVE-2021-35211) in Sev-U that allows Microsoft to report to SolarWinds when it discovers an exploit in the wild and provides a proof of concept for exploiting the vulnerability. Unauthenticated remote attackers can use this vulnerability to execute arbitrary code with special permissions on the affected servers. Please take measures to protect yourself as soon as possible.

This vulnerability exists in the SSH protocol and is not related to the SUNBURST supply chain attack. It only affects SolarWinds Managed File Transfer and Sev-U Secure FTP. SSH is enabled by default when a domain is created using the Serv-U management console wizard. If SSH is not enabled in the Serv-U environment, this vulnerability is not affected.

The SSH implementation is found in serv-u by enumerating ssh-strings. An SSH instance looks like Figure 1:

 

Figure 1. Ssh-string

The researcher sets a breakpoint in the above code and tries to confirm this assumption with an SSH client connection to sev-u:

Figure 2. Call stack for breakpoints set in the code in Figure 1

At this point, the researchers found that both serv-U.dll and Rhinonet.dll had ASLR support disabled. The researchers found that they could trace the path of SSH messages by reversing the relevant code in serv-U.dll and Rhinonet.dll. To handle incoming SSH connections, serv-U.delll from RhinoNET! The CRhinoSocket class creates the CSUSSHSocket object. Lifetime of the CSUSSHSocket object is the length of the TCP connection. The underlying CRhinoSocket provides a cached interface for sockets, so a single TCP packet can contain many bytes. This indicates that a single package may also contain any number of SSH messages as well as some SSH messages. CSUSSHSocket: : ProcessRecvBuffer function is responsible for handling data from the cache socket of SSH.

CSUSSHSocket: : ProcessRecvBuffer ParseBanner check SSH version first. If ParseBanner succeeds in getting the SSH version from the banner, ProcessRecvBuffer loops through ParseMessage, gets a pointer to the current message from the socket data, and extracts the MSg_ID and Length fields from the message.

 

Figure 3. CSUSSHSocket: : ProcessRecvBuffer processing cycle code

The message data is contained in the payload cache, where the first byte is msg_id:

 

ProcessRecvBuffer then processes the message based on msg_ID. Some messages are passed directly through the message processing loop, while others are passed to sSH_PKt_OTHERS, which publishes messages to the queue for another thread to process.

In figure 4. CSUSSHSocket: : ProcessRecvBuffer certification process

If msg_id entrusted to another thread, CSSHSession: : OnSSHMessage will be responsible for handling. This function mainly handles messages that need to interact with serv-U administration user profile data and UI updates. Because CSSHSession: : OnSSHMessage need to success for user interaction, so also didn’t find loopholes.

When fuzzing the Sev-U, it became clear that the application found some inclusion exceptions, such as logging errors, system crashes, and so on. These actions can improve the uptime of a file server application or cause a possible content crash. Attackers can use this opportunity to launch attacks such as brute force cracking.

During testing, the researchers found some anomalies, such as read/write access violations, that could cause crashes:

 

Figure 5. WinDbg shows a crash caused by SSH messages generated by a fuzzy test

As mentioned above, CRYPTO_ctr128_encrypt in libeay32.dll attempts to call an invalid address. OpenSSL version 1.0.2U is used. Here are the related OpenSSL functions:

 

In the meantime, here is the structure of the processing:

The crash function is obtained from the OpenSSL API through the following path:

EVP_EncryptUpdate -> evp_EncryptDecryptUpdate -> aes_ctr_cipher -> CRYPTO_ctr128_encrypt

Further analysis of the call stack, found Serv – U will from CSUSSHSocket: : EVP_EncryptUpdate ParseMessage call, as shown below:

 

Figure 6. Where OpenSSL is called, the attacker controlled function pointer may be called

At this point, the researchers operated on a minimal TCP packet cache through tests that were small enough to trigger the minimum SSH messages needed for a crash.

 

The researchers found that the root cause of the problem was that Serv-U created OpenSSL AES128-CTR code, as shown below:

Call EVP_EncryptInit_ex with a NULL key or empty IV. Serv-u does this because the context is created while processing the KEXINIT message. But THE AES key extension is not executed until the key is set, and the CTX -> CIpher_data data remains uninitialized until the key extension is executed. Therefore, the researchers speculate that the sequence of messages causes enc_ALGO_client_to_server ->decrypt to be called before the key is initialized. Serv -u KEXINIT Handler creates objects for all parameters in the message. However, the corresponding object is not replaced by the newly created object when the NEWKEYS message is processed. The client completes the key exchange process during a normal SSH connection before publishing the NEWKEYS message. Serv-U handles NEWKEYS regardless of connection state or key exchange.