版权声明:本文.NETlab原创,依据 CC BY-SA 4.0 许可证进行授权,转载请附上出处链接及本声明。
2021年3月25日,360 NETLAB的BotMon系统发现一个的VT 0检测的可疑ELF文件(MD5=64f6cfe44ba08b0babdd3904233c4857),它会与4个业务类型截然不同的域名进行通信,端口均为TCP 443(HTTPS),但流量却并非TLS/SSL类型,这个异常行为引起了我们的兴趣,进一步分析发现它是一个针对linux X64系统的后门木马,该家族至少已经存在3年但目前还是0检测。基于该家族使用rotate加密,并且运行后对root/non-root账户有不同的行为,犹如一只双头龙,一体双向,我们将它命名为RotaJakiro
。
RotaJakiro隐蔽性较强,对加密算法使用比较多,包括:使用AES算法加密样本内的资源信息;C2通信综合使用了AES,XOR,ROTATE加密和ZLIB压缩算法。指令方面,RotaJakiro支持12种指令码,其中3种是和特定plugin相关的,遗憾的是目前我们并没有捕获到这类payload,因此并不知道它的真正目的。从广义的后门角度来看,RotaJakiro支持的功能可以归纳成以下4类:
当所有分析结束后,我们尝试对RotaJakiro进行溯源
,根据解密后的资源以及编码的风格的相似性,我们推测它是Torii Botnet作者的又一作品。
我们从捕获的样本出发,寻找RotaJakiro同源者,最终发现了以下4个样本,它们在VT上都是0检测,从VT的First Seen时间来看,RotaJakiro至少已经存在了3年。
FILENAME | MD5 | DETECTION | FIRST SEEN IN VT |
---|---|---|---|
systemd-daemon | 1d45cd2c1283f927940c099b8fab593b | 0/61 | 2018-05-16 04:22:59 |
systemd-daemon | 11ad1e9b74b144d564825d65d7fb37d6 | 0/58 | 2018-12-25 08:02:05 |
systemd-daemon | 5c0f375e92f551e8f2321b141c15c48f | 0/56 | 2020-05-08 05:50:06 |
gvfsd-helper | 64f6cfe44ba08b0babdd3904233c4857 | 0/61 | 2021-01-18 13:13:19 |
这批样本都内嵌了以下4个C2,目前它们在VT上也是0检测。这4个C2域名有非常接近的Crteated Updated Expired
时间,我们推测它们一直以来用于同一个业务,从这个角度来看,RotaJakiro背后的团伙至少已经活动了6年。
DOMAIN | DETECTION | CREATED | LAST UPDATED | EXPIRED |
---|---|---|---|---|
news.thaprior.net | 0/83 | 2015-12-09 06:24:13 | 2020-12-03 07:24:33 | 2021-12-09 06:24:13 |
blog.eduelects.com | 0/83 | 2015-12-10 13:12:52 | 2020-12-03 07:24:33 | 2021-12-10 13:12:52 |
cdn.mirror-codes.net | 0/83 | 2015-12-09 06:24:19 | 2020-12-03 07:24:32 | 2021-12-09 06:24:19 |
status.sublineover.net | 0/83 | 2015-12-09 06:24:24 | 2020-12-03 07:24:32 | 2021-12-09 06:24:24 |
4个RotaJakiro样本,时间分布从2018到2021,它们的功能非常接近,本文选取2021年的样本为分析对象,它的基本信息如下:
MD5:64f6cfe44ba08b0babdd3904233c4857
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, stripped
Packer:No
从编码层面来说,RotaJakiro采用了动态AES,双层加密的通信协议等技术来对抗安全人员的二进制&网络流量分析。
从功能层面来说,RotaJakiro运行时首先会判断当时用户是root,还是non-root,不同的账户有不同的执行策略,然后使用AES&ROTATE解密出相关的敏感资源供后续的持久化,进程守护和单一实例使用,最后和C2建立通信,等待执行C2下发的指令。
下文将从上述角度出发剖析RotaJakiro具体实现。
动态生成AES加密算法所需的常量表,防止算法被直接识别
使用stack strings obfuscation技术存储加密的敏感资源信息
使用双层加密的网络通信
RotaJakiro中所有的敏感资源都是加密的,在IDA中我们可以看出解密方法dec_proc调用了60次,它是由AES,Rotate俩部分组成。
AES解密入口如下所示:
其中aes_dec的采用的是AES-256, CBC模式,key&iv都是硬编码。
key
14 BA EE 23 8F 72 1A A6 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
iv
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Rotate解密入口如下所示:
所谓Rotate即循环移位,可以看出此处使用的循环左移,其中移位的次数由plain_len(明文长度)&7
的值决定。
以解密以下C2密文为例:
ff ba a2 3b cd 5b 7b 24 8c 5f e3 4b fc 56 5b 99
ac 91 cf e3 9a 27 d4 c9 6b 39 34 ce 69 ce 18 60
其与解密相关的各种参数如下图所示,密文长度为32字节,明文长度为26字节
首先使用AES解密后得到以下“次级密文”:
然后从次级密文
中取出有效密文,其中有效密文
从第8字节开始,长度为明文长度减8,此处即为26-8=18字节。
98 1B DB D9 8B 59 19 5D 59 1B 59 D8 1D DC 8B D8
DB 5B
最后通过明文长度26可以计算26&7=2,得到移位的次数,将上述有效密文
逐字节左移2位,就能得到C2明文。
blog.eduelects.com
RotaJakiro在实现持久功能时,对root/non-root用户做了区分,不同的账号采用了不同的技术。
根据不同Linux系统发行版本,创建相应的自启动脚本/etc/init/systemd-agent.conf
或者/lib/systemd/system/systemd-agent.service
。
Content of systemd-agent.conf
-----------------------------
#system-daemon - configure for system daemon
#This service causes system have an associated
#kernel object to be started on boot.
description "system daemon"
start on filesystem or runlevel [2345]
exec /bin/systemd/systemd-daemon
respawn
Content of systemd-agent.service
-----------------------------
[Unit]
Description=System Daemon
Wants=network-online.target
After=network-online.target
[Service]
ExecStart=/usr/lib/systemd/systemd-daemon
Restart=always
[Install]
用于伪装的文件名,俩者2选1
/bin/systemd/systemd-daemon
/usr/lib/systemd/systemd-daemon
创建桌面环境的自启动脚本$HOME/$.config/autostart/gnomehelper.desktop
[Desktop Entry]
Type=Application
Exec=$HOME/.gvfsd/.profile/gvfsd-helper
修改.bashrc文件,创建shell环境的自启动脚本
# Add GNOME's helper designed to work with the I/O abstraction of GIO
# this environment variable is set, gvfsd will not start the fuse filesystem
if [ -d ${HOME} ]; then
${HOME}/.gvfsd/.profile/gvfsd-helper
fi
用于用于伪装的文件名,俩者同时存在
$HOME/.dbus/sessions/session-dbus
$HOME/.gvfsd/.profile/gvfsd-helper
RotaJakiro实现了进程守护以保护自身的运行,和持久化一样,对root/non-root用户有不同的实现方式。
在root账号下运行时,根据不同Linux系统发行版本,通过向服务的配置文件中写入Restart=always
或者respawn
,当服务进程被结束时,会自动创建新进程。
实际效果如下图所示,可以看到systemd-daemon进程被结束后,立马就生成了新进程。
在non-root账号下运行时,RotaJakiro生成session-dbus
和gvfsd-helper
俩个进程,它们监控着彼此的存活,当其中一方被结束时,另一方将其恢复,这是非常典型的双进程保护。
RotaJakiro的双进程保护是如何实现的呢?首先以shmget API创建一片共享内存,session-dbus和gvfsd-helper通过这片共享内存实现进程间通信,告诉对方的自己的PID。然后通过/proc/[PID]
目录,动态地获取进程的存活情况。当发现对方进程死亡时,通过execvp创建进程,帮助死亡一方“复活”,大致流程如下图所示:
实际效果如下图所示,可以看到session-dbus
和gvfsd-helper
被kill -9结束后,新进程立马就创建了。
RotaJakiro通过文件锁来实现单一实例,具体实现如下所示:
其中用到的lockfile
在root/non-root账号下有所不同。
root下的lockfile,2选1
/usr/lib32/.X11/X0-lock
/bin/lib32/.X11/X0-lock
non-root下的lockfile,同时存在
$HOME/.X11/X0-lock
$HOME/.X11/.X11-lock
以实际中non-root账号为例,通过/proc/locks
可以将进程以及文件锁对应起来,此时再执行对应的RotaJakiro的样本,可以看到并不会有对应的新进程创建。
RotaJakiro通过以下代码片段和C2建立通信,等待执行后续指令:
这个过程可以分成2个阶段
通过前文所述的解密算法解密出C2列表,目前样本中内置了以下4个C2:
news.thaprior.net
blog.eduelects.com
cdn.mirror-codes.net
status.sublineover.net
RotaJakiro首先会尝试和它们建立连接,然后通过以下代码片段构造上线信息,
接着将上线信息加密并发送给C2
最后接收C2的回包,解密并校验其合法性,若通过校验,进入Stage 2。
通过以下代码片段接收并执行C2下发的指令:
目前RotaJakiro一共支持12条指令,指令码与功能的对应关系如下表所示:
CMDID | FUNCTION |
---|---|
0x138E3E6 | Exit |
0x208307A | Test |
0x5CCA727 | Heartbeat |
0x17B1CC4 | Set C2 timeout time |
0x25360EA | Streal Senstive Info |
0x18320e0 | Upload Device Info |
0x2E25992 | Deliver File/Plugin |
0x2CD9070 | Query File/Plugin Status |
0x12B3629 | Delete File/Plugin Or Dir |
0x1B25503 | Run Plugin_0x39C93E |
0x1532E65 | Run Plugin_0x75A7A2 |
0x25D5082 | Run Plugin_0x536D01 |
其中Run Plugin
功能复用相同的代码,通过以下逻辑实现函数调用:
我们目前被没有捕获到这类payload,因此用Plugin_“参数”的形式来表示不同的任务。
RotaJakiro的网络通信包如下图所示,由head,key,payload
三部分组成,其中header是必须的,长度为82字节,而body&payload部分是可选的。head&key采用的XOR&Rotate加密,payload采用AES&ZLIB加密压缩。
下面我们将通过BOT与C2的一轮交互,来说明网络流量head&key&payload
的组成以及解密过程。
读取前0x52字节,就是head的内容。head如何解密呢?方法很简单,逐字节左移3位,然后和0x1b异或
即可,解密后得下以内容:
00000000 16 11 10 b9 03 b1 0c fb 04 20 00 00 00 08 00 e0 |...¹.±.û. .....à|
00000010 20 83 01 c2 20 64 20 01 e2 00 00 00 00 c2 0c 00 | .. d .â....Â..|
00000020 00 00 32 42 36 39 33 33 34 46 38 34 31 44 30 44 |..2B69334F841D0D|
00000030 39 46 41 30 36 35 38 45 43 33 45 32 39 46 41 44 |9FA0658EC3E29FAD|
00000040 34 39 c8 53 e6 9c 48 c4 8b 77 24 2e 02 1c 96 d9 |49ÈSæ.HÄ.w$....Ù|
00000050 81 28
------------filed parse------------------
offset 0x09, 4 bytes--->payload length
offset 0x0d, 2 bytes--->body length
offset ox0f, 4 bytes--->cmdid
通过字段解析,可知key的长度为0x8字节,payload的长度为0x20字节,要执行的指令码为0x18320e0,即上报设备信息。
从偏移0x52读取8字节就得到了keyea 9a 1a 18 18 44 26 a0
,使用和head一样的解密方法,得到4c cf cb db db 39 2a 1e
,它是作为AES的密钥来解密payload。
从偏移0x5a读取32字节,就得到了下面的payload:
54 c1 c3 69 00 18 31 e4 a2 5b 10 7f 67 ab d1 4b
b2 7b 3d 3f b3 bc 66 6a 26 f6 f6 b3 f7 2e 66 6d
使用解密后的key做为AES-256的密钥,以CBC模式解密以上数据得下以下内容:
3b c7 f8 9b 73 2b d1 04 78 9c e3 60 60 60 d8 df d9 c1 71 56 f7 6f 00 00 13 80 04 28
第8字节起即为ZLIB
压缩数据,解压得到如下内容:
08 00 00 00 bf 89 88 08 cd 2d fd 50
------------filed parse------------------
offset 0, 4 bytes--->length
解压后的payload有什么用呢?它是做为新的AES密钥,用来解密部分敏感资源信息。
例如Bot在收集设备信息时,有一项是当前操作系统发行版本的信息,它是通过cat /etc/*release | uniq
命令实现的。
root@debian:~# cat /etc/*release | uniq
PRETTY_NAME="Debian GNU/Linux 9 (stretch)"
NAME="Debian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
cat /etc/*release | uniq
这条命令正是以下密文通过新的AES密钥配合下图中的参数解密而来。
cmd ciphertxt
---------------------------
74 00 dd 79 e6 1e aa bb 99 81 7e ca d9 21 6b 81
6b d9 9d 14 45 73 6a 1c 61 cc 28 a3 0f 2b 41 5a
6b 33 8c 37 25 89 47 05 44 7e f0 6b 17 70 d8 ca
当BOT接收到C2的“上报设备信息”指令后,会向C2发送以下数据,可以看出key部分的值依然是ea 9a 1a 18 18 44 26 a0
。
从上文已知解密后key值为4c cf cb db db 39 2a 1e
,通过这个值将Bot发往C2的payload解密解压后,得到如下数据,正是设备的各种信息,其中有前文提到的通过cat /etc/*release | uniq
获取的信息,验证了我们的分析是正确的。
Torii僵尸网络于2018年9月20日被友商Avast曝光,对比RotaJakiro,俩者的相似之处体现在以下3方面:
RotaJakiro&Torii的敏感资源解密后,我们发现它们复用了大量相同的命令。
1:semanage fcontext -a -t bin_t '%s' && restorecon '%s'
2:which semanage
3:cat /etc/*release
4:cat /etc/issue
5:systemctl enable
6:initctl start
...
在构造流量的过程中,大量使用常数,构造方式非常接近。
从安全研究人员进行逆向工程的角度来说,RotaJakiro&Torii有着相常相似的风格:使用加密算法隐藏敏感资源,都实现了相当old-school式的持久化,结构化的网络流量等。
基于这些考量,我们推测RotaJakiro和Torii出自同一个团伙之手。
至此RotaJakiro的逆向与溯源告一段落,但真正的工作远没结束,有许多问题依然没有答案:“RotaJakiro是怎么传播的,它的目的是什么?”,“RotaJakiro是否有特定的攻击目标,是不是APT?”,“RotaJakiro与Torii背后的黑手是谁?”......由于我们的视野有限,目前只能向安全社区分享这么多。如果社区有相关的线索,欢迎与我们联系,让我们一起Make Cyber Security Great Again
。