OneShell

I fight for a brighter tomorrow

0%

mirai源码分析:实战真实mirai样本2-无符号

此次分析的样本同样是一个发生在真实应急场景中的mirai样本,与上一个分析的样本不同,该样本去掉了符号,如果直接硬看还是很费力的。

1
2
$ file ninja.mip.virus
ninja.mip.virus: ELF 32-bit MSB executable, MIPS, MIPS-I version 1 (SYSV), statically linked, stripped

在分析前,同样需要明确分析的目的:提取样本中的配置信息,弱口令字典等。配置信息中通常需要重点关注CNC地址,如果有设备或者仿真执行,也可以通过抓包的方式获取,弱口令字典需要看样本中是否包含了爆破模块。在此次的样本中,并没有弱口令扫描的模块,因此把重心放在了配置信息提取上。

样本分析思路

一般情况下,mirai都是使用了xor的方式来对配置信息进行加解密。在mirai源码中,配置信息在函数table_init中通过调用函数add_entry将硬编码且加密的数据逐条添加到table结构体中。在bot需要使用到相应的数据时,通过函数table_unlock_val将对应条目的table解密,等到使用完毕后,再调用table_lock_val重新将table中的条目进行加密。具体的代码实现逻辑可以参考上一个分析带符号的mirai文章,以及之前的mirai bot源码分析。

那么可以总结出如下的信息:

  1. 样本中可得配置信息:加密后的配置信息是以硬编码的形式在样本中。
  2. 加解密函数可定位:样本通常使用xor的方式进行加密,在样本中搜索对应架构的xor指令可以定位加解密函数(理论上还可以采用其他的加解密方式)。
  3. 加解密函数使用特征:样本在使用配置信息前后,会依次调用解密函数table_unlock_val和加密函数table_lock_val

因此,对该样本进行分析的思路就是:

  1. 寻找样本中所有使用到xor的函数,判断是否涉及到加解密操作。
  2. 寻找到加解密函数后,根据在样本中的交叉引用,确定加解密函数。
  3. 定位样本配置信息初始化的函数table_init
  4. 定位样本中的 add_entry函数获取到加密后的配置信息,并根据解密函数逻辑进行解密。

定位和确认加解密函数

在IDA中搜索所有的MIPS xor指令,通过简单分析,得到如下的三个函数可能是加解密相关函数:

  • sub_409DCB
  • sub_40A6F8
  • sub_40A7DC
    其中sub_40A6F8和sub_40A7DC的逻辑基本相同,且xor操作都使用到了敏感数据:0xDEADBEEF,因此可以判断这两个函数极大可能是加解密函数。
    三个函数的反编译结果如下:
    sub_409DCB
    undifined
    sub_40A6F8
    undifined
    sub_40A7DC
    undifined

在文章开头也提到过,mirai使用table中的结构体有一个特征:那就是先使用函数table_unlock_val将对应table_id的条目解密,使用完毕后再使用函数table_lock_val加密。因此,可以通过查看如上三个函数的交叉引用,查看是否有相似的匹配特征。最终确定了:

  • sub_40A7DC = table_unlock_val
  • sub_40A6F8 = table_lock_val
    如下是一个典型的mirai bot使用数据的特征:
    undifined

定位配置信息初始化函数table_init

mirai bot的配置信息例如C2等在函数table_init中进行初始化,攻击配置例如弱口令字典是在函数attack_init中进行初始化。在此处关注的是对配置信息的提取。

在无符号样本中寻找table_init的思路是:

  1. 根据函数table_lock_val或者其他函数寻找到全局变量table
  2. 根据全局变量table的交叉引用,来寻找函数table_init

在函数table_lock_val中,传入配置table_id,来对table进行数据的操作:
undifined

继续查看table的交叉引用,最后定位到table_init。在table_init中会多次将硬编码的配置信息存入到table中,因此特征很明显。如下是交叉引用(已经恢复部分符号)以及table_init中进行初始化配置:
undifined
undifined

解密脚本编写

那么编写解密函数的思路也很简单了:

  1. 在函数table_init中分析util_memcpy(手动恢复的符号)使用到的硬编码加密数据和数据长度。
  2. 根据函数table_unlock_val的解密逻辑对数据进行解密

table_unlock_val的反编译逻辑如下,实则非常简单,就是使用了mirai的默认加密算法:数据的每个字节逐个和0xdeadbeef进行异或。
undifined
解密脚本如下:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import idc
import idautils

