概述
2023 年 7月初,我们捕获到了一个未知 DDoS Botnet 家族样本,该样本诸多基础函数从 Mirai 源码移植而来,但在主机行为、网络控制协议方面又做了很大改动。样本中的多个核心函数由 "ripper_" 开头,所以我们把这个家族命名为 Ripper。
我们对其样本和 C&C 的关联分析,发现该家族已经传播了数月时间,只不过之前的样本与现在的样本变化比较大。经过梳理,我们暂时把 Ripper 家族的样本分为 3 个版本:V0、V1 和 最新的 V2。
Ripper 的传播机制借鉴自 Mirai:样本中内置 Telnet 弱口令扫描模块,扫描模块会把扫描结果上报给 Report Server,攻击者会根据上报的扫描结果,控制 Loader 向失陷主机植入 Ripper 的木马文件。最新的 Ripper V2 中,C&C 还会通过特定的指令动态更新样本扫描用到的弱口令字典。从我们的威胁监控系统中可以看到,Ripper V2 最近在 7.4 日到 7.6 日有一波积极传播:
样本关键行为分析
以如下 Ripper V2 样本为例,我们对样本关键特性进行分析:
- | - | - | - |
---|---|---|---|
文件名 | 文件类型 | 文件大小 | 文件MD5 |
loki.arm6 | ELF 32-bit LSB executable, ARM | 105760 bytes | c244f9422006848e3893d0b8b8fc6b8f |
初始化准备工作
首先,样本会尝试用以下命令重置 iptables 规则:
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -t NAT -F
iptables -t mangle -F
iptables -t NAT -F
iptables -F
iptable -X
紧接着,Ripper V2 会在控制台输出 "exec\r\n",从 Mirai 移植的 rand_init() 做随机函数初始化,然后通过 init_load_address() 函数设定样本工作需要用到的 C&C 服务器地址和端口,最后在本地开启一个 Socks5 服务,端口为 63254,可以用做 Socks5 代理节点。Socks5 服务开启成功后,在控制台输出 "START-S5\n"。
Ripper V2 设定了 3 组工作用的 C&C 服务器,其中地址相同,只是端口不同:
- util_srv_addr: 自己搭建的域名解析服务地址,在攻击指令解析环节和 Socks5 代理服务中用来解析目标域名,端口 15757
- cnc_srv_addr: C&C 控制的服务器地址,端口 15636
- report_srv_addr: 扫描结果上报的服务器地址,端口 20216
Ripper V2 的 C&C 地址字符串经过自定义编码逻辑进行变形,要经过 lets_encrypt() 函数经过解码。对于内置的 C&C "15.217.144.198" 来说,解码的结果就是把首位字符和末位字符对调位置,得出 "85.217.144.191" 就是其 C&C IP。
C&C 通信
首先是连接 C&C 服务器,Ripper V2 用 UDP 协议与 C&C 通信:
上线
Ripper V2 的 Bot 向 C&C 发送的数据包有 3 种:
- | - |
---|---|
指令代码 | 说明 |
0x55 | 上线 |
0x29 | 上报 Socks5 服务端口 |
0x36 | Pong |
成功连接 C&C 之后,样本会立即向 C&C 发送上线包。上线包的内容中第一个字节是上线包的指令代码 0x55,后面紧接着就是样本的启动参数(bot_tag)。拼接好之后交给 cnc_send() 函数来发送:
cnc_send() 函数会给需要发送的数据加上前缀和后缀,作为数据起始和结尾的标志:
- 前缀: "\x00\x01\x02"
- 后缀: "\x02\x01\x00"
这样一来,假如样本启动路径为 "/tmp/loki",那么最终发送出去的上线包数据如下:
00000000 00 01 02 55 2f 74 6d 70 2f 6c 6f 6b 69 02 01 00 |...U/tmp/loki...|
00000010|
如果前面成功启动了 Socks5 服务,紧接着 Ripper V2 还会把 Socks5 服务的端口(63254)上报给 C&C 服务器。上报 Socks5 服务的指令代码为 0x29,同样也是交给 cnc_send() 函数发送上报数据,数据包如下:
指令解析
Ripper V2 的样本目前支持以下控制指令:
- | - | - |
---|---|---|
指令代码 | 说明 | 备注 |
0x33 | 启动扫描任务 | 如果 recv data length>2,代表同时下发了弱口令字典 |
0xde | 重发上线包 | |
0xdf | Kill Bot | |
0xed | 停止 DDoS 攻击 | |
0xef | 发起 DDoS 攻击 | |
Ping | 无指令码("\x00\x01\x02\PING\x02\x01\x00\x00") |
C&C 发给 Bot 的指令,也都会附带一个前缀和后缀字节串,并且与 Bot 发给 C&C 的不同,此处的前缀是 "\x00\x01\x02",后缀是 "\x02\x01\x00\x00"。Bot 样本收到 C&C 发来的数据后,会通过检测前缀和后缀来确定一个完整的指令数据包,先剔除前缀和后缀的字节串,再对指令内容做解析。
样本上线成功后,C&C 一般会先下发指令启动扫描任务,并附带一组扫描用到的弱口令,数据包如下:
注意弱口令列表的数据,每一对弱口令之间会用 "0xFF" 来做间隔字符,后面会调用一个 util_tokenize() 函数来解析这些用 "0xFF" 来隔开的多组弱口令:
当 cmd_code 为 0xef 时,Ripper V2 的 Bot 会发起 DDoS 攻击,此时指令数据包的结构伪码如下:
struct cmd_pkt {
uint8[3] prefix; // "\x00\x01\x02"
uint8 cmd_code;
uint32 target_ip;
uint16 target_port;
uint32 duration;
uint8 atk_code;
uint8 atk_flag_cnt;
atk_flag[atk_flag_cnt] flags;
uint8[4] subffix; // "\x02\x01\x00\x00"
}
struct atk_flag {
uint8 flag_type;
uint8 flag_len; // flag 数据长度(bytes)
char[flag_len] flag_data; // flag 数据,最长 64 bytes
}
Ripper V2 支持的 DDoS 攻击方式共有 7 种:
- | - |
---|---|
attack code | attack method |
0 | UDP Flood |
1 | UDP Plain Flood |
2 | UDP Raw Flood |
3 | TCP Plain Flood |
4 | TCP RAW Flood |
5 | MIX Flood |
6 | HTTP Flood |
Ripper V2 的 Attack Flag 数据结构的设计从 Mirai 移植而来,但具体支持的 Attack Flag 与 Mirai 大不相同,Ripper V2 支持 6 种自定义的 Flag:
- | - |
---|---|
flag code | flag 说明 |
1 | 域名 |
2 | Attack payload size |
3 | TCP Flag ([a |
4 | Socket Option(non_block |
5 | 发起攻击的轮数 |
6 | 每一轮的攻击次数 |
Ripper V2 的攻击指令解析逻辑比较复杂, 它把 Mirai 中所有指令解析、发起攻击的函数全部打散,以内联的形式写到了一个函数中,整个函数体积特别大,对逆向分析会造成一定程度的阻力,函数结构如下:
如果 Bot 收到的攻击指令中,包含针对域名目标的攻击,Ripper V2 会先调用自定义的域名解析函数 resolve_address(),访问自己搭建的服务对域名进行解析:
如上图所示,Bot 访问自己的服务(util_srv,地址与 C&C 地址一致,端口为 15757),通过 UDP 协议,发送的 Payload 第一个字节为固定的 "\x01",第二个字节为请求解析域名的长度,后面附带域名字符串,Payload 结构伪码如下:
struct domain_resolv_payload {
_uint8 code; // "\x01"
_uint8 domain_len;
char[domain_len] domain_str;
}
然后 Bot 从服务端读取数据,如果读取数据长度为 4,则说明域名解析成功,返回了一个 4 bytes 的 IP。
样本层面的版本演进
我们发现的 3 个版本的 Ripper 家族样本,功能细节差异比较大,但也有明显的共同特征,正是基于这些共同特征,我们把它们归为同一家族:
- 关键函数均以 "ripper_" 开头,也有其他大量的相同功能和名字的函数
- V0 和 V1 版本的文件名均为 "boat.{CPU_ARCH}"
- 都有相同的 util_tokenize() 函数,为一些函数做基于特定分隔符的切分、解析,喜欢用 0xFF 作为分隔符
- 网络数据包都设计成带有特定前缀和和后缀字节串的形式
接下来我们会对这些特性做详细分析。
Ripper V0 样本:
- | - | - | - |
---|---|---|---|
文件名 | 文件类型 | 文件大小 | 文件MD5_ |
boat.arm7 | ELF 32-bit LSB executable, ARM | 94644 bytes | b73116306de407394bee825ff4d95091 |
在终端连续输出以下 3 串符串:
__"Komorebi\\r\\n"__
__"Domorebi\\r\\n"__
__"Xomorebi\\r\\n"__
C&C 明文硬编码在样本中:
Ripper V0 指令特征:
- 所有的指令以 "\r\n\n\r" 4 个字节为结尾标志
- 指令中各字段以 "0xFF" 为分隔符
各指令总结如下:
- | - | - |
---|---|---|
方向 | cmd code | 功能 |
Bot->C2 | 0x00 | 上线 |
C2->Bot | 0x01 | 停止 DDoS 攻击 |
C2->Bot | 0x00 | 发起 DDoS 攻击 |
Ripper V0 支持的 DDoS 攻击方式及对应的 Attack Code:
上线包:第一个字节为 0x00,第二个字符为分割标志字符 0xFF,第三部分为第一个命令行启动参数,最后为数据包结尾标志: "\r\n\n\r"
指令包也都是以 "\r\n\n\r" 结尾,样本中会把结尾 4 个字符去掉再解析、执行指令:
在函数 cmd_handle() 中,样本会把从 C&C 收到的数据调用 util_tokenize() 函数,以 "0xFF" 为分隔符做切分、重组,最后再根据解析好的指令执行相应动作:
util_tokenize() 函数反编译结果如下:
Ripper V1 样本
- | - | - | - |
---|---|---|---|
文件名 | 文件类型 | 文件大小 | 文件MD5 |
boat.arm7 | ELF 32-bit LSB executable, ARM | 136411 bytes | e9c5048652f4263d9daf4a3af5c3709d |
在终端输出字符串: "EXECUTED\r\n"
C&C 仍然硬编码在样本中,不过同一个 C&C IP 开了两个服务端口:
- 5900,常规 C&C 服务端口,用来控制 Bot 行为
- 5901,API Server 端口,用来给 Bot 下发工作中用到的数据,比如 Scanner 口令、Table Entry 相关数据等
Ripper V1 的上线包比 Ripper V0 包含的信息更丰富,各字段说明如下:
- | - | - | - |
---|---|---|---|
field | offset | length(bytes) | default value |
cmd_code | 0 | 1 | 0x0 |
bot_version | 0 | 4 | "\x00\x00\x00\x40" |
bot_id(argv[0]) | 4 | len(bot_id) | |
bot_edianess | len(bot_id)+4 | 1 | |
bot_cpu_arch | len(bot_id)+5 | 1 | |
bot_cpu_cores | len(bot_id)+6 | 1 | |
bot_mem_mb | len(bot_id)+7 | 4 |
其中第一个字节为固定的 "0x00",代表上线包;接下来是 4 字节的 bot_version,固定值为 "\x00\x00\x00\x40",后面每个字段时间都用 "0xFF" 分隔:
样本从 C&C 的 API Server 中给你接收到的数据,也是以 "0xFF" 作为字段分隔标志,并用与 Ripper V0 几乎相同的 util_tokenize() 函数来解析、重组数据:
Ripper V1 攻击指令包的结构与 Ripper V0 也大不相同,Ripper V0 的攻击指令数据中,各字段会以 "0xFF" 分隔,最后以 "\r\n\n\r" 结尾;Ripper V1 的指令包,采用了固定长度的结构,各个字段之间无分隔符。结构如下:
- | - | - |
---|---|---|
field | offset | length |
UNKOWN | 0 | 1 |
attack code | 1 | 1 |
target host | 2 | 4 |
target port | 6 | 2 |
duration | 8 | 4 |
其中第一个字节会被直接丢弃:
然后解析其余数据,提取字段、重组后面 DDoS 攻击要用到的结构体:
Ripper V1 支持的 DDoS 攻击方式以及对应的指令码如下:
- | - |
---|---|
attack code | attack type |
0 | ICMP Flood |
1 | UDP Plain Flood |
2 | TCP Plain Flood |
3 | TCP Flood |
4 | HTTP Flood |
总结
目前我们已经捕获到 Ripper V2 发起的数十次攻击,其攻击目标包括数据货币交易所 1million.exchange,时代华纳的 DNS 服务器 74.74.74.74,以及英国独立报官网 www.independent.co.uk。
鉴于 Ripper 在样本层面的频繁变动持续升级,以及逐步开展 DDoS 攻击,我们会对 ripper 家族的活动持续监控。
IoC:
MD5
b73116306de407394bee825ff4d95091
364a1676d638e5069ab88cb98288bef2
4a1fb73a6f09468b8ab0fc7478fe715a
4d3312115eb5694a7e7769867fcf227a
53fa2ebff19861c6a381d802a8bef9ba
79ee10eb0c48c8bf4fa6deb342db1a99
8845e1c8daf7545bfde3ddf6729664a4
908ab5382ee42c0ab034820730d6ba09
99ab1760db8fe89b556a943afdd105ee
bc87628437819895c031ecb72a70659a
c55cbde42ff1cbf09ed07ca98ca1f017
e9c5048652f4263d9daf4a3af5c3709d
015e73f206790663b1efba854d9888ee
087b071253958042dd74d53475666d78
78f1155bc7a9c0a89a3d30992e0efb5f
1387c774b707ab1c379acc3ef882b214
c244f9422006848e3893d0b8b8fc6b8f
C2
85.217.144.191:15636
85.217.144.191:15757
85.217.144.191:20216
194.55.224.126:1738
194.55.224.182:5900
194.55.224.182:5901