Summary
SALTWATER is a backdoor that has been used in the exploitation of the Barracuda 0-day vulnerability CVE-2023-2868. It is a module for the Barracuda SMTP daemon called bsmtpd. The malware hooked the recv, send, and close functions using an open-source hooking library called funchook. The following functionalities are implemented: execute arbitrary commands, download and upload files, proxy functionality, and tunneling functionality.
Technical analysis
SHA256: 1c6cad0ed66cf8fd438974e1eac0bc6dd9119f84892930cb71cb56a5e985f0a4
The malware implements hooks on the recv, send, and close functions in a method called cc_init, as shown below:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/1.jpg)
It obtains the address of the original functions using the dlsym method. The function called get_symbol_by_name returns the address (see Figure 2).
![](https://cybergeeks.tech/wp-content/uploads/2023/06/2.jpg)
As Mandiant mentioned in their report, the binary uses the funchook hooking library to construct the hooks. The following functions are called: funchook_create, funchook_prepare, and funchook_install.
![](https://cybergeeks.tech/wp-content/uploads/2023/06/3.jpg)
![](https://cybergeeks.tech/wp-content/uploads/2023/06/4.jpg)
We will now describe the hooking functions my_recv, my_send, and my_close.
Firstly, my_recv calls the original recv function, as highlighted in Figure 5:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/5.jpg)
The value stored at the gIsRecvAlready_ptr address should be 0. The process allocates a new memory area that will store the buffer to be received:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/6.jpg)
The server response is copied to the gConnectedData_ptr address and is overwritten with the “quitrn” string. The value stored at gIsRecvAlready_ptr is set to 1, which means that a successful receive operation occurred:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/7.jpg)
The malware retrieves the IP address of the peer connected to the socket passed as a parameter to the send function using getpeername:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/8-1024x402.jpg)
The original send function is called if any error occurred, as shown below:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/9.jpg)
The process implements a function called CheckRemoteIp, which converts the IP addresses from binary to text form using inet_ntop and then compares them with the value found at the gConnectedAddr_ptr address. It’s worth mentioning that the socket family is also expected to be 0xA (AF_CCITT), which corresponds to CCITT protocols:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/10.jpg)
![](https://cybergeeks.tech/wp-content/uploads/2023/06/11.jpg)
In the my_close function, the binary searches for the socket descriptor passed as a parameter in a socket descriptors list:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/12.jpg)
The malware compares the socket descriptor with the value stored at gConnectedfd_ptr and expects them to be equal; otherwise, it calls the original close method (Figure 13).
![](https://cybergeeks.tech/wp-content/uploads/2023/06/13-1.jpg)
A new thread that executes the cc_worker or Connected2Vps functions is created using the pthread_create method:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/14-1024x165.jpg)
In the Connected2Vps function, the binary implements a method called OpenConnection, which takes two parameters (IP adddress and port number) that are supposed to indicate the threat actor’s VPS infrastructure:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/15.jpg)
The process obtains a structure of type hostent for the IP address using gethostbyname and the addrinfo structure using the getaddrinfo method:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/16-1024x475.jpg)
The binary creates a new socket and connects to the C2 server using the connect method:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/17.jpg)
The malware sends the buffer found at the gConnectedData_ptr address to the server via a function call to write (see Figure 18).
![](https://cybergeeks.tech/wp-content/uploads/2023/06/18.jpg)
The select method is utilized to monitor multiple socket file descriptors, waiting for the read file descriptor to be ready for reading:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/19.jpg)
Finally, the process reads the server response using the read method:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/20.jpg)
It creates a new SSL structure and sets the socket descriptor as the input/output for network connections:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/21.jpg)
The malware expects a 21-byte structure that contains the command to be executed. SSL_read is utilized to read data from the connections:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/22.jpg)
![](https://cybergeeks.tech/wp-content/uploads/2023/06/23.jpg)
The first byte extracted from the response corresponds to the backdoor functionalities named “Channels”:
- 0 (ShellChannel)
- 1 (DownloadChannel)
- 2 (UploadChannel)
- 3 (ProxyChannel)
- 4 (TunnelArgs)
![](https://cybergeeks.tech/wp-content/uploads/2023/06/24-1024x469.jpg)
ShellChannel
The remaining structure received from the C2 server called SHELL has 20 bytes.
The server can specify a command that will be executed on the infected device:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/25.jpg)
The received command is compared with “exit” and “exitn”, which means that the process just exits. In any other case, the command is passed to the run_cmd function, as highlighted below:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/26.jpg)
![](https://cybergeeks.tech/wp-content/uploads/2023/06/27.jpg)
The popen function is used to run the desired command on the device, and the output is read using the fgets method (Figure 28).
![](https://cybergeeks.tech/wp-content/uploads/2023/06/28.jpg)
The malware implements a function called MyWriteAll, which calls the SSL_write method. It sends 4 NULL bytes to the C2 server:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/29.jpg)
![](https://cybergeeks.tech/wp-content/uploads/2023/06/30.jpg)
DownloadChannel
The remaining structure received from the C2 server called TRANSFILE has 20 bytes.
The process allocates 5MB of memory and expects that TRANSFILE[0:4], which is the file name’s length that will be created, to be <= 1022:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/31.jpg)
The file name to be created is read using the MyReadAll function, as shown in Figure 32.
![](https://cybergeeks.tech/wp-content/uploads/2023/06/32.jpg)
The open64 routine is used to create the file on the device:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/33.jpg)
The file is populated with content received from the C2 server using the lseek64 and write functions:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/34.jpg)
UploadChannel
The remaining structure received from the C2 server called TRANSFILE has 20 bytes.
The binary expects that TRANSFILE[4:8], which is the file’s length to be exfiltrated, to be <= 5MB. It calls MyReadAll in order to read the file path that will be exfiltrated to the C2 server:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/35.jpg)
The file’s length is obtained using a function called GetFileSize, which uses the stat64 method to retrieve it. The malware opens the target file via a function call to open64:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/36.jpg)
![](https://cybergeeks.tech/wp-content/uploads/2023/06/37.jpg)
The MyWriteAll function is used again to send 21 bytes to the C2 server, as shown in the figure below.
![](https://cybergeeks.tech/wp-content/uploads/2023/06/38.jpg)
Finally, the target file content is read using the lseek64 and read routines, and then sent to the C2 server:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/39.jpg)
![](https://cybergeeks.tech/wp-content/uploads/2023/06/40.jpg)
ProxyChannel
The remaining structure received from the C2 server called PROXY has 20 bytes.
The process allocates 6MB of memory and expects PROXY[12:16] to be 0, as shown below:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/41.jpg)
The first 4 bytes (PROXY[0:4]) are used to construct the Proxy IP address. The Proxy port number is derived from PROXY[10:12]:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/42.jpg)
The OpenConnection function presented above uses the recv method to receive data until a new line is received from the socket (see Figure 43).
![](https://cybergeeks.tech/wp-content/uploads/2023/06/43.jpg)
The binary creates a new SSL structure, connects it with the socket descriptor initially passed as parameter to the my_close function, and initiates the TLS/SSL handshake with the Proxy server:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/44.jpg)
The process requests a 21-byte structure from the C2 server and sends it to the Proxy server using the MyReadAll and MyWriteAll functions:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/45.jpg)
As we’ve already mentioned, the first byte determines the command to be executed. A special case occurs if the byte is equal to 2, which leads to a call to the DownloadByProxyChannel routine:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/46-1024x566.jpg)
In the DownloadByProxyChannel function, the binary reads a buffer from the C2 server and transmits it to the Proxy server:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/47.jpg)
Moving forward, the malware can read a 21-byte structure corresponding to a command from the Proxy server and sent it to the C2 server (Figure 48).
![](https://cybergeeks.tech/wp-content/uploads/2023/06/48-1024x281.jpg)
TunnelArgs
The remaining structure received from the C2 server called TUNNEL has 20 bytes.
The value TUNNEL[16:20] is expected to be different than 1 and TUNNEL[12:16] could be 0, 1, or 2:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/49.jpg)
In the first case, the bytes extracted from TUNNEL[0:4] and TUNNEL[8:12] are used to construct two IP addresses, and TUNNEL[4:8] is converted to a port number. The resulting values are stored at the gRemoteAddr_ptr, gRemotePort_ptr, and gConnectedAddr_ptr addresses (Figure 50).
![](https://cybergeeks.tech/wp-content/uploads/2023/06/50.jpg)
The process sends the command ID (4) and a 20-byte buffer that contains NULL bytes to the C2 server:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/51.jpg)
In the second case, the binary extracts the IP addresses found at gRemoteAddr_ptr and gConnectedAddr_ptr, and the port number found at gRemotePort_ptr and converts them to integer using the atoi function. The 20-byte buffer that is exfiltrated to the C2 server contains the converted values:
![](https://cybergeeks.tech/wp-content/uploads/2023/06/52.jpg)
![](https://cybergeeks.tech/wp-content/uploads/2023/06/53.jpg)
In the third case, the values stored at gRemoteAddr_ptr, gRemotePort_ptr, and gConnectedAddr_ptr are overwritten with NULL bytes (Figure 54).
![](https://cybergeeks.tech/wp-content/uploads/2023/06/54.jpg)
References
https://www.mandiant.com/resources/blog/barracuda-esg-exploited-globally
I found the SALTWATER sample from the #Barracuda ESG report on CVE-2023-2868 on VT
The funny thing is: s/o appended a 0x00 so that it got a different hash (not the one from the IOC list)
PS: I don't have the orig file. I found out by removing 1 byte.https://t.co/Sz3p4dAyYN pic.twitter.com/blpUQ71bbS
— Florian Roth (@cyb3rops) June 8, 2023
https://linux.die.net/