Writeup - Challenge DGSE Brigitte Friang - Forensic | Introduction au Forensic avec un cas réaliste

Du 24 octobre au 11 novembre 2020, la DGSE et ESIEE Paris organisaient le challenge/CTF Brigitte Friang.
Cette année, “Brigitte Friang”, résistante et membre du BCRA, les services de renseignement de la France Libre de la Seconde Guerre mondiale est mise à l’honneur.
En résumé, le challenge était composé de trois parties :
Une première consistant à décoder un message depuis la première page du challenge: challengecybersec.fr. Une fois cette étape passée, nous arrivions sur un “chat” composé de plusieurs agents nous donnant des missions de plusieurs disciplines (algo, web, cryptographie, forensic). Nous devions résoudre une de ces catégories pour enfin accéder à la troisième partie. Qui était finalement une plateforme de CTF classique en équipe.
Nous allons voir dans cet article la manière dont j’ai résolu la track forensic de la deuxième partie du challenge. Qui est, je trouve, assez intéressante pour être détaillée et partagée.
Elle constitue une très bonne introduction au domaine du Forensic Avec un cas plutôt réaliste, un “vrai” malware et un peu de reverse engineering!
C’est parti!⌗
Nous commençons donc directement à la deuxième partie du challenge, le chat avec les différentes catégories.

Acceptons la mission du cher monsieur Bertillon !

Logs Nginx⌗
Faisons une simple recherche avec grep
sur le nom “Evil” de “EvilCorp”
$ grep "Evil" access.log
179.97.58.61 - - [Nov 05 2020 16:22:20] "POST /login HTTP/1.1" 200 476 "-" "Evil Browser"

Bon…
Steganographie⌗
Passons aux choses sérieuses. Nous obtenons une image.

