About pushdo:
Four times since 2008, authorities and technology companies have taken the prolific PushDo malware and Cutwail spam botnet offline. Yet much like the Energizer Bunny, it keeps coming back for more.
In early March, researchers at Damballa discovered a new version of the malware that had adopted a domain generation algorithm (DGA) in order to not only help it avoid detection by security researchers, but to add resiliency.
http://threatpost.com/pushdo-malware-resurfaces-with-dga-capabilities
In this blog post I would explain the DGA algorithm used behind Pushdo malware . Based on bitdefender figures Indian PCs have been most affected by the outbreak of pushdo, but systems in the UK, France and the US have also been hit.
http://labs.bitdefender.com/2014/07/...ent/#more-2002
[Analysis]
The unpacked artefacts clearly shows a glimpse of DGA :
Code:
04006598 50 36 34 00 57 69 6E 58 50 00 00 00 57 69 6E 32 P64.WinXP...Win2 040065A8 4B 00 00 00 7A 78 74 73 72 71 70 6E 6D 6C 6B 67 K...zxtsrqpnmlkg 040065B8 66 64 63 62 00 00 00 00 61 69 6F 75 00 00 00 00 fdcb....aiou.... 040065C8 61 65 69 6F 75 79 00 00 62 63 64 66 67 68 6A 6B aeiouy..bcdfghjk 040065D8 6C 6D 6E 70 71 72 73 74 76 77 78 7A 00 00 00 00 lmnpqrstvwxz.... 040065E8 2E 6B 7A 00 73 6D 74 70 2E 63 6F 6D 70 75 73 65 .kz.smtp.compuse 040065F8 72 76 65 2E 63 6F 6D 00 6D 61 69 6C 2E 61 69 72 rve.com.mail.air 04006608 6D 61 69 6C 2E 6E 65 74 00 00 00 00 73 6D 74 70 mail.net....smtp
and it the debugger will pop directly in DGA code, when they'll be processed.
The PushDo is known to generate ".kz" suffix domains, the presence of ".kz" at 040065E8
is of immense help in discovery of DGA, we can cross check the reference to ".kz" in decompressed
code right after packer finishes its own job. Just set breakpoint at "VirtualProtect" and let it go for 2 times, then
break the execution at any point.
Check the reference to ".kz" string :
Code:
0400523F PUSH 40065E8 ; ".kz"
Code:
0400519B 55 PUSH EBP 0400519C 8D6C24 94 LEA EBP,DWORD PTR SS:[ESP-6C] 040051A0 81EC 98000000 SUB ESP,98 040051A6 53 PUSH EBX 040051A7 56 PUSH ESI 040051A8 8B75 74 MOV ESI,DWORD PTR SS:[EBP+74] 040051AB 33DB XOR EBX,EBX 040051AD 895D 68 MOV DWORD PTR SS:[EBP+68],EBX 040051B0 3BF3 CMP ESI,EBX 040051B2 0F84 A2000000 JE 0400525A 040051B8 57 PUSH EDI 040051B9 8B7D 78 MOV EDI,DWORD PTR SS:[EBP+78] 040051BC 3BFB CMP EDI,EBX 040051BE 0F84 95000000 JE 04005259 040051C4 8D45 54 LEA EAX,DWORD PTR SS:[EBP+54] 040051C7 50 PUSH EAX 040051C8 FF15 00610004 CALL DWORD PTR DS:[4006100] ; kernel32.GetLocalTime 040051CE 0FB745 5A MOVZX EAX,WORD PTR SS:[EBP+5A] 040051D2 50 PUSH EAX 040051D3 0FB745 56 MOVZX EAX,WORD PTR SS:[EBP+56] 040051D7 50 PUSH EAX 040051D8 0FB745 54 MOVZX EAX,WORD PTR SS:[EBP+54] 040051DC 50 PUSH EAX 040051DD E8 B1000000 CALL 04005293 : GenerateSeed(day, month, year) 040051E2 0345 7C ADD EAX,DWORD PTR SS:[EBP+7C] 040051E5 83C4 0C ADD ESP,0C 040051E8 3BFB CMP EDI,EBX 040051EA 8945 74 MOV DWORD PTR SS:[EBP+74],EAX 040051ED 7E 6A JLE SHORT 04005259 040051EF 897D 64 MOV DWORD PTR SS:[EBP+64],EDI 040051F2 897D 68 MOV DWORD PTR SS:[EBP+68],EDI 040051F5 BF 80000000 MOV EDI,80 040051FA 57 PUSH EDI 040051FB 8D45 D4 LEA EAX,DWORD PTR SS:[EBP-2C] 040051FE 6A 00 PUSH 0 04005200 50 PUSH EAX 04005201 E8 53F0FFFF CALL 04004259 ; clears memory (memset) 04005206 57 PUSH EDI 04005207 8D45 D4 LEA EAX,DWORD PTR SS:[EBP-2C] 0400520A 50 PUSH EAX 0400520B 8D45 74 LEA EAX,DWORD PTR SS:[EBP+74] 0400520E 6A 04 PUSH 4 04005210 50 PUSH EAX 04005211 E8 C5F4FFFF CALL 040046DB : GenerateMDHash() 04005216 83C4 1C ADD ESP,1C 04005219 85C0 TEST EAX,EAX 0400521B 7E 05 JLE SHORT 04005222 0400521D 8B4D D4 MOV ECX,DWORD PTR SS:[EBP-2C] 04005220 EB 05 JMP SHORT 04005227 04005222 8B4D 74 MOV ECX,DWORD PTR SS:[EBP+74] 04005225 03CB ADD ECX,EBX 04005227 894D 74 MOV DWORD PTR SS:[EBP+74],ECX 0400522A 83E1 03 AND ECX,3 0400522D 83C1 09 ADD ECX,9 04005230 51 PUSH ECX 04005231 56 PUSH ESI 04005232 50 PUSH EAX 04005233 8D45 D4 LEA EAX,DWORD PTR SS:[EBP-2C] 04005236 50 PUSH EAX 04005237 E8 D8FEFFFF CALL 04005114 ; Generates domain 0400523C 83C4 10 ADD ESP,10 0400523F 68 E8650004 PUSH 40065E8 ; ASCII ".kz" 04005244 56 PUSH ESI 04005245 FF15 E0600004 CALL DWORD PTR DS:[40060E0] ; kernel32.lstrcatA 0400524B FF45 74 INC DWORD PTR SS:[EBP+74] 0400524E 83C3 07 ADD EBX,7 04005251 83C6 28 ADD ESI,28 04005254 FF4D 64 DEC DWORD PTR SS:[EBP+64] 04005257 ^75 A1 JNZ SHORT 040051FA 04005259 5F POP EDI 0400525A 8B45 68 MOV EAX,DWORD PTR SS:[EBP+68] 0400525D 5ESI 0400525E 5B POP EBX 0400525F 83C5 6C ADD EBP,6C 04005262 C9 LEAVE 04005263 C3 RETN
Code:
040051C8 FF15 00610004 CALL DWORD PTR DS:[4006100] ; kernel32.GetLocalTime
Code:
040051CE 0FB745 5A MOVZX EAX,WORD PTR SS:[EBP+5A] 040051D2 50 PUSH EAX 040051D3 0FB745 56 MOVZX EAX,WORD PTR SS:[EBP+56] 040051D7 50 PUSH EAX 040051D8 0FB745 54 MOVZX EAX,WORD PTR SS:[EBP+54] 040051DC 50 PUSH EAX 040051DD E8 B1000000 CALL 04005293
as in following line :
Code:
040051E2 0345 7C ADD EAX,DWORD PTR SS:[EBP+7C]
[code]
04005201 E8 53F0FFFF CALL 04004259
[code]
Is setting up memory for action, so its a memset call.
The interesting calls occurs at:
Code:
04005211 E8 C5F4FFFF CALL 040046DB
Code:
040051DD E8 B1000000 CALL 04005293 : GenerateSeed(day, month, year) 040051E2 0345 7C ADD EAX,DWORD PTR SS:[EBP+7C]
Code:
04005237 E8 D8FEFFFF CALL 04005114
Let us check the code
Code:
04005114 55 PUSH EBP 04005115 8BEC MOV EBP,ESP 04005117 83EC 0C SUB ESP,0C 0400511A 57 PUSH EDI 0400511B 33FF XOR EDI,EDI 0400511D 33C0 XOR EAX,EAX 0400511F 397D 08 CMP DWORD PTR SS:[EBP+8],EDI 04005122 74 74 JE SHORT 04005198 04005124 397D 0C CMP DWORD PTR SS:[EBP+C],EDI 04005127 74 6F JE SHORT 04005198 04005129 53 PUSH EBX 0400512A 8B5D 10 MOV EBX,DWORD PTR SS:[EBP+10] 0400512D 3BDF CMP EBX,EDI 0400512F 74 66 JE SHORT 04005197 04005131 397D 14 CMP DWORD PTR SS:[EBP+14],EDI 04005134 74 61 JE SHORT 04005197 04005136 56 PUSH ESI 04005137 8B35 90600004 MOV ESI,DWORD PTR DS:[4006090] ; kernel32.lstrlenA 0400513D 53 PUSH EBX 0400513E FFD6 CALL ESI 04005140 397D 0C CMP DWORD PTR SS:[EBP+C],EDI 04005143 7E 40 JLE SHORT 04005185 04005145 3B45 14 CMP EAX,DWORD PTR SS:[EBP+14] 04005148 7D 3B JGE SHORT 04005185 0400514A 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] 0400514D 8A0407 MOV AL,BYTE PTR DS:[EDI+EAX] 04005150 6A 08 PUSH 8 04005152 8845 FC MOV BYTE PTR SS:[EBP-4],AL 04005155 8D45 F4 LEA EAX,DWORD PTR SS:[EBP-C] 04005158 6A 00 PUSH 0 0400515A 50 PUSH EAX 0400515B E8 F9F0FFFF CALL 04004259 ; Memset 04005160 6A 07 PUSH 7 04005162 8D45 F4 LEA EAX,DWORD PTR SS:[EBP-C] 04005165 50 PUSH EAX 04005166 FF75 FC PUSH DWORD PTR SS:[EBP-4] 04005169 E8 26FFFFFF CALL 04005094 ; Generates domain stringlets (parts of domain name in sequential order) 0400516E 83C4 18 ADD ESP,18 04005171 8D45 F4 LEA EAX,DWORD PTR SS:[EBP-C] 04005174 50 PUSH EAX 04005175 53 PUSH EBX 04005176 FF15 E0600004 CALL DWORD PTR DS:[40060E0] ; kernel32.lstrcatA 0400517C 53 PUSH EBX 0400517D FFD6 CALL ESI 0400517F 47 INC EDI 04005180 3B7D 0C CMP EDI,DWORD PTR SS:[EBP+C] 04005183 ^7C C0 JL SHORT 04005145 04005185 53 PUSH EBX 04005186 FFD6 CALL ESI 04005188 8B4D 14 MOV ECX,DWORD PTR SS:[EBP+14] 0400518B 3BC1 CMP EAX,ECX 0400518D 7E 04 JLE SHORT 04005193 0400518F C6040B 00 MOV BYTE PTR DS:[EBX+ECX],0 04005193 53 PUSH EBX 04005194 FFD6 CALL ESI 04005196 5E POP ESI 04005197 5B POP EBX 04005198 5F POP EDI 04005199 C9 LEAVE
Code:
04005169 E8 26FFFFFF CALL 04005094
is being strcat to earlier produced string from same call. So this loop is the domain name generator,
in which the subroutine at 04005094 is responsible for generating the stringlets for domain name.
Lets check code at 04005094
Code:
04005094 8B4C24 08 MOV ECX,DWORD PTR SS:[ESP+8] 04005098 33C0 XOR EAX,EAX 0400509A 85C9 TEST ECX,ECX 0400509C 74 75 JE SHORT 04005113 0400509E 837C24 0C 03 CMP DWORD PTR SS:[ESP+C],3 040050A3 7E 6E JLE SHORT 04005113 040050A5 53 PUSH EBX 040050A6 8A5C24 08 MOV BL,BYTE PTR SS:[ESP+8] 040050AA 57 PUSH EDI 040050AB 6A 13 PUSH 13 040050AD 33D2 XOR EDX,EDX 040050AF 0FB6C3 MOVZX EAX,BL 040050B2 5F POP EDI 040050B3 F7F7 DIV EDI 040050B5 FEC3 INC BL 040050B7 6A 05 PUSH 5 040050B9 5F POP EDI 040050BA 6A 02 PUSH 2 040050BC 8A82 D0650004 MOV AL,BYTE PTR DS:[EDX+40065D0] 040050C2 8801 MOV BYTE PTR DS:[ECX],AL 040050C4 0FB6C3 MOVZX EAX,BL 040050C7 33D2 XOR EDX,EDX 040050C9 F7F7 DIV EDI 040050CB FEC3 INC BL 040050CD 8A82 C8650004 MOV AL,BYTE PTR DS:[EDX+40065C8] 040050D3 8841 01 MOV BYTE PTR DS:[ECX+1],AL 040050D6 8079 01 65 CMP BYTE PTR DS:[ECX+1],65 040050DA 58 POP EAX 040050DB 75 17 JNZ SHORT 040050F4 040050DD F6C3 07 TEST BL,7 040050E0 74 12 JE SHORT 040050F4 040050E2 0FB6C3 MOVZX EAX,BL 040050E5 6A 03 PUSH 3 040050E7 33D2 XOR EDX,EDX 040050E9 5F POP EDI 040050EA F7F7 DIV EDI 040050EC 8A82 C0650004 MOV AL,BYTE PTR DS:[EDX+40065C0] 040050F2 EB 17 JMP SHORT 0400510B 040050F4 F6C3 01 TEST BL,1 040050F7 74 18 JE SHORT 04005111 040050F9 FEC3 INC BL 040050FB 0FB6C3 MOVZX EAX,BL 040050FE 6A 0F PUSH 0F 04005100 33D2 XOR EDX,EDX 04005102 5F POP EDI 04005103 F7F7 DIV EDI 04005105 8A82 AC650004 MOV AL,BYTE PTR DS:[EDX+40065AC] 0400510B 6A 03 PUSH 3 0400510D 8841 02 MOV BYTE PTR DS:[ECX+2],AL 04005110 58 POP EAX 04005111 5F POP EDI 04005112 5B POP EBX 04005113 C3 RETN
whereas 04005114 subroutine generates domain name from these stringlets.
For next domain name,
the first DWORD of processed MD5 (first 8 bytes in hex) + 1
are taken as seed to generate next md5hash.
The decoded python version of this DGA is :
Code:
''' Developer : Garage4Hackers Greets : b0nd, FB1H2S, "vinnu", l0rdDeathStorm, nightrover and all g4h team ''' import os, time, datetime, hashlib print "PushDO-DGA" def rc4crypt(data, key): x = 0 box = range(256) for i in range(256): x = (x + box[i] + ord(key[i % len(key)])) % 256 box[i], box[x] = box[x], box[i] x = 0 y = 0 out = [] for char in data: x = (x + 1) % 256 y = (y + box[x]) % 256 box[x], box[y] = box[y], box[x] out.append(chr(ord(char) ^ box[(box[x] + box[y]) % 256])) return ''.join(out) def hasher(data, algorithm="md5"): h = hashlib.new(algorithm) h.update(data) return h.hexdigest() def getDate(): dt = str(datetime.datetime.now()).split(' ')[0] dstash = dt.split('-') dd = dstash[2] mm = dstash[1] yyyy = dstash[0] return int(dd),int(mm),int(yyyy) def generateSeed(a1, a2, a3) : result = '' v4 = '' v5 = 0 v6 = 0 v7 = 0 v8 = "1F1C1F1E1F1E1F1F1E1F1E1F" v8 = v8.decode("hex") result = 0 if ( a1 > 0 ) : if ( (a2 - 1) <= 0xB ) : if ((a3 - 1) <= 0x1E ) : v4 = (a1 & 0x80000003) == 0 if ( (a1 & 0x80000003) < 0 ) : v4 = (((a1 & 0x80000003) - 1) | 0xFFFFFFFC) == -1 print "v4 : %x"%v4 if ( v4 ) : v8[11] = chr(0x1D) v5 = 0 if ( a2 > 1 ) : v7 = v8 #&v8 v6 = a2 - 1 i7 = 0 while (v6) : v5 += ord(v7[i7]) #*v7 i7 += 1 v6 -= 1 print "\tv5 : %x v6 : %x v7 : %x"%(v5, v6,ord(v7[i7])) ecx = 365 * (a1 - (a1 / 4)) eax = 366 * (a1 / 4) print "ecx : %x eax : %x"%(ecx, eax) result = a3 + v5 + ecx + eax return result def generateString(salt, seed): buf = '' tmp = "%08x" % seed tmp = tmp.decode("hex") for i in range(4) : buf = tmp[i]+buf return buf def generateDomain(mdhash, length): buf = '' for c in mdhash: if len(buf) > length : return buf bl = ord(c) v1 = "aiou" v2 = "aeiouy" c1 = "bcdfghjklmnpqrstvwxz" c2 = "zxtsrqpnmlkgfdcb" edx = 0 eax = bl edi = 0x13 edx = eax%edi bl += 1 edi = 5 al = c1[edx] print "edx : %x al : %s bl : %x" % (edx, al, bl) buf += al eax = bl edx = 0 edx = eax%edi bl += 1 al = v2[edx] print "edx : %x al : %s bl : %x" % (edx, al, bl) buf += al eax = 2 if ord(al) == 0x65 : if bl & 0x07 : eax = bl edi = 3 edx = 0 edx = eax%edi al = v1[edx] print "\t[1]edx : %x al : %s bl : %x" % (edx, al, bl) buf += al ''' else : if bl != 1 : bl += 1 eax = bl edi = 0x0F edx = 0 edx = eax%edi al = c2[edx] print "\t[2]edx : %x al : %s bl : %x" % (edx, al, bl) buf += al ''' else : if (bl & 1) : bl += 1 eax = bl edi = 0x0F edx = 0 edx = eax%edi al = c2[edx] print "\t[0]edx : %x al : %s bl : %x" % (edx, al, bl) buf += al bl += 1 return buf def initDGA(salt): domains = [] print "[+] "+utility+" : Initiated" day,month,year = getDate() print "day : "+str(day) seed = generateSeed(year, month, day) seed = generateString(salt, seed)#.decode("hex") print "tmp : "+seed.encode("hex") for i in range(0x1E): print "Seed : %s" %seed.encode("hex") hashit = hasher(seed) print "hash : "+hashit domain = generateDomain(hashit.decode("hex"), 0x0A) print "Domain : "+domain seed = ("%08x" % (int(hashit[:8],16)+0x01000000)).decode("hex") domains.append(domain) #time.sleep(1) return domains domains = initDGA(0) index = 0 for domain in domains : print "["+str(index)+"] "+domain[:8]+".kz" index += 1
in the range of 30 previous days plus 15 next days totalling 46 days generating 1380 domains
to contact in a day. Above python code for DGA generates domains for current day only,
but it can be modified to generate all 1380 domains easily.
Sample SHA256 : 42b62189ab294872fd8c587d80823cbc8aab0e256dc3a7fa35 5a28482a58e8a2
------------------------XXX------------------------
Rate this article