ByteCTF2019 PWN 部分writeup
前言
是七哥最后一场线上赛,看七哥姚老板P1umer日天日地日虚拟机日浏览器感觉自己差的太多了,要更努力。
mheap
程序逻辑
程序是自己实现的malloc和free,有alloc、show、free、edit等四个功能,自己分析半天没找到漏洞,看了wp才晓得。
alloc不限制输入的size,输入一个非负的size,首先0x10对齐,之后加上0x10的chunk头。自定义的chunk_addr[0]为其size,chunk_addr[1]为上一个释放的堆块地址。
1 | unsigned __int64 __fastcall Alloc(unsigned int idx) |
Free函数将释放堆块的chunk[1]处改为上一个释放堆块的地址,0x4040d0处填入此堆块的地址,类似实现fastbin的链表结构。
1 | int __fastcall Free(unsigned int idx) |
Edit可以编辑0x10字节的堆块,Show可以打印堆块内容。
漏洞利用
漏洞出现在get_input函数,read函数向ptr+count处写数据,一旦我们分配的size大于add的总和(超过0x1000的边界),read写边界之后的数据是非法的,因此会返回-1。这里返回值的判断出了问题,此时会绕过判断,count–,变成-1,现在向ptr-1处写,依然会写到边界后的数据,因此依然写入失败,返回-1,再继续后向尝试…,一直到遇到换行符结束寻找退出(注意此时还是写入了数据,只是不够我们要求的数据长度,提前结束了read的后向寻址),或者一直没遇到换行符,找到一个足够容纳数据长度的地址开始写。利用这个漏洞我们可以后向修改一个释放chunk的chunk[1],为0x4040e0,之后再分配一个相同大小的chunk就会把这个chunk分配掉,0x4040d0处写入0x4040e0,之后再分配一个0x2333000的chunk,程序会从0x4040e0开始找,找一个size部分合适的堆块分配,而0x4040e0的size位置正合适,因此分配这个块,可以覆写0x4040f0处即chunk_list[2]为atoi_got,Show泄露libc,Edit改成system函数,之后atoi(“/bin/sh\x00”)拿shell。
1 | __int64 __fastcall get_input(__int64 ptr, signed int size) |
1 | 0x4040d0: 0x00000000004040e0 0x0000000000000000 |
exp.py
1 | #coding=utf-8 |
mulnote
漏洞利用
free之后10s才清空,UAF
exp.py
1 | #coding=utf-8 |
note_five
漏洞利用
看着17师傅的exp复现了一遍。程序有off-by-one,禁用了fastbin,上来先构造overlapchunk,unsorted bin修改global_max_fast,之后可以使用smallbin。在stdout前寻找合适size的fake_chunk,这里用的是stderr的flag字段的0xfb作为size,分配0xe8的堆块到这里,之后再往后构造一个fake_chunk,从而得以覆写stdout,泄露libc。拿shell的方法是构造fake_vtable(之前的数据照抄,0xd8处改成伪造的vtable地址,使得__xsputn=one_gadget即可)
1 | gdb-peda$ p* (struct _IO_jump_t *)0x7ffff7dd26c8 |
exp.py
1 | #coding=utf-8 |
vip
前言
第一次做seccomp类的题目,考察bpf的规则编写.
漏洞利用
Edit函数里可以修改0x4040e0进行溢出写,也可以通过一些方法改open函数的返回值为0进行溢出写,这里利用的就是seccomp的filter修改系统调用的返回值为errno(0)。根据manual可以知道这个调用是把code作为errno的值返回给用户(并不执行系统调用)SECCOMP_RET_ERRNO
This value results in the SECCOMP_RET_DATA portion of the fil‐
ter's return value being passed to user space as the errno
value without executing the system call.
因此我们用以下规则可以进行如上的操作:1
2
3
4
5
6
7struct sock_filter filter[] = {
BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 0), //A = sys_num
BPF_JUMP(BPF_JMP|BPF_JEQ, 257, 1, 0), // if A == 257, goto 4 else goto 3
BPF_JUMP(BPF_JMP|BPF_JGE, 0, 1, 0), // if A >= 0,goto 4 else goto 3
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO),
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
};
1 | ssize_t __fastcall get_input(void *ptr, int size) |
改完之后ORW得到flag(很久没用这个了,注意fd默认是递增的,因此是0,1,2->3),另外open函数的系统调用是openat,系统调用号为257,我们禁用它之后用opem(sys_num:0)来打开flag进行读取。
exp.py
1 | #coding=utf-8 |