祥云杯2022 sandboxheap和bitheap
我是先写的sandboxheap,开始还以为sandbox文件是多余的,就直接单独拿sandboxheap去写,就在本地打通后,发现远程是有通过sandbox去执行sandboxheap,当时我就没能写出来。
山重水复疑无路,柳暗花明又一村!没想到bitheap漏洞和sandboxheap一模一样,而且没有sandbox,当时就只写出了bitheap。
漏洞分析
这两道题都是一样的漏洞,主要是看懂编辑函数中的溢出和加密:
如下图将堆块写入数据的大小乘8 ,然后加1;最后是通过基于输入的8个字符去对堆中的一个字节进行位运算,每个字符可以操作堆中一个字节的一位;最后会多出一个字节影响下一个堆块的size
如下图sub_C61函数,基于堆块中的字符来位运算,但堆块初始值都是0
,最后被写入堆块的也是0 ;如果输入的是\x31
字符会让最后被写入堆块的的是1。
堆利用
kali对这道题使用patchelf会报错,这里我是用ubuntu完成的
由于溢出的字节只能更改下一个堆块的size的标志位(判断堆块是否被使用),需要对下一个堆块的prev_size和标志位修改,让其释放后进入unsortedbin,然后与上边的unsortedbin合并。
具体写入
bitheap
利用堆块重叠直接去修改__free_hook为system函数即可。
完整的exp如下:
1 |
|
sandboxheap
以前都是写调用prctl函数,禁止系统调用开启的沙盒题,但这题目直接使用沙盒程序来保护其它程序。
利用堆块重叠修改__free_hook到setcontext段上,释放堆块会执行setcontext段上的代码,在此过程中rdi就是被释放堆块堆块的地址,进而劫持rsp。
1 |
|
最初复现时我想在堆上执行ROP将flag通过orw读出来,但是沙盒程序好像也禁用open之类的系统调用,最后看网上别的师傅写的wp才知道需要通过int 3
这个软中断去绕过。
关于int 3的绕过
关于int 3的绕过我是看ctftime上关于Sandybox的wp,由于我英语不太好只能理解到这里了。
sandbox程序fork一个子进程,通过ptrace函数跟踪子进程。
调用ptrace(PTRACE_SYS, pid, 0, signal)使内核在子进程进入和退出系统调用时都将其暂停。
sandbox程序ida中主要的伪代码(简化):
1 |
|
- 循环开头处的 ptrace(PTRACE_SYSCALL, v4, 0LL, 0LL)、waitpid(v4, 0LL, 0)是等待子进程进入系统调用;
- 中间的代码就是获取当前的系统调用号(rax),过滤一些系统调用;
- 循环结尾处的 ptrace(PTRACE_SYSCALL, v4, 0LL, 0LL)、waitpid(v4, 0LL, 0)是等待子进程离开系统调用。
使用int 3
这个软中断后,可以让父进程循环开头处的 ptrace误以为子进程已经进入系统调用,但实际上子进程并未进入系统调用;当子进程真正进入系统调用后,是触发循环结尾处的 ptrace, 事实上ptrace(PTRACE_SYSCALL, v4, 0LL, 0LL)并不能判断子进程是进入系统调用还是离开系统调用,这样就绕过了中间对系统调用的过滤。
使用int 3后:
- 循环结尾处的 ptrace(PTRACE_SYSCALL, v4, 0LL, 0LL)、waitpid(v4, 0LL, 0)是等待子进程进入系统调用;
- 循环开头处的 ptrace(PTRACE_SYSCALL, v4, 0LL, 0LL)、waitpid(v4, 0LL, 0)是等待子进程离开系统调用。
相当于反转了循环,之后就可以顺利执行接下来的shellcode了。
完整的exp如下:
1 |
|
ctftime上关于Sandybox的wp:https://ctftime.org/writeup/20115
实际上ptrace函数还有其它更多的功能,具体请看:https://www.anquanke.com/post/id/231078