View RSS Feed

garage4hackers

Reverse Engineering : Domain generation for PushDo Malware algorithm released.

Rating: 13 votes, 4.31 average.
DGA : The domain generation for PushDo unleashed
Name:  Screen Shot 2014-08-26 at 1.22.56 am.jpg
Views: 3575
Size:  21.3 KB

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
The presence of groups of vowels and consonents, anyone can seta breakpoint on them
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"
There we land in DGA. Let us check the dissembled code from prologue :

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
The above shown code snippet is the DGA itself. The code starts with a call to GetLocalTime as:
Code:
040051C8   FF15 00610004    CALL DWORD PTR DS:[4006100]                             ; kernel32.GetLocalTime
The very next call after GetLocalTime call takes year, month and day returned by returned by GetLocalTime as :
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
The code at 04005293 returns a number and that number is then added to one of arguments of DGA itself.
as in following line :

Code:
040051E2   0345 7C          ADD EAX,DWORD PTR SS:[EBP+7C]
Initially this argument at [EBP+7C] is 0. call instruction at

[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
This call generates MD5 hash of number returned and processed by 04005293 as in following code:

Code:
040051DD   E8 B1000000      CALL 04005293 : GenerateSeed(day, month, year)
040051E2   0345 7C          ADD EAX,DWORD PTR SS:[EBP+7C]
MD5 is generated of seed + no. at [EBP+7C]. Now the only call left before strcat of ".kz" is

Code:
04005237   E8 D8FEFFFF      CALL 04005114
It looks like this is the domain name generator based on the MD5 hash generated by the seed.
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
The only important call in above code snippet is

Code:
04005169   E8 26FFFFFF      CALL 04005094
Before this call string length is being checked and the returned string from this call
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
This call processes the above generated MD5 hash and produces stringlets,
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
A total of 0x1E domains are generated for 1 day. The PushDo is known to generate domains
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------------------------

Updated 08-26-2014 at 01:41 AM by garage4hackers

Categories
Uncategorized

Comments

Trackbacks

Total Trackbacks 0
Trackback URL: