Ce challenge demande de récupérer des données chiffrées par un ransomware.
Lors de l’analyse du serveur de fichiers, il a été constaté que toutes les copies du film ont été chiffrées par un ransomware, et que les sauvegardes ont été supprimées. L’objectif est donc de récupérer la clé de déchiffrement pour pouvoir restaurer les documents.
Si vous extrayez des binaires, il est très important de ne pas exécuter les binaires extraits sur vos machines principales surtout si vous êtes sur un système Windows.
volatility3 : InstallationL'outil volatility3 peut être installé avec le script suivant :
git clone https://github.com/volatilityfoundation/volatility3.git
cd volatility3/
python3 -m venv venv && . venv/bin/activate
pip install -e ".[dev]"
Pour commencer, rechercher un processus potentiellement responsable du
chiffrement.
pslist permet d'énumérer les processus actifs :
./volatility3/vol.py -f dump_srv2.dmp windows.pslist
En parcourant la sortie, on remarquera un processus au nom inhabituel
VwXIz5Kj3V5NMS (PID 4444).
Pour vérifier son contexte (parent/enfants, threads), afficher l'arborescence
des processus :
./volatility3/vol.py -f dump_srv2.dmp windows.pstree --pid 4444
Extrait de la sortie pstree :
Volatility 3 Framework 2.27.0
Progress: 100.00 PDB scanning finished
PID PPID ImageFileName Offset(V) Threads Handles SessionId Wow64 CreateTime ExitTime Audit Cmd Path
760 684 winlogon.exe 0xcb8f884a9080 6 - 1 False 2025-10-10 07:19:40.000000 UTC N/A \Device\HarddiskVolume3\Windows\System32\winlogon.exe winlogon.exe C:\WINDOWS\system32\winlogon.exe
* 5244 760 userinit.exe 0xcb8f899750c0 0 - 1 False 2025-10-10 07:19:53.000000 UTC 2025-10-10 07:20:27.000000 UTC \Device\HarddiskVolume3\Windows\System32\userinit.exe - -
** 5300 5244 explorer.exe 0xcb8f897ca080 60 - 1 False 2025-10-10 07:19:53.000000 UTC N/A \Device\HarddiskVolume3\Windows\explorer.exe C:\WINDOWS\Explorer.EXE C:\WINDOWS\Explorer.EXE
*** 1232 5300 powershell.exe 0xcb8f8974b080 12 - 1 False 2025-10-10 07:20:54.000000 UTC N/A \Device\HarddiskVolume3\Windows\System32\WindowsPowerShell\v1.0\powershell.exe "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
**** 4444 1232 VwXIz5Kj3V5NMS 0xcb8f8cfce080 4 - 1 False 2025-10-10 07:22:21.000000 UTC N/A \Device\HarddiskVolume3\Users\SRV2\VwXIz5Kj3V5NMScrmw.exe "C:\Users\SRV2\V...exe" C:\Users\SRV2\V...exe
On constate que
VwXIz5Kj3V5NMSest un enfant depowershell.exeet que le
chemin complet de l'exécutable estC:\Users\SRV2\V...exe, cela paraît
suspect.
À partir de là, l'étape suivante est d'extraire le binaire du processus pour
l'analyser.
Lister les objets fichiers en mémoire et rechercher l'offset correspondant :
./volatility3/vol.py -f dump_srv2.dmp windows.filescan 2>/dev/null \
| grep -m1 VwXIz5Kj3V5NMS \
| awk '{print $1}'
Résultat observé :
0xcb8f8a42b690
Récupérer le fichier à cet offset :
./volatility3/vol.py -f dump_srv2.dmp windows.dumpfiles --virtaddr 0xcb8f8a42b690
Cela créera une copie mémoire dans le répertoire courant, par exemple :
file.0xcb8f8a42b690.0xcb8f8c85ed70.ImageSectionObject.VwXIz5Kj3V5NMScrmw.exe.img
Importer le binaire extrait dans un outil de décompilation (Ghidra ou IDA Pro).
Ici nous utilisons Ghidra pour la décompilation et l'analyse des fonctions.
main.En explorant le Function Call Tree on identifie la fonction principale qui
appelle deux fonctions intéressantes.

La fonction main (FUN_7ff72a802710) appelle notamment FUN_7ff72a801f90 et
FUN_7ff72a8025d0, ce sont les fonctions que nous analysons en priorité.