Très jolie… Mais un peu lourde pour sa taille en regardant de plus près avec file
et ls
$ file evil_country_landscape.jpg && ls -lh evil_country_landscape.jpg
evil_country_landscape.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, progressive, precision 8, 640x436, components 3
-rw-r--r-- 1 nirae nirae 303M 26 oct. 15:37 evil_country_landscape.jpg
Une image de 640x436 pesant 303 Mo. Elle contient surement des fichiers cachés à l’intérieur.
Nous pouvons le vérifier avec l’outil binwalk
$ binwalk evil_country_landscape.jpg
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 JPEG image data, JFIF standard 1.01
79798 0x137B6 Zip archive data, at least v2.0 to extract, uncompressed size: 173015040, name: part2.img
4775856 0x48DFB0 Zlib compressed data, best compression
^[[C34737670 0x2120E06 MySQL MISAM compressed data file Version 1
56164092 0x358FEFC IMG0 (VxWorks) header, size: 257308484
128298187 0x7A5ACCB Cisco IOS microcode, for "w%"
158637081 0x9749C19 Zip archive data, at least v2.0 to extract, uncompressed size: 173015040, name: part3.img
239002530 0xE3EE3A2 Zlib compressed data, best compression
317286906 0x12E969FA End of Zip archive, footer length: 22
Effectivement elle contient bien d’autres fichiers, dont des archives ZIP. Binwalk nous permets de les extraire.
$ binwalk -eM evil_country_landscape.jpg
Scan Time: 2020-10-26 11:07:46
Target File: /home/nirae/Lab/brigitte-friang/evil_country_landscape.jpg
MD5 Checksum: 8c9bb1dddadd9f04df78cbe8d44be4fe
Signatures: 391
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 JPEG image data, JFIF standard 1.01
79798 0x137B6 Zip archive data, at least v2.0 to extract, uncompressed size: 173015040, name: part2.img
4775856 0x48DFB0 Zlib compressed data, best compression
34737670 0x2120E06 MySQL MISAM compressed data file Version 1
56164092 0x358FEFC IMG0 (VxWorks) header, size: 257308484
128298187 0x7A5ACCB Cisco IOS microcode, for "w%"
158637081 0x9749C19 Zip archive data, at least v2.0 to extract, uncompressed size: 173015040, name: part3.img
239002530 0xE3EE3A2 Zlib compressed data, best compression
317286906 0x12E969FA End of Zip archive, footer length: 22
Scan Time: 2020-10-26 11:08:51
Target File: /home/nirae/Lab/brigitte-friang/_evil_country_landscape.jpg.extracted/part2.img
MD5 Checksum: 0eb69c5169184ab37d478dce45e4ed55
Signatures: 391
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
3694592 0x386000 Linux EXT filesystem, blocks count: 83456, image size: 85458944, rev 1.0, ext3 filesystem data, UUID=bcb580b1-a10c-4c29-9cab-833f13f213f2
142617411 0x8802B43 Cisco IOS microcode, for "w%"
159703094 0x984E036 MySQL MISAM compressed data file Version 8
Scan Time: 2020-10-26 11:09:10
Target File: /home/nirae/Lab/brigitte-friang/_evil_country_landscape.jpg.extracted/part3.img
MD5 Checksum: 00f1ff95393315894f605c1ff94b8ee6
Signatures: 391
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------
RAID⌗
Nous avons donc obtenus deux fichiers intéressants : “part2.img” et “part3.img”. Observons les.
$ file part2.img && file part3.img
part2.img: Linux Software RAID version 1.2 (1) UUID=dfaa645a:19afec72:60f1fa33:30d841da name=user-XPS-15-9570:6 level=5 disks=3
part3.img: Linux Software RAID version 1.2 (1) UUID=dfaa645a:19afec72:60f1fa33:30d841da name=user-XPS-15-9570:6 level=5 disks=3
Ce sont en fait deux images de disques RAID Linux. On apprend aussi qu’il s’agit de RAID 5, avec 3 disques. Et nous en avons visiblement 2.
Voyons ce que Wikipédia a à nous dire:
Le RAID est un ensemble de techniques de virtualisation du stockage permettant de répartir des données sur plusieurs disques durs afin d’améliorer soit les performances, soit la sécurité ou la tolérance aux pannes de l’ensemble du ou des systèmes.
Le principe du RAID5 est de fonctionner avec au moins 3 disques et justement pouvoir récupérer les données si un des disques tombe en panne ou manque. C’est notre cas ici.
Nous allons donc reconstruire le RAID pour accéder aux données qui y sont stockées. Je vais faire ça dans une machine virtuelle Debian, avec Vagrant.
On va d’abord monter les fichiers images sur des fichiers loop avec losetup
pour les passer à mdadm
$ sudo losetup /dev/loop0 part2.img
$ sudo losetup /dev/loop1 part3.img
Maintenant, grâce à mdadm
, nous allons pouvoir recréer le RAID logiciel tel qu’il était avant la perte d’un des disques.
$ sudo mdadm --assemble /dev/md0 /dev/loop0 /dev/loop1
Les données sont maintenant ré-assemblées dans le volume RAID /dev/md0
, il ne reste plus qu’a le monter.
$ sudo mkdir /mnt/recover
$ sudo mount /dev/md0 /mnt/recover
$ ls /mnt/recover
dump.zip lost+found
Et voilà ! Nous avons pu récupérer le fichier dump.zip
.
Forensic⌗
Après avoir décompressé le fichier trouvé, nous obtenons un fichier dump.vmem
. Regardons s’il s’agit d’un dump mémoire avec Volatility.
J’utilise personnellement une version Docker de Volatility mais c’est évidemment la même chose avec la version classique.
Pour savoir si c’est un dump mémoire, nous pouvons lancer un imageinfo
afin d’en connaitre le type si c’est le cas
$ docker run --rm -v $(pwd):/data -ti cincan/volatility imageinfo -f /data/dump.vmem !
Volatility Foundation Volatility Framework 2.6.1
INFO : volatility.debug : Determining profile based on KDBG search...
Suggested Profile(s) : Win7SP1x64, Win7SP0x64, Win2008R2SP0x64, Win2008R2SP1x64_24000, Win2008R2SP1x64_23418, Win2008R2SP1x64, Win7SP1x64_24000, Win7SP1x64_23418
AS Layer1 : WindowsAMD64PagedMemory (Kernel AS)
AS Layer2 : FileAddressSpace (/data/dump.vmem)
PAE type : No PAE
DTB : 0x187000L
KDBG : 0xf80002c4c0a0L
Number of Processors : 1
Image Type (Service Pack) : 1
KPCR for CPU 0 : 0xfffff80002c4dd00L
KUSER_SHARED_DATA : 0xfffff78000000000L
Image date and time : 2020-10-05 11:17:37 UTC+0000
Image local date and time : 2020-10-05 13:17:37 +0200
Volatility nous indique que c’est effectivement un dump, probablement un Windows 7 (Win7SP1x64)
On regarde les fichiers présents et au bout de quelques recherches simples avec grep
, on tombe sur des fichiers avec l’extension .evil
$ volatility -f data/dump.vmem --profile=Win7SP1x64 filescan | grep evil
Volatility Foundation Volatility Framework 2.6
0x000000001715ed50 16 0 R--r-- \Device\HarddiskVolume1\Users\user\Documents\informations_attaque.txt.evil
0x000000003fa3ebc0 2 0 RW-r-- \Device\HarddiskVolume1\ProgramData\Microsoft\RAC\PublishedData\RacWmiDatabase.sdf.evil
0x000000003fac8d10 32 0 RW-r-- \Device\HarddiskVolume1\ProgramData\Microsoft\Windows\WER\ReportQueue\NonCritical_Firefox_installe_d514681bfc376345742b2157ace1e72c17fd991_cab_0938b7ba\appcompat.txt.evil
0x000000003fad8620 16 0 RW-r-- \Device\HarddiskVolume1\Users\user\AppData\Local\Microsoft\Windows\Caches\{AFBF9F1A-8EE8-4C77-AF34-C647E37CA0D9}.1.ver0x0000000000000002.db.evil
On peut donc supposer que la machine a été infectée par un virus, qui a chiffré certains fichiers. Partons à sa recherche en listant l’arbre des processus en cours
$ volatility -f /data/dump.vmem --profile=Win7SP1x64 pstree
Volatility Foundation Volatility Framework 2.6.1
Name Pid PPid Thds Hnds Time
-------------------------------------------------- ------ ------ ------ ------ ----
0xfffffa8002816060:wininit.exe 404 336 3 74 2020-10-05 11:13:42 UTC+0000
. 0xfffffa8002a007c0:lsass.exe 512 404 7 565 2020-10-05 11:13:42 UTC+0000
. 0xfffffa8002a0db30:lsm.exe 520 404 10 145 2020-10-05 11:13:42 UTC+0000
. 0xfffffa80029f76d0:services.exe 504 404 8 220 2020-10-05 11:13:42 UTC+0000
.. 0xfffffa8002b6e430:VGAuthService. 1560 504 4 84 2020-10-05 11:13:46 UTC+0000
.. 0xfffffa8002d2ab30:svchost.exe 1304 504 20 325 2020-10-05 11:13:45 UTC+0000
.. 0xfffffa8002b8ab30:svchost.exe 920 504 48 979 2020-10-05 11:13:43 UTC+0000
.. 0xfffffa8002a0fb30:svchost.exe 644 504 10 353 2020-10-05 11:13:43 UTC+0000
... 0xfffffa8001ca9060:WmiPrvSE.exe 3032 644 15 330 2020-10-05 11:14:07 UTC+0000
... 0xfffffa8002f1f060:WmiPrvSE.exe 1892 644 10 193 2020-10-05 11:13:47 UTC+0000
.. 0xfffffa8000e78920:taskhost.exe 2464 504 6 88 2020-10-05 11:17:08 UTC+0000
.. 0xfffffa8002bf8060:svchost.exe 304 504 20 391 2020-10-05 11:13:44 UTC+0000
.. 0xfffffa8002cc6310:spoolsv.exe 1156 504 15 270 2020-10-05 11:13:44 UTC+0000
.. 0xfffffa80031d5790:sppsvc.exe 2844 504 6 156 2020-10-05 11:16:54 UTC+0000
.. 0xfffffa8001e6ab30:dllhost.exe 1832 504 20 186 2020-10-05 11:13:47 UTC+0000
.. 0xfffffa8003021690:VSSVC.exe 2076 504 6 109 2020-10-05 11:13:49 UTC+0000
.. 0xfffffa8002d11a60:taskhost.exe 1216 504 10 204 2020-10-05 11:13:44 UTC+0000
.. 0xfffffa8002affb30:svchost.exe 708 504 6 276 2020-10-05 11:13:43 UTC+0000
.. 0xfffffa80030d4770:svchost.exe 2372 504 23 258 2020-10-05 11:13:51 UTC+0000
.. 0xfffffa8002b6f2c0:vmtoolsd.exe 1584 504 12 292 2020-10-05 11:13:46 UTC+0000
... 0xfffffa800117db30:cmd.exe 1744 1584 0 ------ 2020-10-05 11:17:37 UTC+0000
.... 0xfffffa8001116060:ipconfig.exe 2832 1744 0 ------ 2020-10-05 11:17:37 UTC+0000
.. 0xfffffa801bbf6b30:svchost.exe 2276 504 12 327 2020-10-05 11:16:54 UTC+0000
.. 0xfffffa8002fd4b30:wmpnetwk.exe 2296 504 11 213 2020-10-05 11:13:50 UTC+0000
.. 0xfffffa8002ef2b30:dllhost.exe 2008 504 17 195 2020-10-05 11:13:48 UTC+0000
.. 0xfffffa8002fc9560:msdtc.exe 872 504 15 155 2020-10-05 11:13:49 UTC+0000
.. 0xfffffa800107c6a0:WmiApSrv.exe 2632 504 7 119 2020-10-05 11:17:18 UTC+0000
.. 0xfffffa8002c4bb30:SearchIndexer. 2156 504 12 557 2020-10-05 11:13:50 UTC+0000
... 0xfffffa80030e4750:SearchProtocol 2400 2156 7 273 2020-10-05 11:13:51 UTC+0000
... 0xfffffa80030e7b30:SearchFilterHo 2420 2156 4 86 2020-10-05 11:13:51 UTC+0000
.. 0xfffffa8002b28b30:svchost.exe 880 504 16 320 2020-10-05 11:13:43 UTC+0000
... 0xfffffa8002c6b500:dwm.exe 1072 880 6 124 2020-10-05 11:13:44 UTC+0000
.. 0xfffffa8002bd4060:svchost.exe 244 504 26 720 2020-10-05 11:13:44 UTC+0000
.. 0xfffffa8002b1f4a0:svchost.exe 760 504 23 514 2020-10-05 11:13:43 UTC+0000
... 0xfffffa8002bb4b30:audiodg.exe 980 760 7 128 2020-10-05 11:13:44 UTC+0000
0xfffffa8002810060:csrss.exe 352 336 8 517 2020-10-05 11:13:42 UTC+0000
. 0xfffffa8002161630:conhost.exe 2928 352 0 ------ 2020-10-05 11:17:37 UTC+0000
0xfffffa8000e91b30:drpbx.exe 2304 2916 8 149 2020-10-05 11:17:01 UTC+0000
0xfffffa8000cc5b30:System 4 0 87 393 2020-10-05 11:13:41 UTC+0000
. 0xfffffa800bad5480:smss.exe 264 4 2 29 2020-10-05 11:13:41 UTC+0000
0xfffffa8002c6fb30:explorer.exe 1084 1064 32 828 2020-10-05 11:13:44 UTC+0000
. 0xfffffa8001072060:notepad.exe 1880 1084 1 62 2020-10-05 11:17:36 UTC+0000
. 0xfffffa8002d27b30:vmtoolsd.exe 1248 1084 8 166 2020-10-05 11:13:44 UTC+0000
. 0xfffffa8002d234f0:vm3dservice.ex 1240 1084 3 39 2020-10-05 11:13:44 UTC+0000
0xfffffa80111d32e0:csrss.exe 412 396 9 209 2020-10-05 11:13:42 UTC+0000
0xfffffa80029c2910:winlogon.exe 460 396 4 110 2020-10-05 11:13:42 UTC+0000
Après un peu de temps à analyser les processus, il y en a un qui parait intéressant, drpbx.exe
. Un processus qui tenterait de se faire passer pour Dropbox ?
Notre moteur de recherche préféré (ou pas forcément) nous indiquera qu’il s’agit bien d’un virus connu, Jigsaw.
Jigsaw est un ransomware (rançongiciel) très connu car très actif à partir de 2016. Il est écrit en .NET
et il existe plusieurs versions différentes dans la nature. Mais qui fonctionne de la même manière, il va se faire passer pour Firefox et/ou Dropbox, chiffrer les fichiers utilisateurs (fichiers textes, images, zip, etc) en les renommant avec une extension différente suivant les versions. L’utilisateur se retrouve donc sans fichiers et avec une interface demandant de payer une certaine somme en Bitcoin pour les déchiffrer.
Au vu de l’extension .evil
, on sait qu’il s’agit d’une version modifiée du malware car elle ne fait pas partie des extensions des versions connues. On va donc devoir l’analyser nous-même.
Pour l’extraire:
$ volatility -f /samples/dump.vmem --profile=Win7SP1x64 procdump --dump-dir /samples/out -p 2304
Volatility Foundation Volatility Framework 2.6.1
Process(V) ImageBase Name Result
------------------ ------------------ -------------------- ------
0xfffffa8000e91b30 0x0000000000870000 drpbx.exe OK: executable.2304.exe
Update 02-12-2020:
Le virus n’a pas mis longtemps avant de tomber entre les mains de quelques chercheurs qui l’ont lancé dans des sandboxes. Laissant apparaitre son interface.
#Jigsaw #Evil #Ransomware extension .evil!
— GrujaRS (@GrujaRS) October 24, 2020
Sample https://t.co/cI2RfgFejU pic.twitter.com/dSbdwBXF1Y

On reconnait bien la french touch grâce au nom “Saw See Son”!
Reverse Engineering⌗
Nous allons maintenant dé-compiler le malware pour voir son code. Nous savons qu’il est écrit en .NET
, donc nous allons utiliser ILSpy. Pour ma part, je vais (encore) utiliser cette version Docker de ILSpy.
$ docker run -v $(pwd):/samples cincan/ilspy /samples/executable.2304.exe
Je vous passe le code entier, pour des raisons évidentes, mais voyons la partie qui nous intéresse, les fonctions et variables de chiffrement/déchiffrement.
....
internal const string EncryptionPassword = "RXZpbERlZmF1bHRQYXNzIQ==";
....
private static void DecryptFile(string path, string encryptionExtension)
{
try
{
if (!path.EndsWith(encryptionExtension))
{
return;
}
string outputFile = path.Remove(path.Length - 4);
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider())
{
aesCryptoServiceProvider.Key = Convert.FromBase64String("RXZpbERlZmF1bHRQYXNzIQ==");
aesCryptoServiceProvider.IV = new byte[16]
{
0,
1,
0,
3,
5,
3,
0,
1,
0,
0,
2,
0,
6,
7,
6,
0
};
DecryptFile(aesCryptoServiceProvider, path, outputFile);
}
}
catch
{
return;
}
try
{
File.Delete(path);
}
catch (Exception)
{
}
}
....
private static void EncryptFile(SymmetricAlgorithm alg, string inputFile, string outputFile)
{
byte[] array = new byte[65536];
using (FileStream fileStream = new FileStream(inputFile, FileMode.Open))
{
using (FileStream stream = new FileStream(outputFile, FileMode.Create))
{
using (CryptoStream cryptoStream = new CryptoStream(stream, alg.CreateEncryptor(), CryptoStreamMode.Write))
{
int num;
do
{
num = fileStream.Read(array, 0, array.Length);
if (num != 0)
{
cryptoStream.Write(array, 0, num);
}
}
while (num != 0);
}
}
}
}
....
On peut donc voir que c’est un chiffrement AES. Nous avons la clé dans le code ainsi que le vecteur d’initialisation. Le programme utilise la classe AesCryptoServiceProvider, avec le mode CBC par défaut
Nous avons donc toutes les informations pour recoder la fonction de déchiffrement en python, sous la forme d’un script.
#! /usr/bin/env python3
# usage: decrypt.py <filename>
from Crypto.Cipher import AES
import sys
import base64
key = b'RXZpbERlZmF1bHRQYXNzIQ=='
iv = bytearray([0, 1, 0, 3, 5, 3, 0, 1, 0, 0, 2, 0, 6, 7, 6, 0])
def main(filename):
with open(filename, 'rb') as file:
content = file.read()
file.close()
aes = AES.new(base64.b64decode(key), AES.MODE_CBC, iv)
aes.block_size = 128
text = aes.decrypt(content)
result_filename = filename[:-5]
with open(result_filename, 'wb') as file:
file.write(text)
print("Good! %s was decrypted to %s" % (filename, result_filename))
if __name__ == "__main__":
if len(sys.argv) != 2:
print("usage: decrypt.py <filename>")
else:
main(sys.argv[1])
Notre script va ouvrir le fichier passé en paramètre, le déchiffrer et restaurer le fichier d’origine sans l’extension .evil
.
Précedemment, nous avions trouvé un fichier qui avait l’air très intéressant, informations_attaque.txt.evil
. Extractons le et utilisons notre script dessus!
$ docker run --rm -v $(pwd):/samples -ti cincan/volatility -f /samples/dump.vmem --profile=Win7SP1x64 dumpfiles -D /samples/out -Q '0x000000001715ed50'
$ mv file.None.0xfffffa800e9fec60.dat informations_attaque.txt.evil
$ ./decrypt.py informations_attaque.txt.evil
Good! informations_attaque.txt.evil was decrypted to informations_attaque.txt
Ce message est destiné à toute force alliée en mesure de nous venir en aide, nous la résistance d’Evil Country. Hier, nous sommes parvenus à mettre la main sur un dossier confidentiel émanant des services secrets d’Evil Gouv.
Ces documents font mention d’une opération d’anéantissement de la résistance à l’aide d’un puissant agent innervant, le VX. L’attaque est prévue dans 4 jours à 4h40 au sein du fief de la résistance.
Nous savons de source sure qu’un convoi permettant la synthèse du VX partira de l’entrepot Stockos de Bad Country vers le fief de la résistance d’ici quelques jours. Il faut intercepter ce convoi !
Contactez-nous à cette adresse : http://ctf.challengecybersec.fr/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Nous avons déchiffré le fichier ! 🏁
Merci de votre lecture, et vive Brigitte !