| title | Burger shellcode | |||
|---|---|---|---|---|
| challenge | Prépa 8 — Burger | |||
| difficulty | Hard | |||
| platform | amd64/local | |||
| date | 2024-02-28 | |||
| tags |
|
|||
| author | Ryan Bouchou | |||
| status | solved |
Résumé (1-2 lignes)
Le buffer est morcelé par un canary : on divise le shellcode en deux parties contiguës, on réécrit le canary puis on retourne sur la seconde moitié afin d’exécuter /bin/sh.
- Source : parcours « Prépa exploitation » (niveau Burger)
- Environnement testé : Arch Linux (amd64)
- Fichiers fournis :
main.c, binaire compilé correspondant
Injecter un shellcode fonctionnel alors qu’un canary statique 0xdeadbeefdeadbeef coupe le buffer en deux segments.
- GDB + GEF — traçage dynamique, suivi des registres et de la mémoire.
- pwntools — génération de payloads, orchestrations multi-phase et automatisation.
- NASM — assemblage de shellcodes personnalisés ou instrumentation précise.
- Python 3 — scripts d'automatisation, tooling auxiliaire, parsing.
- C — code vulnérable, harness minimal, reproductions.
- Autres utilitaires : checksec, file, strings, objdump, angr, etc.
buffer[15]est rempli viagetstr; après la lecture, deux vérifications :isadmin == 256(jamais atteignable) puis comparaison du canary. 【F:shellcodes/shellcodes-burger/main.c†L7-L21】- Le
printfinitial fournit l’adresse exacte du buffer. - Comme dans la version « canary », l’adresse de retour se situe
39octets après le début du buffer.
gdbconfirme que seuls 15 octets sont disponibles avant le canary, insuffisant pour un shellcode complet.- L’espace entre la fin du canary et
saved RIPest de 16 octets contigus, assez pour une seconde portion de shellcode. - On peut donc : écrire la première moitié (setup registres) avant le canary, recopier la valeur du canary, puis placer la seconde moitié juste après.
Stratégie : deux morceaux + saut relatif court
piece1(15 B) prépare les registres (xor rsi, rsi,push rsi, etc.).piece2(16 B) charge"//bin/sh"avecmovabs, puis effectue unjmp rel8arrière pour retomber sur la première moitié.- Le payload final :
payload = piece1
payload += b"\x90" * (canary_offset - len(piece1))
payload += p64(0xDEADBEEFDEADBEEF)
payload += piece2
payload = payload.ljust(saved_rip_offset, b"\x90")
payload += p64(piece2_addr)piece2_addrpointe sur le début de la seconde moitié ; le saut relatif ramène l’exécution surpiece1, et le shell s’ouvre. 【F:shellcodes/shellcodes-burger/exploit.py†L19-L68】
- Flag :
{02da1bb071fc47d6525fbcc218212329}obtenu via le shell.
Lecture non bornée et pile exécutable : malgré la présence d’un canary, l’absence de randomisation et de NX permet de composer un shellcode multi-segments.
- Utiliser un canary aléatoire régénéré à chaque exécution.
- Activer NX pour bloquer l’exécution sur la pile, supprimer le
printfde debug.
- Segmenter un shellcode est une option viable lorsque le buffer est fragmenté.
- Explorer des shellcodes auto-modifiants pour réduire la taille du premier segment.
gdb -q ./8-prepa-01-burger,pattern create,distancepour les offsets- Shellcodes x86-64 : shell-storm
exploit.pysc1.nasmsh.nasmtemp.pypour les tests rapides