XZ供应链后门检测方案(CVE-2024-3094)

感谢 https://github.com/amlweems/xzbot

测试环境搭建

  1. 编译环境apt install golang
  2. exp脚本git clone https://github.com/amlweems/xzbot
  3. 编译exp
1
2
cd xzbot
go build
  1. 创建指向patch的符号链接 cd assets; ln -s liblzma.so.5.6.1.patch liblzma.so.5
  2. 在assets目录下,启动sshd env -i LANG=C LD_LIBRARY_PATH=”./“ /usr/sbin/sshd -p 2222 -r
  3. 回到父目录,使用./xzbot -addr 127.0.0.1:2222 -cmd ‘id > /tmp/.xz’执行命令

后门利用

带后门的liblzma文件被成功执行后会修改plt表中RSA_public_decrypt函数使其指向自己的payload

当使用证书认证方式登录时,sshd会调用RSA_public_decrypt对证书进行解密。通过对sshd的源代码进行分析,在证书认证登录时共有两次调用RSA_public_decrypt。调用链分别是

  1. userauth_pubkey->sshkey_from_blob->cert_parse->sshkey_verify->RSA_public_decrypt
  2. userauth_pubkey->sshkey_verify->RSA_public_decrypt
    其中1最终RSA_public_decrypt的参数为ca证书的publickey,而2为key的publickey。由于2在调用sshkey_verify前会使用user_key_allowed函数判断证书是否正确,所以无法利用。
1
2
3
4
5
6
7
8
9
/* test for correct signature */
authenticated = 0;
if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) &&
PRIVSEP(sshkey_verify(key, sig, slen,
sshbuf_ptr(b), sshbuf_len(b),
(ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL,
ssh->compat, &sig_details)) == 0) {
authenticated = 1;
}

而1中在调用RSA_public_decrypt前并未做任何前置的校验,所以适合被用来RCE。SSH的证书登录一般都是直接使用公私钥对直接认证,很少会使用CA证书签名,可以看出后门利用链是经过精心选取的。
在hook后的 RSA_public_decrypt中,后门使用一个硬编码的ED448公钥进行签名验证和解密payload。如果需要触发后门需要使用自己的密钥替换这个密钥。如果签名校验失败或者payload的格式不符合,后门会回到真的RSA_public_decrypt进行后续的校验。由于与正常的SSH证书认证流程并无差别,所以无法使用全网探测或者测绘的方式找到被感染的主机。

命令执行

进程链

后门在成功触发后会使用system函数来执行命令。

调用代码使用动态获取函数地址的方式调用system函数。

检测方式

System执行

由于使用system函数执行,进程链上会有明显的特征,可用来作为检测规则。

  1. 正常的ssh登录的进程链是sshd->bash->cmd
  2. 正常ssh未登录直接执行命令的进程链是 sshd->cmd
  3. 后门链的进程链是sshd->sh -c {cmd} ->cmd

环境变量

此外由于exp执行时不会执行/root/.bashrc,所以也可通过执行命令环境变量的差异来构造检测规则

误报分析

背景研判