双头暗影魔龙(双头龙RotaJakiro)
双头暗影魔龙(双头龙RotaJakiro)systemd-daemonFIRST SEEN IN VTFileNAMEMD5DETECTION
概述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类:
- 上报设备信息
- 窃取敏感的信息
- 文件/Plugin管理(查询,下载,删除)
- 执行特定的Plugin
当所有分析结束后,我们尝试对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用户做了区分,不同的账号采用了不同的技术。
针对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账号的进程守护实现在root账号下运行时,根据不同Linux系统发行版本,通过向服务的配置文件中写入Restart=always或者respawn,当服务进程被结束时,会自动创建新进程。
实际效果如下图所示,可以看到systemd-daemon进程被结束后,立马就生成了新进程。
针对non-root账号的进程守护实现在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个阶段
- Stage 1,初始化阶段:解密出C2列表,和C2建立连接,发送上线信息,接回并解密C2返回的信息。
- Stage 2,业务阶段:验证C2的返回信息,若通过验证,执行C2后续下发的指令。
通过前文所述的解密算法解密出C2列表,目前样本中内置了以下4个C2:
news.thaprior.net
blog.eduelects.com
cdn.mirror-codes.net
status.sublineover.net
RotaJakiro首先会尝试和它们建立连接,然后通过以下代码片段构造上线信息,
接着将上线信息加密并发送给C2
最后接收C2的回包,解密并校验其合法性,若通过校验,进入Stage 2。
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 Botnet团伙的关系Torii僵尸网络于2018年9月20日被友商Avast曝光,对比RotaJakiro,俩者的相似之处体现在以下3方面:
1:字符串相似性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
...
2:流量相似性
在构造流量的过程中,大量使用常数,构造方式非常接近。
3:功能相似性从安全研究人员进行逆向工程的角度来说,RotaJakiro&Torii有着相常相似的风格:使用加密算法隐藏敏感资源,都实现了相当old-school式的持久化,结构化的网络流量等。
基于这些考量,我们推测RotaJakiro和Torii出自同一个团伙之手。
冰山一角至此RotaJakiro的逆向与溯源告一段落,但真正的工作远没结束,有许多问题依然没有答案:“RotaJakiro是怎么传播的 它的目的是什么?”,“RotaJakiro是否有特定的攻击目标,是不是APT?”,“RotaJakiro与Torii背后的黑手是谁?”......由于我们的视野有限,目前只能向安全社区分享这么多。如果社区有相关的线索,欢迎与我们联系,让我们一起Make Cyber Security Great Again。