cover image for post 'The DGA of DirCrypt'

The DGA of DirCrypt

Table of Contents
Disclaimer

These are just unpolished notes. The content likely lacks clarity and structure; and the results might not be adequately verified and/or incomplete.

DGArchive

The DGA in this blog post has been implemented by the DGArchive project.

Malpedia

For more information about the malware in this blog post see the Malpedia entry on DirCrypt.

The DGA

DirCrypt is an inactive Ransomware that uses a Domain Generation Algorithm (DGA) for its callback call. Because I couldn`t find the DGA algorithm online, I decided to reverse engineer this sample from malwr.com. I list more samples that use the DGA in section Sample on malwr.com.

The DGA of DirCrypt uses a hardcoded seed located in the resource section of the executable. For the examined sample, the seed is labeled with the integer identifier 0x7D:

load seed from resource section

For my sample, the value of resource identifier 0x7D was 0xF2113C2A:

seed value

The malware passes the seed and the number of distinct domains it wants to generate to a subroutine I called spawn_6_callback_threads:

calls spawning

The subroutine creates six callback threads - all getting a pointer to the same structure with seed and number of domains. The routine will wait for all six threads to finish before it returns:

spawning 6 threads

The callback routine callback_loop creates new domains with the following routine “the_dga”. The counter dga_nr_of_domains (initialized to 30) is decreased after a new domain is generated. The thread returns when a command-and-control callback is successful or the counter reaches zero. Here is the disassembly of the DGA:

UPX0:0040183B the_dga         proc near               
UPX0:0040183B
UPX0:0040183B seed            = dword ptr  4
UPX0:0040183B domain          = dword ptr  8
UPX0:0040183B
UPX0:0040183B                 push    ebx
UPX0:0040183C                 push    esi
UPX0:0040183D                 push    edi
UPX0:0040183E                 push    20
UPX0:00401840                 push    8
UPX0:00401842                 lea     eax, [esp+14h+seed]
UPX0:00401846                 push    eax
UPX0:00401847                 call    rand_int
UPX0:0040184C                 mov     ebx, [esp+0Ch+domain]
UPX0:00401850                 mov     edi, eax
UPX0:00401852                 xor     esi, esi
UPX0:00401854                 test    edi, edi
UPX0:00401856                 jbe     short loc_40186E
UPX0:00401858
UPX0:00401858 loc_401858:                            
UPX0:00401858                 push    'z'
UPX0:0040185A                 push    'a'
UPX0:0040185C                 lea     eax, [esp+14h+seed]
UPX0:00401860                 push    eax
UPX0:00401861                 call    rand_int
UPX0:00401866                 mov     [esi+ebx], al
UPX0:00401869                 inc     esi
UPX0:0040186A                 cmp     esi, edi
UPX0:0040186C                 jb      short loc_401858
UPX0:0040186E
UPX0:0040186E loc_40186E:                           
UPX0:0040186E                 push    offset a_com    ; ".com"
UPX0:00401873                 add     edi, ebx
UPX0:00401875                 push    edi
UPX0:00401876                 call    strcpy
UPX0:0040187B                 mov     eax, [esp+0Ch+seed]
UPX0:0040187F                 pop     edi
UPX0:00401880                 pop     esi
UPX0:00401881                 pop     ebx
UPX0:00401882                 retn    8
UPX0:00401882 the_dga         endp
UPX0:00401882

with rand_int being:

UPX0:00404E9E rand_int        proc near               
UPX0:00404E9E                                        
UPX0:00404E9E
UPX0:00404E9E seed            = dword ptr  4
UPX0:00404E9E lower           = dword ptr  8
UPX0:00404E9E upper           = dword ptr  0Ch
UPX0:00404E9E
UPX0:00404E9E                 mov     eax, [esp+upper]
UPX0:00404EA2                 sub     eax, [esp+lower]
UPX0:00404EA6                 push    eax             ; span
UPX0:00404EA7                 push    [esp+4+seed]
UPX0:00404EAB                 call    rand_mod
UPX0:00404EB0                 add     eax, [esp+lower]
UPX0:00404EB4                 retn    0Ch
UPX0:00404EB4 rand_int        endp

and rand_mod being a standard linear congruential generator:

UPX0:00404E6B rand_mod        proc near               
UPX0:00404E6B                                        
UPX0:00404E6B
UPX0:00404E6B seed            = dword ptr  4
UPX0:00404E6B span            = dword ptr  8
UPX0:00404E6B
UPX0:00404E6B                 mov     ecx, [esp+seed]
UPX0:00404E6F                 mov     eax, [ecx]
UPX0:00404E71                 xor     edx, edx
UPX0:00404E73                 push    esi
UPX0:00404E74                 mov     esi, 127773
UPX0:00404E79                 div     esi
UPX0:00404E7B                 pop     esi
UPX0:00404E7C                 imul    eax, 2836
UPX0:00404E82                 imul    edx, 16807
UPX0:00404E88                 sub     edx, eax
UPX0:00404E8A                 mov     eax, [esp+span]
UPX0:00404E8E                 mov     [ecx], edx
UPX0:00404E90                 lea     ecx, [eax+1]
UPX0:00404E93                 mov     eax, edx
UPX0:00404E95                 xor     edx, edx
UPX0:00404E97                 div     ecx
UPX0:00404E99                 mov     eax, edx
UPX0:00404E9B                 retn    8
UPX0:00404E9B rand_mod        endp

As mentioned above, all six threads access — inside a critical section — the same seed and dga_nr_of_domains. Therefore, at most 30 different domains are created. The following Python code generates the 30 domains of the DGA for a given seed:

import argparse

class RandInt:

    def __init__(self, seed): 
        self.seed = seed

    def rand_int_modulus(self, modulus):
        ix = self.seed                
        ix = 16807*(ix % 127773) - 2836*(ix / 127773) & 0xFFFFFFFF        
        self.seed = ix
        return ix % modulus 

def get_domains(seed, nr):
    r = RandInt(seed)
    for i in range(nr):
        domain_len = r.rand_int_modulus(12+1) + 8
        domain = ""
        for i in range(domain_len):
            char = chr(ord('a') + r.rand_int_modulus(25+1))
            domain += char
        domain += ".com"
        yield domain

if __name__=="__main__":
    parser = argparse.ArgumentParser(description="generate Dircrypt domains")
    parser.add_argument("seed", help="seed as hex")
    args = parser.parse_args()
    for domain in get_domains(int(args.seed, 16), 30):
        print(domain)

For example:

$ python dga.py f2113c2a
rauggyguyp.com
llullzza.com
mluztamhnngwgh.com
mycojenxktsmozzthdv.com
inbxvqkegoyapgv.com
furiararji.com
zrkdvzjhse.com
wyuhdsdttczd.com
hpaxgpkteomjaxywwelr.com
mydojltbqjnwailyyoa.com
wbgzpjfxlxlcvbth.com
pibqzedhzwt.com
vlbqryjd.com
nsxdczggybtkdukmyf.com
jarjvddjzqrmnepeqwd.com
plxeyaja.com
lfehajeex.com
swtjyuhuefvl.com
ftdkuoulfhfudds.com
eblgaosyeszzjkbhhdyh.com
afececrkycbeyqm.com
xnloppwhfamkcltuxkif.com
xjjcditjfkgkihfe.com
mblmvrla.com
vxlkofoazme.com
ktqyrmiyvnidd.com
jsntwyjcv.com
wvquldqwwsttp.com
pivzovznpssx.com
ggspyfmreouxnhqi.com

The following table summarizes the properties of the DGA:

propertyvalue
seedhardcoded in resource section of executable
domains per seed30
tested domainsall
sequenceone after another, but DNS queries can occur out of order because six concurrent threads make callback calls
wait time between domainsnone
top level domain.com for all observed seeds
second level characterslower case letters, picked uniformly at random
second level domain length8 to 20 characters

Samples on malwr.com

I sifted through all samples on malwr.com where at least one of the virus scanners identified the sample as “DirCrypt”. I then brute forced the seed that leads to the observed domains. Because the callbacks run in six concurrent threads, the domains sometimes appear out of order. Also, some of the DirCrypt samples use an additional hardcoded domain: pdstriker.com, oktendentaries.com or jwuiygpnslht.com (this domain is generated by the DGA, just not with the hardcoded seed).

The following table lists the md5 hash of the sample (linked to the analysis on malwr.com), the submission date to malwr.com, the used seed, and any additional domains that are not covered by the DGA’s seed. The periodicity of the pseudo random number generator is 232/2 or half the number range; therefore, there are two seeds for each sequence of domains.

seedmd5datenot covered
18a62b7a, 98a62b794bb6c6c3f1ad7c2fb6096f6156c1df9b10. Jul. 2013pdstriker.com
18a62b7a, 98a62b793c03f0478ed6b0e81397b8e93cd4be9029. Jul. 2013
1fcbef63, 9fcbef62339901b416c580d4d6c7fae4a088d2e428. Aug. 2013oktedentaries.com
18a62b7a, 98a62b79d224637a6b6e3001753d9922e749d00d06. Sep. 2013
1a11b7cd, 9a11b7ccc1c117a8fbcd87b1c52a7c1c8e4bd2c930. Sep. 2013
72113c2b, f2113c2add69a49ab475dafc7246dee9f0f4c87706. Oct. 2013
72113c2b, f2113c2a42b77df04c7c34294c0e9459550cde9b06. Oct. 2013
72113c2b, f2113c2afa126a680351484beb450053e7ccccd006. Oct. 2013
72113c2b, f2113c2ae53d4e64930a40a12cd994f2779a11e907. Oct. 2013
1a11b7cd, 9a11b7cc7d978608d8fbaf3b756d692fff24345015. Oct. 2013
741fd6e2, f41fd6e170b86fdf69b8059ed4bf12e2a7707ae623. Oct. 2013
72113c2b, f2113c2a70d0a1b577dde513a0dfae09722d3ddd25. Oct. 2013
6c75a989, ec75a9880a807e0a2d29f19c95b313d018e1c2bd16. Nov. 2013
72113c2b, f2113c2aa88cfaa2e408df1245d74d0b5053197602. Dec. 2013
72113c2b, f2113c2a1186590b731d17206c63aadbe5a0484a02. Dec. 2013
78731d07, f8731d060e5e8f6edd2c1496614bb6a71ba3f25610. Dec. 2013jwuiygpnslht.com 6522e630, e522e62f
6c75a989, ec75a988b2752b6151b6fd8342e68b9bd5aa632b11. Dec. 2013
6e46566, 86e46565f99f10c3a02eff983e99216cd5f54ce931. Dec. 2013
6c75a989, ec75a988f7b0ae2f4d669e3705b60fe20a5bbf7a08. Jan. 2014
1fcbef63, 9fcbef62ee3c8b0bbea638e10eda11fa042069e011. Jan. 2014oktedentaries.com
52ce8a67, d2ce8a6680b356b9203d7e494ccc795d1599913319. Apr. 2014
22a47ee8, a2a47ee783f94b0697e3d69c3b219191984620d622. Apr. 2014
52ce8a67, d2ce8a66bbc1d7261ee18363aa2677708abeb5a025. Apr. 2014
52ce8a67, d2ce8a6608956c46e09c2375a6ee64313adc9d4a26. Apr. 2014
52ce8a67, d2ce8a66ec92487de0c66ceac950daff102c557603. May. 2014
52ce8a67, d2ce8a66b9e7b880bd095d11c16d6adc40eaff3d05. May. 2014
4caa1fc5, ccaa1fc41451cf7b82c70be7ea6744b69acc996029. May. 2014
4caa1fc5, ccaa1fc4bc918d15033b2f97bc0ba745949577d229. May. 2014
52ce8a67, d2ce8a660d24562e7e2ae008b757c471976bd2f629. May. 2014
52ce8a67, d2ce8a66245d39fad0e9c31dfac810ae413e4a9630. May. 2014
52ce8a67, d2ce8a6644bc29f11d907a33eca52cb1c872f9d630. May. 2014
52ce8a67, d2ce8a665af46d0edfffb0089dd1c1c9945e117030. May. 2014
52ce8a67, d2ce8a66ba682f257c4acf0d706e4ed29cabf47620. Jul. 2014

Most samples use the seed 52ce8a67 / d2ce8a66 (10 samples) and 72113c2b / f2113c2a (7 samples). The following table summarizes the seeds that I was able to identify, the first five generated domains, and the number of samples on malwr.com:

seedfirst 5 domainsfound hashes
6e46566, 86e46565wejcqzbosbczzlnikyvt.com, muiccxbvkvjb.com, tqwpmpwckhidiss.com, gzredieexn.com, ghhcwldtj.com1
72113c2b, f2113c2arauggyguyp.com, llullzza.com, mluztamhnngwgh.com, mycojenxktsmozzthdv.com, inbxvqkegoyapgv.com7
741fd6e2, f41fd6e1cbhytcvyxzzj.com, ervqveknzq.com, jxuynwdac.com, bucelslmpwyajzlguis.com, zhszoxeavbhmtkbju.com1
1a11b7cd, 9a11b7cclldpoyrzfi.com, chbqrhunxg.com, iqhbyacfnea.com, lgsfbhyyrrnalpcbqkob.com, fktihyjhkomdxqkucg.com2
18a62b7a, 98a62b79viweabkkfe.com, lscyqrjofqmtn.com, ltcfpuctidqqqxxzpikz.com, wowsfhnnvlwhlotryvh.com, linbzxpkmdtngnbdg.com3
4caa1fc5, ccaa1fc4qjdygsnoiqaudcq.com, iwqvktutvmptevjbnzy.com, vcgietkhdgvjhhsbdu.com, mkhjbvxvuqznmcjmy.com, jgtkrjdnqeyrjpbnqxym.com2
1fcbef63, 9fcbef62fzfqphobttefkhbvkzs.com, pmyddiicql.com, pihxsxitdfzpvpgeusf.com, glurejnjtdbj.com, oomxzlhazpiz.com2
78731d07, f8731d06ttaebamktjdbizrnqxp.com, znpszzwstgzzyk.com, jsngvficglxttjwg.com, frwwkrpnkvig.com, egdbvrhtcptgoqorompj.com1
52ce8a67, d2ce8a66aexluxmagbyg.com, izllzixotympqqr.com, pwxqjnhsocyln.com, pmzlyoesekeqytc.com, ypveltysbgcpm.com10
22a47ee8, a2a47ee7mhrmhuxlcvkxay.com, lvphxfvpsigghujpdm.com, ctskthnhq.com, safkylboxhb.com, gcifbxymnmmdfay.com1
6c75a989, ec75a988hiuctidthkvwowhvo.com, fcnjgeiicc.com, jpryjfvwlf.com, mlavvgdzq.com, rvcysvtrdqvfeoxpkgay.com3