XCTF的国际赛分站De1CTF,赛中只做出两道比较基础的题,weapon和A+B Judge,其中A+B Judge应该是非预期解,我队最终排名56。
0x01 Weapon
程序分析
checksec查看保护机制,看到保护全开
1 | [*] '/home/sunxiaokong/Desktop/pwn/De1CTF-2019/Weapon/pwn' |
程序中维护了一个大小为10的weapon数组,weapon结构体定以如下,由一个8字节的指针(name),和一个8字节的整数(size)组成.
程序提供了create、delete、rename功能,分别对应创建weapon结构、删除、编辑name。
漏洞点
漏洞点位于delete功能函数中,free掉name指针后未将name指针置零,size也未置零,造成存在悬挂指针。
利用思路
通过远程double free测试,远程libc版本<2.26,需通过fastbin attack来利用
由于程序没有提供输出name内容的功能,因此不能通过正常的UAF手段来泄露libc基地址。因此,要通过覆盖**_IO_2_1_stdout**来泄露libc地址。参考链接如下:
https://www.sunxiaokong.xyz/2019-04/lzx213410/#bms
https://xz.aliyun.com/t/5057#toc-1
通过**_IO_2_1_stdout来泄露的关键是,要通过fastbin attack分配堆到_IO_2_1_stdout处,从而改写结构体造成leak,而程序又开启了PIE,因此要通过partial overwrite来爆破_IO_2_1_stdout的地址,也就是,使fastbin中的fd指针指向libc中的地址,然后通过改写低字节,爆破中间字节来使下一次分配时分配到_IO_2_1_stdout**。
首先要使得fastbin中的fd指针指向libc中,由于程序限定了size必须<0x60,因此不能直接获得指向**libc(main_arena-88)**的fd指针。这里可以通过UAF,先释放一个fastbin_chunk,再将其伪造成符合unsorted_bin大小的chunk,再释放一次,这样,该chunk就会既存在fastbin中也存在于unsortedbin中,fd指针就会指向libc。然后再进行常规的爆破就可以了。
成功泄露libc后,劫持malloc_hook为one_gadget即可getshell
完整EXP
1 | #-*-coding:utf8-*- |
0x02 A+B Judge
题目分析
本题应该是属于出题人非预期解法
题目提供了一个在线编译服务,可以给他一个源代码编译,并且它会将输出结果打印出来
题目的server.py脚本如下
1 | #! /bin/python |
获取flag
通过题目提供的docker文件夹,可以看到,flag与server.py在同一个目录下
而server又没有过滤system等函数,因此直接跑system(“cat ./flag”)查看flag。
1 | #include <stdio.h> |
0x03 Unprintable(大致思路)
比赛时没有做出来,根据官方writeup整理了一下大致思路,记录一下。
程序分析
如图,main函数中会提供一个栈的地址,关闭stdout,然后往.bss段的buf中读入4096字节,然后是一个格式化字符串漏洞,最终调用exit()函数退出。
利用思路
由于关闭了stdout,并不能直接通过格式化字符串漏洞泄露地址什么的。
因此这道题的利用点在于exit()函数。gdb调试一下,单步跟进去exit()函数中,在**_dl_fini()函数**中可以跟到一个可以利用的点
这里,rdx的值为零,往上查看汇编代码,可以看到r12的来源
由于用gdb调试时动态库的装载地址都是不变的,所以可以下断点到这里看看rax和rbx的值。调试后可以看见,[rax+0x8]即为**0x600dd8 (__do_global_dtors_aux_fini_array_entry),这是程序中.bss段前面的地址,而[rbx]为0x7ffff7ffe168,这个地址是存在于ld.so中的,该地址处的值为0。也就是说,最后call的时候,call的地址是[0x600dd8 (__do_global_dtors_aux_fini_array_entry) + 偏移(*0x7ffff7ffe168)]**,而0x7ffff7ffe168这个地址,在exit()函数调用时,是存放在栈上的。
根据官方writeup的说法,应该是通过控制这个栈地址来控制rbx的值,最终使r12指向.bss段,劫持程序的执行流。
但是我自己在追踪rbx的来源时,并没有追到这里,可能是我调试水平太菜了吧。。。
劫持执行流之后就是一些ROP操作和gadget的利用了。这部分操作也还没有完全搞懂,这里主要是学习了exit()函数里的利用的点。
完整的利用过程在官方writeup:
https://github.com/De1ta-team/De1CTF2019/blob/master/writeup/pwn/Unprintable/README_zh.md