Saturday, April 5, 2014

Playing around with SROP

Recently, an interesting paper came out[1] that described an interesting mechanism that could be used for the exploitation of memory corruption vulnerabilities. While the paper does a good job of explaining how such an attack could be performed, this is my attempt at reproducing the same with a PoC and introducing a library that could be used to write such exploits in an easier fashion. This paper was interesting for two reasons : it talked about "portable shellcode" and "relative ease of exploitation".

-============================]
Vulnerable code

The code can be viewed here[2]. It contains an obvious overflow, is compiled statically, and has inline assembly to make the demonstration even easier. The server code also has intentional info leaks where it gives out information about a the address of a local buffer and the address of a page mapped in using mmap, that has RW permissions. The PoC exploit for 32 bit does not use the mmap'd region and is run on non-ASLR systems.


-============================]
Concept

Signal handlers return to a stub that perform a system call named "sigreturn". Its purpose is to restore the execution state of the program that had been interrupted when the signal handler was called. It does this by popping a signal frame that contains the values of all registers and storing those values into the registers.

This presents us with an opportunity to craft fake signal frames and perform a kind of "SROP-chaining".

Assumptions made :-
[a] EAX/RAX has the register value of 0x77/0xf.
[b] The "syscall/int 0x80 instruction is present"
[c] There is a writeable page in memory whose address we know.
[d] Nulls are allowed in the payload.

Advantage of using SROP the authors[1] point out is that :-
- You only need the address of one gadget "syscall/int 0x80; ret".
- For the most part, the signal frames that are created can be reused.

-============================]
Exploitation(32-bit)

Let us consider the vulnerable program shown at [2]. An EIP overwrite can be achieved by giving in input of length greater than the buffer size. We can notice that for ease of presenting the idea, we have added an inline gadget to meet requirement [a][b], and a call to mmap to accurately meet [c]. The input is read in the vulnerable program using "read" to circumvent the null space terminal requirement and meet [d].

The exploit can be viewed here[3]. It performs a jump to the gadget that performs a sigreturn call which in turn will load the sigreturn frame from the stack and load it into the registers. In order to ease the creation of the signal frame you could choose to do use the Frame module[4] like the following :-
>>> from Frame import SigreturnFrame
>>> frame = SigreturnFrame()
>>> frame.setvalue("<register>", <value>)
>>> sploit += frame.get_frame()

An exploit that spawns an execve shell can be found at [3].

-============================]
Further plans
- Add support/poc for x64 , its been added[5], thanks for [Segfault]Reno for helping out.
- Add support/poc for ARM
- Try finding out global unvarying addresses for gadgets such as "int 80/syscall/svc; ret" across multiple distros.

References :-
[1] Framing Signals, A return to portable shellcode
[2] https://github.com/eQu1NoX/srop-poc/blob/master/poc-32.c
[3] https://github.com/eQu1NoX/srop-poc/blob/master/sploit-32.py
[4] https://github.com/eQu1NoX/srop-poc/blob/master/Frame.py
[5] https://github.com/eQu1NoX/srop-poc/blob/master/sploit-64.py

No comments:

Post a Comment