Back to last GreHack edition, Herbert Bos has presented a novel technique to
exploit stack-based overflows more reliably on Linux. We review hereafter this
new exploitation technique and provide an exploit along with the vulnerable
server. Even if this technique is portable to multiple platforms, we will focus
on a 64-bit Linux OS in this blog post.
All sample code used in this blogpost is available for download
here
We’ve got a signal
When the kernel delivers a signal, it creates a frame on the stack where it
stores the current execution context (flags, registers, etc.) and then gives
the control to the signal handler. After handling the signal, the kernel calls
sigreturn to resume the execution. More precisely, the kernel uses the
following structure pushed previously on the stack to recover the process
context. A closer look at this structure is given by figure 1.
Now, let’s debug the following program (sig.c) to see what really happens when
handling a signal on Linux. This program simply registers a signal handler to
manage SIGINT signals.
First of all, we need to tell gdb to not intercept this signal:
Then, we set a breakpoint at the signal handling function, start the program
and hit CTRLˆC to reach the signal handler code.
We note here that the frame #1 is created in order to resume the process
execution at the point where it was interrupted before. This is confirmed by
checking the instructions pointed by rip which corresponds to sigreturn
syscall:
Figure 1 shows the stack at signal handling function entry point.
Figure 1: Stack at signal handling function entry point
We can check the values of some saved registers and flags. Note that sigcontext
structure is the same as uc_mcontext structure. It is located at rbp + 7 * 8
according to figure 1. It holds saved registers and flags value:
Now, we can verify that after handling the signal, registers will recover their
values:
Exploitation
If we manage to overflow a saved instruction pointer with sigreturn address and
forge a uc mcontext structure by adjusting registers and flags values, then we
can execute any syscall. It may be a litte confusing here. In effect, trying to
execute a syscall by returning on another syscall (sigreturn) may be strange at
first sight. Well, the main difference here is that the latter does not require
any parameters at all. All we need is a gadget that sets rax to 0xf to run any
system call through sigreturn syscall. Gadgets are small pieces of instructions
ending with a ret instruction. These gadgets are chained together to perform a
specific action. This technique is well-known as ROP: Return-Oriented
Programming [Sha07].
Surprisingly, it is quite easy to find a syscall ; ret gadget on some Linux
distribution where the vsyscall map is still in use. The vsyscall page is
mapped at fixed location into all user-space processes. For interested readers,
here is good link about vsyscall.
Bosman and Bos list in [BB14] locations of sigreturn and syscall gadgets for
different operating systems including FreeBSD and Mac OS X.
Assumed that we found the required gadgets, we need to arrange our payload as
shown in figure 3 in order to successfully exploit a classic stack-based
overflow. Note that zeroes should be allowed in the payload (e.g. a non strcpy
vulnerability); otherwise, we need to find a way to zero some parts of
uc_mcontext structure.
The following code (srop.c) is a proof of concept of sigreturn oriented
programming that starts a /bin/sh shell:
The programm fills a uc_mcontext structure with execve syscall parameters.
Additionally, the cs register is set to 0x33:
Instruction pointer rip points to syscall; ret gadget.
rax register holds execve syscall number.
rdi register holds the first paramater of execve (“/bin/sh” address).
rsi register holds the second parameter of execve (“/bin/sh” arguments).
rdx register holds the last parameter of execve (zeroed at struture initialization).
Then, the program overflows the saved rip pointer with mov %rax, $0xf; ret
gadget address (added artificially to the program through gadget function).
This gadget is followed by the syscall gadget address. So, when the main
function will return, these two gadgets will be executed resulting in sigreturn
system call which will set registers values from the previously filled
structure. After sigreturn, execve will be called as rip points now to syscall
gadget and rax holds the syscall number of execve. In our example, execve will
start /bin/sh shell.
Code
In this section we provide a vulnerable server
(vuln.c) and use
the SROP techniqpue to exploit it
(exploit.c).
Vulnerable server
The following program is a simple server that replies back with a welcoming
message after receiving some data from client. The vulnerability is present in
the handle_conn function where we can read more data from client (4096 bytes)
than the destination array (input) can hold (1024 bytes). The program is
therefore vulnerable to a classical stack-based overflow.
Exploit
We know that our payload will be copied in a fixed location in .bss. (at
0x6012c0). Our strategy is to copy a shellcode there and then call mprotect
syscall in order to change page protection starting at 0x601000 (must be a
multiple ot the page size).
Figure 2: Payload copied in .bss
In this exploit, we overflow our vulnerable buffer as shown by figure 3. First,
we fill our buffer with a nop sled (not necessary) followed by a classical
bindshell. This executable payload is prepended with an address pointing to the
shellcode in .bss (see figure 2).
Our goal is to change protection of memory page containing our shellcode. More
precisely, we want to make the following call so that we can execute our
shellcode:
Here, is what happens when the vulnerable function returns:
The artificial gadget is executed. It sets rax register to 15.
Our artificial gadget is followed by a syscall gadget that will result in a sigreturn call.
The sigreturn uses our fake uc_mcontext structure to restore registers values. Only non shaded parameters in figure 3 are relevant to the exploit. After this call, rip points to syscall gadget, rax is set to mprotect syscall number, and rdi, rsi and rdx hold the parameters of mprotect function. Additionally, rsp points to our payload in .bss.
mprotect syscall is executed.
ret instruction of syscall gadget is executed. This instruction will set instruction pointer to the address popped from rsp. This address points to our shellcode (see figure 2).
The shellcode is executed.
Figure 3: Stack after overflowing input buffer
Replaying the exploit
The above code has been compiled using gcc (gcc -g -o vuln vuln.c) on a
Debian Wheezy running on x_86_64 arch. Before reproducing this exploit, you
need to adjust first the following addresses:
SYSCALL_GADGET
RAX_15_GADGET
DATA
References
[BB14] Erik Bosman and Herbert Bos. We got signal. a return to portable exploits. (working title, subject to change.). In Security & Privacy (Oakland), San Jose, CA, USA, May 2014. IEEE.
[Sha07] Hovav Shacham. The geometry of innocent flesh on the bone: Return-into-libc without function calls (on the x86). In Proceedings of the 14th ACM Conference on Computer and Communications Security, CCS ’07, pages 552– 561, New York, NY, USA, 2007. ACM.