此次分析的样本同样是一个发生在真实应急场景中的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源码分析。
那么可以总结出如下的信息:
- 样本中可得配置信息:加密后的配置信息是以硬编码的形式在样本中。
- 加解密函数可定位:样本通常使用xor的方式进行加密,在样本中搜索对应架构的xor指令可以定位加解密函数(理论上还可以采用其他的加解密方式)。
- 加解密函数使用特征:样本在使用配置信息前后,会依次调用解密函数
table_unlock_val
和加密函数table_lock_val
。
因此,对该样本进行分析的思路就是:
- 寻找样本中所有使用到xor的函数,判断是否涉及到加解密操作。
- 寻找到加解密函数后,根据在样本中的交叉引用,确定加解密函数。
- 定位样本配置信息初始化的函数
table_init
。
- 定位样本中的
add_entry
函数获取到加密后的配置信息,并根据解密函数逻辑进行解密。
定位和确认加解密函数
在IDA中搜索所有的MIPS xor指令,通过简单分析,得到如下的三个函数可能是加解密相关函数:
- sub_409DCB
- sub_40A6F8
- sub_40A7DC
其中sub_40A6F8和sub_40A7DC的逻辑基本相同,且xor操作都使用到了敏感数据:0xDEADBEEF,因此可以判断这两个函数极大可能是加解密函数。
三个函数的反编译结果如下:
sub_409DCB
sub_40A6F8
sub_40A7DC
在文章开头也提到过,mirai使用table
中的结构体有一个特征:那就是先使用函数table_unlock_val
将对应table_id
的条目解密,使用完毕后再使用函数table_lock_val
加密。因此,可以通过查看如上三个函数的交叉引用,查看是否有相似的匹配特征。最终确定了:
- sub_40A7DC =
table_unlock_val
- sub_40A6F8 =
table_lock_val
如下是一个典型的mirai bot使用数据的特征:
定位配置信息初始化函数table_init
mirai bot的配置信息例如C2等在函数table_init
中进行初始化,攻击配置例如弱口令字典是在函数attack_init
中进行初始化。在此处关注的是对配置信息的提取。
在无符号样本中寻找table_init
的思路是:
- 根据函数
table_lock_val
或者其他函数寻找到全局变量table
- 根据全局变量
table
的交叉引用,来寻找函数table_init
在函数table_lock_val
中,传入配置table_id
,来对table
进行数据的操作:
继续查看table
的交叉引用,最后定位到table_init
。在table_init
中会多次将硬编码的配置信息存入到table
中,因此特征很明显。如下是交叉引用(已经恢复部分符号)以及table_init
中进行初始化配置:
解密脚本编写
那么编写解密函数的思路也很简单了:
- 在函数
table_init
中分析util_memcpy
(手动恢复的符号)使用到的硬编码加密数据和数据长度。
- 根据函数
table_unlock_val
的解密逻辑对数据进行解密
table_unlock_val
的反编译逻辑如下,实则非常简单,就是使用了mirai的默认加密算法:数据的每个字节逐个和0xdeadbeef进行异或。
解密脚本如下:
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")
start = 0x0040A8C0 end = 0x0040BD0C
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: return tmp_args each_op = idc.prev_head(each_op) return None
def get_op_string(addr): str_addr_list = list(idautils.DataRefsFrom(addr)) 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 return data, i
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() 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"]) encrypt(arg1, key)
main()
|
解密结果如下(不打码),可以看到其中的C2:king.badplayer.net
和sc.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
|