• Reverse Engineering : Domain generation for PushDo Malware algorithm released.

    DGA : The domain generation for PushDo unleashed


    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------------------------
    This article was originally published in blog: Reverse Engineering : Domain generation for PushDo Malware algorithm released. started by garage4hackers
  • G4H Facebook

  • G4H Twitter