在今年三月份,下面的样本引起了我的兴趣——因为是通过DGA(域名生成算法)来和C&C服务器进行通信:
例如,在4月12号,生成的前10个域名如下:
nctqrgta7o52.net khqzwlib0pun.org lun0lmr4xe7k.com qvc9inkxy3o9.com z8daz8ti3wxy.net k1ar8levktun.net 1ij05qzgta70.org 6bwdyvw5if45.com jwde7stqb0da.org 05azc52rs1yb.org
Virustotal扫面了malwr analysis上的样本,除了” Qihoo-360”其他杀软均显示样本正常。360的检测结果显示的是“HEUR/QVM07.1.0000.Malware.Gen”
恶意软件的特征码都是通过和10个字节的XOR密钥(FC 57 91 BC 75 9A 12 CC A4 26)加密的。可以在这里看到完整的文本字符串,如下:
klpszVersion gBitness kdwTimestamp dData fLength flpData@ hmainType.gsubType
这些字符串和银行木马Qadars很相似,可以在 这篇安全报告 中看出。
Qadars二进制文件包含一些硬编码的版本字符串。在我的例子中,版本字符串为3.0.0.0:
在Qadars2.0.0.0的安全智能报告中没有提及域名生成算法,而这个特征将在Qadars3.0.0.0版本中体现。
下面的列表是DGA算法的反汇编代码。这里我们直接先跳到下一节中,查看基本的DGA属性。
text:004095F0 ; BOOL __cdecl dga(void *pDomain, size_t sld_len) .text:004095F0 dga proc near ; CODE XREF: sub_409FD0+20_x0019_p .text:004095F0 .text:004095F0 charset = byte ptr -38h .text:004095F0 tlds = dword ptr -10h .text:004095F0 pDomain = dword ptr 8 .text:004095F0 sld_len = dword ptr 0Ch .text:004095F0 .text:004095F0 push ebp .text:004095F1 mov ebp, esp .text:004095F3 sub esp, 38h .text:004095F6 push ebx .text:004095F7 push esi .text:004095F8 mov [ebp+tlds], offset a_com ; ".com" .text:004095FF mov [ebp+tlds+4], offset a_org ; ".org" .text:00409606 mov [ebp+tlds+8], offset a_net ; ".net" .text:0040960D push edi .text:0040960E mov edi, edi .text:00409610 .text:00409610 loc_409610: ; CODE XREF: dga+166_x0019_j .text:00409610 mov ebx, [ebp+pDomain] .text:00409613 mov ecx, 9 .text:00409618 mov esi, offset charset ; "abcdefghijklmnopqrstuvwxyz0123456789" .text:0040961D lea edi, [ebp+charset] .text:00409620 rep movsd .text:00409622 movsb .text:00409623 test ebx, ebx .text:00409625 jz loc_409740 .text:0040962B mov edi, [ebp+sld_len] .text:0040962E cmp edi, 5 .text:00409631 jbe loc_409740 .text:00409637 cmp domain_nr, 0 .text:0040963E jnz short loc_40966D .text:00409640 push 0 .text:00409642 call ds:_time64 .text:00409648 add esp, 4 .text:0040964B push 0 .text:0040964D mov esi, eax .text:0040964F sub eax, 345600 .text:00409654 push 604800 .text:00409659 sbb edx, 0 .text:0040965C push edx .text:0040965D push eax .text:0040965E call _allrem .text:00409663 sub esi, eax .text:00409665 and esi, 7FFFFFFFh .text:0040966B jmp short loc_409673 .text:0040966D ; --------------------------------------------------------------------------- .text:0040966D .text:0040966D loc_40966D: ; CODE XREF: dga+4Ej .text:0040966D mov esi, r .text:00409673 .text:00409673 loc_409673: ; CODE XREF: dga+7Bj .text:00409673 push edi ; size_t .text:00409674 push 0 ; int .text:00409676 push ebx ; void * .text:00409677 call memset .text:0040967C lea eax, [ebp+charset] .text:0040967F add esp, 0Ch .text:00409682 lea edx, [eax+1] .text:00409685 .text:00409685 loc_409685: ; CODE XREF: dga+9A_x0019_j .text:00409685 mov cl, [eax] .text:00409687 inc eax .text:00409688 test cl, cl .text:0040968A jnz short loc_409685 .text:0040968C sub eax, edx .text:0040968E xor ecx, ecx .text:00409690 add edi, 0FFFFFFFBh .text:00409693 mov ebx, eax .text:00409695 jz short loc_4096CB .text:00409697 jmp short loc_4096A0 .text:00409697 ; --------------------------------------------------------------------------- .text:00409699 align 10h .text:004096A0 .text:004096A0 loc_4096A0: ; CODE XREF: dga+A7j .text:004096A0 ; dga+D9_x0019_j .text:004096A0 imul esi, 3E39B193h .text:004096A6 mov edx, 89F5h .text:004096AB sub edx, esi .text:004096AD and edx, 7FFFFFFFh .text:004096B3 mov esi, edx .text:004096B5 xor edx, edx .text:004096B7 mov eax, esi .text:004096B9 div ebx .text:004096BB inc ecx .text:004096BC mov al, [ebp+edx+charset] .text:004096C0 mov edx, [ebp+pDomain] .text:004096C3 mov [ecx+edx-1], al .text:004096C7 cmp ecx, edi .text:004096C9 jb short loc_4096A0 .text:004096CB .text:004096CB loc_4096CB: ; CODE XREF: dga+A5j .text:004096CB imul esi, 3E39B193h .text:004096D1 mov ecx, 89F5h .text:004096D6 sub ecx, esi .text:004096D8 and ecx, 7FFFFFFFh .text:004096DE mov eax, 55555556h .text:004096E3 imul ecx .text:004096E5 mov eax, edx .text:004096E7 shr eax, 1Fh .text:004096EA add eax, edx .text:004096EC lea eax, [eax+eax*2] .text:004096EF mov r, ecx .text:004096F5 sub ecx, eax .text:004096F7 mov ecx, [ebp+ecx*4+tlds] .text:004096FB mov eax, ecx .text:004096FD lea ecx, [ecx+0] .text:00409700 .text:00409700 loc_409700: ; CODE XREF: dga+115_x0019_j .text:00409700 mov dl, [ecx] .text:00409702 inc ecx .text:00409703 test dl, dl .text:00409705 jnz short loc_409700 .text:00409707 mov edi, [ebp+pDomain] .text:0040970A sub ecx, eax .text:0040970C mov edx, ecx .text:0040970E dec edi .text:0040970F nop .text:00409710 .text:00409710 loc_409710: ; CODE XREF: dga+126_x0019_j .text:00409710 mov cl, [edi+1] .text:00409713 inc edi .text:00409714 test cl, cl .text:00409716 jnz short loc_409710 .text:00409718 mov ecx, edx .text:0040971A shr ecx, 2 .text:0040971D mov esi, eax .text:0040971F mov eax, domain_nr .text:00409724 rep movsd .text:00409726 mov ecx, edx .text:00409728 and ecx, 3 .text:0040972B rep movsb .text:0040972D inc eax .text:0040972E xor edx, edx .text:00409730 mov ecx, 0C8h .text:00409735 div ecx .text:00409737 mov ebx, [ebp+pDomain] .text:0040973A mov domain_nr, edx .text:00409740 .text:00409740 loc_409740: ; CODE XREF: dga+35j .text:00409740 ; dga+41j .text:00409740 push ebx .text:00409741 call gethostbyname ; ws2_32.gethostbyname .text:00409747 neg eax .text:00409749 sbb eax, eax .text:0040974B neg eax .text:0040974D jnz short loc_40976B .text:0040974F cmp domain_nr, 0 .text:00409756 jnz loc_409610 .text:0040975C mov edx, [ebp+sld_len] .text:0040975F push edx ; size_t .text:00409760 push 0 ; int .text:00409762 push ebx ; void * .text:00409763 call memset .text:00409768 add esp, 0Ch .text:0040976B .text:0040976B loc_40976B: ; CODE XREF: dga+15Dj .text:0040976B xor eax, eax .text:0040976D cmp domain_nr, eax .text:00409773 pop edi .text:00409774 pop esi .text:00409775 setnz al .text:00409778 pop ebx .text:00409779 mov esp, ebp .text:0040977B pop ebp .text:0040977C retn .text:0040977C dga endp .text:0040977C .text:0040977C ; ---------------------------------------------------------------------------
在测试gethostbyname的时候,Qadars的域名生成算法已经产生了200多个不同的域名,如果这200个域名都没有办法得到解析,那么Qadars将会sleep 20s(这个不在之前反汇编代码中),接着从第一个域名开始解析。
这个DGA使用线性同余实现而不是通过随机数算法生成。而这个乘法的增加也是不常见的:
r←(35317−1043968403⋅r)mod2147483647
随机数生成的初始值是系统当前日期:
text:00409640 push 0 .text:00409642 call ds:_time64 .text:00409648 add esp, 4 .text:0040964B push 0 .text:0040964D mov esi, eax .text:0040964F sub eax, 345600 .text:00409654 push 604800 .text:00409659 sbb edx, 0 .text:0040965C push edx .text:0040965D push eax .text:0040965E call _allrem .text:00409663 sub esi, eax .text:00409665 and esi, 7FFFFFFFh
上述反汇编代码算法:
r=(u−(u−4⋅24⋅3600))mod7⋅24⋅3600
U是当前unix时间戳。分配如下:
计算机的结果在每周四晚上的深夜的值都是不一样的,因为1970年1.1日式星期四。所有的Aadars V3将会生成相同的域名,因为随机数生成的起始值都是一样的。
DGA算法使用硬编码的顶级域名:.com,.org以及.net。第二级别的域名包括12个随机字符串,字符串是从小写字母和数字当中选取。
总结,DGA算法的特性如下:
下面的代码以python的形式显示Qadar的DGA。你也可以在其他地方找到这个算法,例如在我的 GitHub 上。
import argparse import time from datetime import datetime import time import string def rand(r): return (35317 - 1043968403*r) & 0x7FFFFFFF def dga(date): charset = string.ascii_lowercase + string.digits tlds = [".com", ".org", ".net"] unix = int(time.mktime(date.timetuple())) b = 7*24*3600 c = 3*24*3600 r = ( (unix//b)*b - c) & 0x7FFFFFFF for i in range(200): domain = "" for _ in range(12): r = rand(r) domain += charset[r % len(charset)] r = rand(r) tld = tlds[r % 3] domain += tld print(domain) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("-d", "--date", help="date for which to generate domains") args = parser.parse_args() if args.date: d = datetime.strptime(args.date, "%Y-%m-%d") else: d = datetime.now() dga(d)
*参考来源: johannesbader.ch ,FB小编东二门陈冠希编译,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)