Mirai源码分析

大概看了下整体结构和流程,然后细看了一下加解密部分。

代码结构:

1
2
3
4
5
6
loader/src   将payload上传到受感染的设备
mirai/bot 在受感染设备上运行的恶意payload
mirai/cnc 恶意者进行控制和管理的接口
mirai/tools 提供的一些工具

其中,cnc部分是Go语言编写的,余下都是C语言

整体网络拓扑关系如下图所示,受感染的设备扫描其他可能受感染的设备,扫描到之后就讲设备信息等提供给loader,loader将payload下载到可能受感染的设备上,比如宏病毒,js下载者等。被感染的设备与c2服务器连接,并向特定受害者发起ddos攻击。

image-20230312203320286

loader

主要功能是向感染设备上传(wget、tftp、echo方式)对应架构的payload文件

目录结构

1
2
3
4
5
6
7
headers/       头文件目录
binary.c 将bins目录下的文件读取到内存中,以echo方式上传payload文件时用到
connection.c 判断loader和感染设备telnet交互过程中的状态信息
main.c loader主函数
server.c 向感染设备发起telnet交互,上传payload文件
telnet_info.c 解析约定格式的telnet信息
util.c 一些常用的公共函数

1)通过待感染节点的telnet用户名和密码成功登录;

2)执行/bin/busybox ps,根据返回结果kill掉某些特殊进程;

3)执行/bin/busybox cat /proc/mounts,根据返回结果切换到可写目录;

4)执行/bin/busybox cat /bin/echo,通过返回结果解析/bin/echo这个ELF文件的头部来判断体系架构,即其中的e_machine字段;

5)选择一种方式上传对应的payload文件,当然首先需要进行判断

6)执行payload并清理

payload

发起DoS攻击以及扫描其它可能受感染的设备,代码在mirai/bot目录,可简单划分为如下几个模块

  1. scanner:扫描其它可能受感染的设备,如果能满足telnet弱口令登录则将结果进行上报,恶意者主要借此扩张僵尸网络
  2. attack:解析下发的攻击命令并发动DoS攻击
  3. killer:此模块主要有两个作用,其一是关闭特定的端口并占用,另一是删除特定文件并kill对应进程,简单来说就是排除异己
  4. public:主要是一些常用的公共函数,供其它几个模块调用

table.c中的table_unlock_valtable_retrieve_val和 table_lock_val函数组合,即获取table中的数据,程序中用到的一些信息是硬编码后保存到table中的,如果获取就要用到这个组合,其中涉及到简单的异或加密和解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/******table.c******
*处理硬编码在table中的数据
*/
//初始化table中的成员
void table_init(void);
//解密table中对应id的成员
void table_unlock_val(uint8_t id);
//加密table中对应id的成员
void table_lock_val(uint8_t id);
//取出table中对应id的成员
char *table_retrieve_val(int id, int *len);
//向table中添加成员
static void add_entry(uint8_t id, char *buf, int buf_len);
//和密钥key进行异或操作,即table中数据的加密或解密
static void toggle_obf(uint8_t id);

一个具体的例子如下所示。所以加密字符串可以寻找类似的\x

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//保存到table中的硬编码信息
add_entry(TABLE_EXEC_SUCCESS, "\x4E\x4B\x51\x56\x47\x4C\x4B\x4C\x45\x02\x56\x57\x4C\x12\x22", 15);

//调用table_unlock_val解密
//初始化key,其中table_key = 0xdeadbeef;
uint8_t k1 = table_key & 0xff, //0xef
k2 = (table_key >> 8) & 0xff, //0xbe
k3 = (table_key >> 16) & 0xff, //0xad
k4 = (table_key >> 24) & 0xff; //0xde
//循环异或
for (i = 0; i < val->val_len; i++)
{
val->val[i] ^= k1;
val->val[i] ^= k2;
val->val[i] ^= k3;
val->val[i] ^= k4;
}

/*解密后的信息:listening tun0
*这时调用table_retrieve_val就可以获取到所需信息
*最后调用table_lock_val加密,同table_unlock_val调用,利用的是两次异或后结果不变的性质
*不过考虑到异或的交换律和结合律,上述操作实际上也就相当于各字节异或一次0x22
*/

cnc

cnc目录主要提供用户管理的接口、处理攻击请求并下发攻击命令

1
2
3
4
5
6
admin.go      处理管理员登录、创建新用户以及初始化攻击
api.go 向感染的bot节点发送命令
attack.go 处理用户的攻击请求
clientList.go 管理感染的bot节点
database.go 数据库管理,包括用户登录验证、新建用户、处理白名单、验证用户的攻击请求
main.go 程序入口,开启23端口和101端口的监听

tools

tools目录主要提供了一些工具,相应的功能如下

1
2
3
4
5
enc.c         对数据进行异或加密处理
nogdb.c 通过修改elf文件头实现反gdb调试
scanListen.go 监听payload(bot)扫描后上报的telnet信息,并将结果交由loader处理
single_load.c 另一个loader实现
wget.c 实现了wget文件下载

enc.c中table_key = 0xdeadbeef被硬编码到里面,不过可能不是所有样本都这样(?

加解密逻辑和table.c中的基本一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
int main(int argc, char **args)
{
**************if (argc != 3)
{
printf("Usage: %s <string | ip | uint32 | uint16 | uint8 | bool> <data>\n", args[0]);
return 0;
}
// ...**************
printf("XOR'ing %d bytes of data...\n", len);
data = x(data, len);
for (i = 0; i < len; i++)
printf("\\x%02X", ((unsigned char *)data)[i]);
printf("\n");
}

void *x(void *_buf, int len)
{
unsigned char *buf = (char *)_buf, *out = malloc(len);
int i;
uint8_t k1 = table_key & 0xff,
k2 = (table_key >> 8) & 0xff,
k3 = (table_key >> 16) & 0xff,
k4 = (table_key >> 24) & 0xff;

for (i = 0; i < len; i++)
{
char tmp = buf[i] ^ k1;

tmp ^= k2;
tmp ^= k3;
tmp ^= k4;

out[i] = tmp;
}

return out;
}

参考

https://github.com/jgamblin/Mirai-Source-Code/blob/master/mirai/tools/enc.c

https://paper.seebug.org/142/#21-payload

Mirai样本分析

MD5: 9dd6dd5bd577226323ba207c5e1127e2

joesandbox : https://www.joesandbox.com/analysis/683525?idtype=analysisid#iocs

为数不多的ip地址可以直接搜索到的样本

Untitled

主要加密函数如下图所示,和源码的差不多

Untitled

密钥和源码一样

image-20230312203551379

应该还是可以先做针对通用的:已知密文的字符串特征(\x…), 密钥(0xdeadbeef), 然后解密之后再匹配ip地址或域名等。 针对更复杂的话,可能需要针对不同的样本实现方式再进一步分析。