概括
SALTWATER 是一个后门,已被用于利用 Barracuda 0day 漏洞CVE-2023-2868。它是 Barracuda SMTP 守护进程的一个模块,称为 bsmtpd。该恶意软件使用名为funchook 的开源挂钩库来挂钩 recv、send 和 close 函数。实现了以下功能:执行任意命令、下载和上传文件、代理功能和隧道功能。
技术分析
SHA256:1c6cad0ed66cf8fd438974e1eac0bc6dd9119f84892930cb71cb56a5e985f0a4
恶意软件在名为 cc_init 的方法中实现了对 receive、send 和 close 函数的挂钩,如下所示:
图1
它使用 dlsym 方法获取原始函数的地址。名为 get_symbol_by_name 的函数返回地址(参见图 2)。
图2
正如 Mandiant 在他们的报告中提到的,二进制文件使用funchook挂钩库来构造挂钩。调用以下函数:funchook_create、funchook_prepare 和 funchook_install。
图3
图4
现在我们将描述挂钩函数 my_recv、my_send 和 my_close。
首先,my_recv 调用原始的 recv 函数,如图 5 所示:
图5
存储在 gIsRecvAlready_ptr 地址的值应该为 0。进程分配一个新的内存区域来存储要接收的缓冲区:
图6
服务器响应被复制到 gConnectedData_ptr 地址并用“quit\r\n”字符串覆盖。gIsRecvAlready_ptr 中存储的值被设置为 1,这意味着发生了成功的接收操作:
图7
恶意软件使用 getpeername 检索连接到套接字的对等方的 IP 地址,该地址作为参数传递给发送函数:
图8
如果发生错误,就会调用原来的send函数,如下所示:
图9
该进程实现了一个名为 CheckRemoteIp 的函数,该函数使用 inet_ntop 将 IP 地址从二进制转换为文本形式,然后将它们与 gConnectedAddr_ptr 地址中找到的值进行比较。值得一提的是,套接字系列也预计为 0xA ( AF_CCITT ),它对应于 CCITT 协议:
图10
图11
在 my_close 函数中,二进制搜索在套接字描述符列表中作为参数传递的套接字描述符:
图12
恶意软件将套接字描述符与 gConnectedfd_ptr 中存储的值进行比较,并期望它们相等;否则,它调用原始 close 方法(图 13)。
图13
使用 pthread_create 方法创建执行 cc_worker 或 Connected2Vps 函数的新线程:
图14
在 Connected2Vps 函数中,二进制文件实现了一个名为 OpenConnection 的方法,该方法采用两个参数(IP 地址和端口号),这两个参数应该指示威胁参与者的 VPS 基础设施:
图15
该进程使用 gethostbyname 获取 IP 地址的 hostent 类型的结构,并使用 getaddrinfo 方法获取 addrinfo 结构:
图16
该二进制文件创建一个新套接字并使用 connect 方法连接到 C2 服务器:
图17
恶意软件通过 write 函数调用将在 gConnectedData_ptr 地址找到的缓冲区发送到服务器(参见图 18)。
图18
select方法用于监视多个socket文件描述符,等待读取文件描述符准备好读取:
图19
最后,该进程使用 read 方法读取服务器响应:
图20
它创建一个新的 SSL 结构并将套接字描述符设置为网络连接的输入/输出:
图21
该恶意软件需要一个 21 字节的结构,其中包含要执行的命令。SSL_read 用于从连接读取数据:
图22
图23
从响应中提取的第一个字节对应于名为“Channels”的后门功能:
0(外壳通道)
1(下载频道)
2(上传通道)
3(代理通道)
4(隧道参数)
图24
ShellChannel
从 C2 服务器接收到的剩余结构(称为 SHELL)有 20 个字节。
服务器可以指定将在受感染设备上执行的命令:
图25
收到的命令与“exit”和“exit\n”进行比较,表示进程刚刚退出。在任何其他情况下,命令都会传递给 run_cmd 函数,如下突出显示:
图26
图27
popen 函数用于在设备上运行所需的命令,并使用 fgets 方法读取输出(图 28)。
图28
该恶意软件实现了一个名为 MyWriteAll 的函数,该函数调用 SSL_write 方法。它向 C2 服务器发送 4 个 NULL 字节:
图29
图30
下载频道
从 C2 服务器接收到的剩余结构(称为 TRANSFILE)有 20 个字节。
该进程分配 5MB 内存,并期望 TRANSFILE[0:4](将创建的文件名长度)<= 1022:
图31
使用 MyReadAll 函数读取要创建的文件名,如图 32 所示。
图32
open64 例程用于在设备上创建文件:
图33
该文件使用 lseek64 和 write 函数填充从 C2 服务器接收的内容:
图34
上传通道
从 C2 服务器接收到的剩余结构(称为 TRANSFILE)有 20 个字节。
二进制文件预计 TRANSFILE[4:8](即要泄露的文件的长度)<= 5MB。它调用 MyReadAll 来读取将渗透到 C2 服务器的文件路径:
图35
文件的长度是使用名为 GetFileSize 的函数获取的,该函数使用 stat64 方法来检索它。恶意软件通过调用 open64 函数打开目标文件:
图36
图37
再次使用MyWriteAll函数向C2服务器发送21个字节,如下图所示。
图38
最后,使用lseek64和read例程读取目标文件内容,然后发送到C2服务器:
图39
图40
代理通道
从 C2 服务器接收到的剩余结构(称为 PROXY)有 20 个字节。
该进程分配了6MB内存,并期望PROXY[12:16]为0,如下所示:
图41
前 4 个字节 (PROXY[0:4]) 用于构造代理 IP 地址。代理端口号源自 PROXY[10:12]:
图42
上面介绍的 OpenConnection 函数使用 recv 方法接收数据,直到从套接字接收到新行为止(参见图 43)。
图43
该二进制文件创建一个新的 SSL 结构,将其与最初作为参数传递给 my_close 函数的套接字描述符连接,并启动与代理服务器的 TLS/SSL 握手:
图44
该进程从 C2 服务器请求 21 字节结构,并使用 MyReadAll 和 MyWriteAll 函数将其发送到代理服务器:
图45
正如我们已经提到的,第一个字节决定要执行的命令。如果字节等于 2,则会出现一种特殊情况,这会导致调用 DownloadByProxyChannel 例程:
图46
在 DownloadByProxyChannel 函数中,二进制文件从 C2 服务器读取缓冲区并将其传输到代理服务器:
图47
接下来,恶意软件可以从代理服务器读取与命令相对应的 21 字节结构,并将其发送到 C2 服务器(图 48)。
图48
隧道参数
从 C2 服务器接收到的剩余结构(称为 TUNNEL)有 20 个字节。
值 TUNNEL[16:20] 预计不同于 1,并且 TUNNEL[12:16] 可能是 0、1 或 2:
图49
第一种情况,从TUNNEL[0:4]和TUNNEL[8:12]中提取的字节用于构造两个IP地址,并且TUNNEL[4:8]被转换为端口号。结果值存储在 gRemoteAddr_ptr、gRemotePort_ptr 和 gConnectedAddr_ptr 地址中(图 50)。
图50
该进程将命令 ID (4) 和包含 NULL 字节的 20 字节缓冲区发送到 C2 服务器:
图51
在第二种情况下,二进制文件提取在 gRemoteAddr_ptr 和 gConnectedAddr_ptr 中找到的 IP 地址以及在 gRemotePort_ptr 中找到的端口号,并使用 atoi 函数将它们转换为整数。渗漏到 C2 服务器的 20 字节缓冲区包含转换后的值:
图52
图53
在第三种情况下,存储在 gRemoteAddr_ptr、gRemotePort_ptr 和 gConnectedAddr_ptr 中的值将被 NULL 字节覆盖(图 54)。
图54
参考
https://www.mandiant.com/resources/blog/barracuda-esg-exploited-globally
https://twitter.com/cyb3rops/status/1666726658806521857
https://linux.die.net/