xman冬令营/NullCon2019/BUUCTF 部分pwn writeup
xman冬令营选拔赛
前言
一共四个题,kernel放弃了,arm本地通了远程失败,format最后队友出了就懒得调了。
nosyscall
程序分析
逻辑很简单,可以写0x10的shellcode并执行,禁了所有的系统调用,开始flag读到了一个mmap的地址里,这里用cmp爆破,正确就报错EOF否则回到开头死循环卡住。
1 | __int64 __fastcall main(__int64 a1, char **a2, char **a3) |
exp.py
遇到EOF手动ctrl+z,进入死循环ctrl+c,之后手动加下dis
1 | #coding=utf-8 |
arm
前言
题目很简单,主要记录一下arm pwn的一般做题方式。
程序分析
可以add、del、print、edit,有UAF,是最简单的题了,题目本身就不再多说了,在name构造好一个fake chunk,malloc一个0xa0的块释放泄露libc,UAF到name进而可以Edit到notelist写入free_hook,再Edit即可改成system。
调试环境搭建
查到了两篇比较详细的介绍,分别是arm32以及arm64,可以用qemu-arm -L /usr/arm-linux-gnueabi ./pwn启动或者socat tcp-l:10002,fork exec:"qemu-arm -g 1234 -L /usr/arm-linux-gnueabi ./pwn",reuseaddr用socat启动gdb attach上去。后者我调试发现有gdb server无法启动的问题,所以作罢了。
最后的解决方法是在exp.py里用p = process(["qemu-arm", "-L", "/usr/arm-linux-gnueabihf", "./pwn"])启动,之后开另一个终端用gdb -q ./pwn再查看这个进程的pid,在gdb内部attach [pid]进行调试,因为我们调试的是qemu-arm所以地址都是外层的,内层地址看不到。
开始泄露libc的时候我是启动时候加了-g参数直接在gdb里删除了一个大的块查看偏移,之后再用exp.py里调试。
exp.py
1 | from pwn import * |
format
程序分析
似乎也没什么说的,嵌套了好几层函数,我的exp是改的one_gadget本地通了数据太多远程挂了,后来发现有后门,就更简单了,队友出了也懒得改了。
exp.py
1 | #coding=utf-8 |
NullCon2019 babypwn
前言
其实只有一道题,记一下scanf的性质,即遇到+/-输入会直接ret即不会将数据输入其中直接跳过,也不会崩溃。
程序逻辑
先格式化字符串漏洞后栈溢出,输入使用的是scanf,第一次泄露libc之后拿scanf绕过canary改返回地址回main再get shell
exp.py
1 | #coding=utf-8 |
BUUCTF
ciscn_2019_sw_5/ciscn_2019_nw_2
程序逻辑
libc-2.27,固定分配0x70(chunk大小为0x80)只能free3次,但malloc之后会输出一次数据,输入技巧题。
漏洞利用
先double free,分配到tcache_struct里0x80对应的位置(需要爆破1/16),构造一个tcache chain出来,比如我这里是往里写一个*2b0而*2b0后面跟着*2e0,进而得到一个*2b0->*2e0的链,之后分配到*2b0修改*2e0的size为0x421(注意后面要有一个fake_chunk的prev_in_use=1),Free掉这个块即可得到Ub(注意此时因为fd=main_arena+96又出了新的链*2e0->main_arena+96),再Malloc可以根据末位一定是\xa0分配到*2d0泄露libc,再分配到main_arena+96修改top_chunk到__malloc_hook前面,最后把刚才那个0x420大小的ub分配完再走top_chunk,最终分配到__malloc_hook改为gadgets[1]即可。
问了队友胡师傅(ID太长记不住),有另一种解法,改tcache的块数量为0xff,double free用来伪造fake chunk的size为0x210,与此同时还能写tcache[0x80]这个块到fake chunk,之后可以分配得到这个块,再释放就到了ub。再分配就可以覆写tcache[0x80]到malloc_hook。
远程数据发送过快可能会粘包,所以这里加了raw_input()多试试就好。
exp.py
1 | #coding=utf-8 |
ciscn_final_4
前言
应该是UCTForw_heap的原型题,不过要求更严苛了一点
程序分析
Init里禁了系统调用execve
题目fork了一个子进程,动作都是在子进程里执行的,父进程去执行watch函数,这个函数里有个循环,ptrace(PTRACE_SYSCALL, a1, 0LL, 0LL);会使得子进程在每次进行系统调用以及结束一次系统调用的时候都会被内核停下来。PTRACE_GETREGS调用将寄存器信息读取出来,往后判断的是寄存器rax的值,即判断系统调用号,这里禁了open/mmap/fork/vfork/ptrace这些系统调用。
1 | // local variable allocation has failed, the output may be wrong! |
子进程里有new/delete/write等功能,存在double free/UAF。最多分配32个块,最大分配的size为0x1000。程序没开PIE。
漏洞利用
为了调试先patch一下原程序,只留子进程方便调试。最开始是想直接HOF一把梭劫持到setcontext+53,patch的过了源程序崩了,因为HOF是触发erro报错,这里会调用mmap这个系统调用,所以直接挂了。最后还是按照之前*CTF的那道题自己伪造一个0x60的small bin,之后ub attack改IO_list_all,最终触发_IO_str_overflow等一系列的调用链进到setcontext+53,这里用mprotect修改heap权限,加上可执行,部署shellcode,因为open被禁了,拿openat(0,absolute_path,0)来open文件,之后r/w即可。
第二种方法是结合栈的,这种漏洞其实是很难发现的,在这里我们在malloc的时候输入的位置+0x38是我们输入name的地方,因为没有开PIE我们可以预先设置好rop chains,之后用堆漏洞改__malloc_hook到一个pivot gadgets,从而在malloc的时候去执行rop chain。这个exp就不写了233。
exp.py
1 | #coding=utf-8 |