ea = idc.get_name_ea_simple("util_memcpy")

# table_init范围内
start = 0x0040A8C0
end = 0x0040BD0C

# 根据MIPS传参规则,分析util_memcpy中的待解密数据地址
def find_args(addr):
each_op = addr + 4
tmp_args = {
"A1" : 0,
"A2" : 0
}
while each_op >= start:
op_disasm = idc.GetDisasm(each_op).upper()
if "ADDIU" in op_disasm and "A1" in op_disasm:
tmp_args["A1"] = each_op
if "LI" in op_disasm and "A2" in op_disasm:
tmp_args["A2"] = each_op
if tmp_args["A1"] != 0 and tmp_args["A2"] != 0:
# print(hex(tmp_args["A1"]), hex(tmp_args["A2"]))
return tmp_args
each_op = idc.prev_head(each_op)
return None

# 根据地址获取待解密数据
def get_op_string(addr):
str_addr_list = list(idautils.DataRefsFrom(addr))
# print(str_addr_list)
data = []
start = str_addr_list[0]
i = 0
while True:
byte = idc.get_wide_byte(start + i)
if byte == 0:
break
data.append(byte)
i += 1
# print("0x%x" % start, data)
return data, i

# 读取硬编码的table_keys
def init_key():
addr = 0x00451034
key = []
i = 0
while True:
byte = idc.get_wide_byte(addr + i)
if byte == 0:
break
key.append(byte)
i += 1
return key, i

# 真正的解密函数
def encrypt(data, key):
key1 = key[0]
key2 = key[1]
key3 = key[2]
key4 = key[3]
for j in range(0, len(data)):
data[j] ^= key1
data[j] ^= key2
data[j] ^= key3
data[j] ^= key4
result = ""
for each in data:
result += chr(each)
print(result)

def main():
key, key_len = init_key()
# 获取util_memcpy的交叉引用
for addr in idautils.CodeRefsTo(ea, 0):
if start <= addr < end:
if "JA" in idc.GetDisasm(addr).upper():
args = find_args(addr)
arg1, arg_len = get_op_string(args["A1"])
# print(arg1, arg_len)
encrypt(arg1, key)

main()

解密结果如下(不打码),可以看到其中的C2:king.badplayer.netsc.badplayer.net

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
king.badplayer.net
ï2
sc.badplayer.net
ï3
lzrd cock fest
/proc/
/exe
(deleted)
/fd
.anime
/status
dvrHelper
NiGGeR69xd
1337SoraLOADER
NiGGeRd0nks1337
X19I239124UIU
IuYgujeIqn
14Fa
ccAD
/proc/net/route
/proc/cpuinfo
BOGOMIPS
/etc/rc.d/rc.local
g1abc4dmo35hnp2lie0kjf
/dev/watchdog
/dev/misc/watchdog
/dev/FTWDT101_watchdog
/dev/netslink/
PRIVMSG
GETLOCALIP
KILLATTK
Eats8
v[0v
93OfjHZ2z
GhostWuzHere666
WsGA4@F6F
ACDB
AbAd
iaGv
shell
enable
system
sh
/bin/busybox LZRD
LZRD: applet not found
ncorrect
/bin/busybox ps
/bin/busybox kill -9
TSource Engine Query
/etc/resolv.conf
nameserver
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.8
Content-Type: application/x-www-form-urlencoded
setCookie('
refresh:
location:
set-cookie:
content-length:
transfer-encoding:
chunked
keep-alive
connection:
server: dosarrest
server: cloudflare-nginx
Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/601.7.7 (KHTML, like Gecko) Version/9.1.2 Safari/601.7.7
Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 5.1; Trident/5.0)
Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/4.0; GTB7.4; InfoPath.3; SV1; .NET CLR 3.4.53360; WOW64; en-US)
Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/4.0; FDM; MSIECrawler; Media Center PC 5.0)
Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/4.0; GTB7.4; InfoPath.2; SV1; .NET CLR 4.4.58799; WOW64; en-US)
Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; FunWebProducts)
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:25.0) Gecko/20100101 Firefox/25.0
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:21.0) Gecko/20100101 Firefox/21.0
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20100101 Firefox/24.0
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:33.0) Gecko/20100101 Firefox/33.0
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94
/dev/watchdog
/dev/misc/watchdog
assword
ogin
enter
dkaowjfirhiad1j3edjkai