FUN_7ff72a801f90.Cette fonction semble récupérer des informations sur le processus (PID et date
d'exécution), puis appeler trois fonctions utilitaires :
FUN_7ff72a801aa0 : renvoie la liste des modules (DLL) chargés par leFUN_7ff72a801d40 : renvoie la liste des variables d'environnement duFUN_7ff72a8010a0 : prends en entrée les listes de modules, d'environnement,
FUN_7ff72a8010a0 dérivation de clésLa fonction FUN_7ff72a8010a0 se découpe en quatre parties observables :
MOD|, ENV| et DATE| pour les sections correspondantes (lignes
c
NVar3 = BCryptOpenAlgorithmProvider(&local_d8,L"SHA256",(LPCWSTR)0x0,0);
NVar3 = BCryptHashData(local_c8,pbInput,(ULONG)lVar5,0);
Ce qui confirme l'utilisation de SHA-256 sur local_c8 (le tampon construit
précédemment).
4. Sortie : écrit la valeur de 32 octets (256 bits) et une autre de 16 (128)
dans les tableaux fournis en argument, vraisemblablement la clé AES-256 (32
o.) et l'IV (16 o.).

Conclusion : la fonction dérive deux clés (32 et 16 octets) à partir des
modules, variables d'environnement et de la date d'exécution en appliquant
SHA-256 à un buffer formaté.
FUN_7ff72a8025d0.Cette fonction parcourt l'arborescence (ici depuis C:\), examine chaque sous
dossier et filtre ceux qui correspondent à un pattern donné
(*plausible*;*mission*;*shadow*;*protocol*).
Si un correspond, elle appelle FUN_7ff72a802460 pour descendre dans le
dossier.

FUN_7ff72a802460 / FUN_7ff72a802240.FUN_7ff72a802460 liste les fichiers dans le répertoire courant et, pour chaque
fichier, appelle FUN_7ff72a802240.
FUN_7ff72a802240 est la fonction de chiffrement.
Les indices importants observés dans le décompilé :
uVar6 = EVP_aes_256_cbc();: l'algorithme utilisé est AES-256 en modeFUN_7ff72a8010a0.Conclusion :
FUN_7ff72a802240chiffre les fichiers trouvés dans les dossiers
correspondant au pattern, en AES-256 CBC, avec la clé/IV dérivées
précédemment.
Pour déchiffrer les fichiers chiffrés par ce binaire, il faut reproduire
exactement la dérivation de clés :
MOD|...,TIME|/DATE|..., ENV|...) et appliquer SHA-256.volatility3)./volatility3/vol.py -f dump_srv2.dmp windows.dlllist --pid 4444
./volatility3/vol.py -f dump_srv2.dmp windows.envars.Envars --pid 4444
Exemple pour retrouver la ligne pslist associée :
./volatility3/vol.py -f dump_srv2.dmp windows.pslist | grep -i "VwXIz5Kj3V5NMS"
Le binaire construit un buffer avec des préfixes ; le format attendu est du type :
MOD|<module1>
MOD|<module2>
...
TIME|YYYY-MM-DDTHH:MM:SS
ENV|VAR1=value1
ENV|VAR2=value2
...
(Il est crucial de reproduire exactement l'ordre, le découpage, l'encodage et
l'absence/présence d'éventuels retours chariot.)
Le code ci-dessous automatise la récupération et le formatage des informations
depuis la copie mémoire, produit le tampon, calcule l'empreinte SHA-256,
l'affiche complètement et ses 16 premiers octets en hexadécimal.
Adaptez process_name et dump_file selon votre environnement.
#!/bin/bash
set -euo pipefail
# If volatility3 is not installed, install it using git
if [ ! -d "./volatility3" ]; then
git clone https://github.com/volatilityfoundation/volatility3.git
cd volatility3/
python3 -m venv venv && . venv/bin/activate
pip install -e ".[dev]"
cd ..
fi
process_name='VwXIz5Kj3V5NMS'
dump_file='./dump/dump_srv2.dmp'
# Produce ISO time without milliseconds
format_time() {
local t="$1"
if [[ -z "$t" ]]; then
date '+%Y-%m-%dT%H:%M:%S'
return
fi
if [[ "$t" =~ ^[0-9]+$ ]]; then
local secs="$t"
date -d "@$secs" '+%Y-%m-%dT%H:%M:%S'
return
fi
if [[ "$t" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,6})?$ ]]; then
echo "${t%%.*}"
return
fi
if parsed=$(date -d "$t" '+%Y-%m-%dT%H:%M:%S' 2>/dev/null); then
echo "$parsed"
return
fi
echo "Invalid time format: $t" >&2
return 1
}
# Read and sort entries (case-insensitive)
sort_lines() {
local file="$1"
if [[ -z "$file" ]]; then
return 0
fi
iconv -f utf-8 -t utf-8//IGNORE "$file" 2>/dev/null | sed 's/\r$//' | sort -f
}
# Inline derive_keys functionality: build deterministic buffer and print SHA256
derive_keys() {
local mods_file="$1" env_file="$2" ptime="$3"
local tmpbuf
tmpbuf=$(mktemp)
trap 'rm -f "$tmpbuf"' RETURN
if [[ -n "$mods_file" && -f "$mods_file" ]]; then
while IFS= read -r line; do
printf '%s\n' "$line"
done < <(sort_lines "$mods_file") | while IFS= read -r m; do
printf 'MOD|%s\n' "$m" >>"$tmpbuf"
done
fi
local time_iso
time_iso=$(format_time "$ptime")
if [[ $? -ne 0 ]]; then return 2; fi
if [[ -n "$time_iso" ]]; then
printf 'TIME|%s\n' "$time_iso" >>"$tmpbuf"
fi
if [[ -n "$env_file" && -f "$env_file" ]]; then
while IFS= read -r line; do
printf '%s\n' "$line"
done < <(sort_lines "$env_file") | while IFS= read -r e; do
printf 'ENV|%s\n' "$e" >>"$tmpbuf"
done
fi
local buf_content
buf_content=$(cat "$tmpbuf")
# Print the buffer and compute sha256
printf '%s' "$buf_content"
local sha_hex
if command -v sha256sum >/dev/null 2>&1; then
sha_hex=$(printf '%s' "$buf_content" | sha256sum | awk '{print $1}')
elif command -v openssl >/dev/null 2>&1; then
sha_hex=$(printf '%s' "$buf_content" | openssl dgst -sha256 -r | awk '{print $1}')
else
echo "No sha256 utility (sha256sum or openssl) found" >&2
return 3
fi
local first16_hex=${sha_hex:0:32}
printf 'SHA256: %s\n' "$sha_hex"
printf 'FIRST_16_BYTES(hex): %s\n' "$first16_hex"
return 0
}
# Get pid of the process
pid=$(./volatility3/vol.py -f "$dump_file" windows.pslist | grep -i "$process_name" | awk '{print $1}')
# DLL list (drop first 4 header lines, case-insensitive sort)
dll_loads=$(./volatility3/vol.py -f "$dump_file" windows.dlllist --pid $pid | awk '{print $6}' | sed '1,4d' | sed '/^[[:space:]]*$/d' | sort -f)
# produce YYYY-MM-DDTHH:MM:SS
date_time=$(./volatility3/vol.py -f "$dump_file" windows.pslist | grep -i "$process_name" | awk '{
d=$9; t=$10;
split(t,a,"[.]");
sec=a[1];
printf "%sT%s", d, sec;
}')
# Run the volatility3 command to extract environment variables
environment_vars=$(./volatility3/vol.py -f "$dump_file" windows.envars.Envars --pid $pid | awk '/^[[:space:]]*[0-9]+[[:space:]]/ { var=$4; $1=$2=$3=$4=""; sub(/^ +/,""); print var"="$0 }' | sed '/^[[:space:]]*$/d' | sort -f)
# Prepare temporary files for modules and environment variables, then run derive_keys
mods_tmp=$(mktemp)
env_tmp=$(mktemp)
trap 'rm -f "$mods_tmp" "$env_tmp"' EXIT
if [ -n "$dll_loads" ]; then
printf '%s\n' "$dll_loads" >"$mods_tmp"
else
: >"$mods_tmp"
fi
if [ -n "$environment_vars" ]; then
printf '%s\n' "$environment_vars" >"$env_tmp"
else
: >"$env_tmp"
fi
echo "Derived keys (from extracted process info):"
derive_keys "$mods_tmp" "$env_tmp" "$date_time" || echo "derive_keys failed"
# Dump the binary of the process
offset_file=$(./volatility3/vol.py -f dump/dump_srv2.dmp windows.filescan 2>/dev/null \
| grep -m1 VwXIz5Kj3V5NMS \
| awk '{print $1}')
./volatility3/vol.py -f "$dump_file" windows.dumpfiles --virtaddr "$offset_file"
Ce script :
Voici un code Python (utilisant pycryptodome) pour déchiffrer un fichier AES
CBC.
Remplacer key et iv par les valeurs dérivées trouvées précédemment et
initialiser encrypted_file / decrypted_file avant d'exécuter.
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
def decrypt_file(input_path: str, output_path: str, key: bytes, iv: bytes):
"""Déchiffre un fichier AES-CBC"""
cipher = AES.new(key, AES.MODE_CBC, iv)
with open(input_path, "rb") as f_in:
ciphertext = f_in.read()
plaintext = unpad(cipher.decrypt(ciphertext), AES.block_size)
with open(output_path, "wb") as f_out:
f_out.write(plaintext)
print(f"[+] Fichier déchiffré : {output_path}")
if __name__ == "__main__":
# Remplacer par les valeurs réellement dérivées
key = bytes.fromhex("8dac...") # 32 octets (256 bits)
iv = bytes.fromhex("8dac...") # 16 octets (128 bits)
encrypted_file = "script.odt"
decrypted_file = "script1.odt"
decrypt_file(encrypted_file, decrypted_file, key, iv)