Heap-Seccomp沙盒逃逸之Glibc2.29的ORW
C沙箱简介
在ctf中常见的实现沙箱的机制有两种,一种是prctl函数调用
,另一种就是seccomp库函数
而其一般都会禁用execve函数,使之无法直接getshell
在严格模式下甚至只支持exit(),sigreturn(),read()和write()的使用,使用其他系统调用都将会杀掉进程
对于过滤模式下,就可以指定允许使用哪些系统调用,规则是bpf,具体可以使用seccomp-tools查看
prctl函数调用
可以通过第一个参数控制程序进程去做什么,该参数常见得为38
和22
两种情况
1 | prctl(38, 1LL, 0LL, 0LL, 0LL); |
- 第一个参数为38,第二个参数为1时,禁用execve且子进程一样
1 | prctl(22, 2LL, &v1); |
- 第一个参数为22
- 第二个参数为1时,只允许调用read/write/_exit(not exit_group)/sigreturn这几个syscall
- 第二个参数为2时,则为过滤模式,其中对syscall的限制通过参数3的结构体来自定义过滤规则
seccomp
首先对seccomp进行初始化
1 | v1 = seccomp_init(0LL); |
- 为0表示白名单模式,为0x7fff0000U则为黑名单模式
1 | //之后对seccomp添加规则 |
- v1对应上面初始化后返回得值
- 0x7fff0000U即黑名单模式
- 2对应系统调用号
- 0 对应系统调用所限制使用得参数数量,0即不限制
简单演示-pwnable_asm
程序分析
再用ida查看伪代码分析
发现虽然开启了pie保护,但是有一段使用mmap分配的固定地址的内存s
而sandbox函数内则使用seccomp对程序进行保护
而最后面则是利用输入的数据s来进行执行程序,即我们可以通过输入汇编代码插入程序来执行
seccomp-tools
查看允许使用的系统调用
也就是说只允许使用read\write\open函数来进行系统调用读取flag
高难度-Balsn2019_Plain
程序分析
一个保护全开且存在沙箱的堆题
其程序存在add、show、delete三个功能
而漏洞点主要在add函数中存在offbynull
漏洞
且程序使用的glibc版本为2.29
利用思路
leak libc_base、heap_base
- 修复被seccomp打乱的堆块
- 生成大量堆块使地址的低16位为
0000
,以便off by null - 利用free largebin使之残留
fd_nextsize
和bk_nextsize
- 在被free的largebin中伪造chunk_size 并修改
fd_nextsize
- 通过unlink attack使堆块申请到fake_chunk上
- 利用重叠的堆块泄露libc地址
Bypass seccomp
- 在libc中查找gadgets,用于修改rdx寄存器
- 控制程序执行到
setcontext
结构体中 - 由setcontext中的gadgets控制寄存器执行orw
- 在堆块中填入orw的rop链
- 最后free掉该堆块,触发
setcontext
中的gadgets,得到flag
泄露地址
申请足够的多的堆块来修复被seccomp打乱的堆布局
将地址填充,提前布局控制下一个堆块地址的低16位为0
申请一个
0x1000
的堆块并free掉,形成largebin此时被free掉的0x1000的堆块fd_nextsize和bk_nextsize将指向该堆块即
0x555555760000
接下来在申请一个0x28的堆块,该堆块将落在被free掉的0x1000堆块上,且其中将包括该堆快的地址(
fd\bk_size的指针
)0x555555760000
,而0x241
则是后续overlap的关键该堆块将被用于作为后续的fake_chunk,在其中因为程序会在输入的末尾加
\x00
,而此时被\x00
覆盖的地址也将指向该堆块地址,也正是第2步布局的原因之后再申请大量堆块促使idx_60地址落到
0x250
上,即idx_61可以落到0x555555760250
处其次再新建7个0x28的堆块并free掉,用于填满tcache
此时在free的堆块就将进入到fastbin中了,而现在free掉
idx_53
\idx_59
\idx_52
三个堆块(后续将在这三个堆块的基础上操作)在把前面填满tcache的bins申请回来后
之后申请的两个堆块则用于绕过unlink的检查并控制其fd和bk都指向
fake_chunk
第三次申请的则用于对idx_60堆块的
prev_size
位修改,对应第4步中fake_chunk的size最后free掉idx_60这个大堆块到unsortedbin中
而由于前面的unlink操作,才使unsortedbin的下个地址得以申请到
0x555555760010
处,最后完成overlaping,继而free掉fake_chunk内的堆块来泄露libc_base和heap_base
Bypass seccomp
先在overlap中通过chunk的fd指针控制堆块可以申请到
free_hook
上然后在free_hook中填入一条gadgets
由于是glibc2.29的原因,其参数rdi不会在赋值到rdx,所以并不能直接使用setcontext,而是需要先找到一条特殊的gadgets,来将rdi的值传到rdx上,之后再由该gadgets跳转到
setcontext
结构体上对此gadgets可以使用
ropper
来对libc进行查找1
ropper -f ./libc-2.29.so search 'mov rdx'|grep "rdi + 8"
构造rop链,控制程序执行到setcontext中
然后在执行free函数时,会先执行之前放在free_hook中的gadgets,之后通过
jmp rax
跳转到setcontext中,而setcontext中有很多的可以gadgets来对寄存器操作,进而控制程序执行到我们后面的rop链中之后通过构造orw的rop链来控制程序读取flag文件,最后对该堆块free即可进入上述的程序流中得到flag
最后对该堆块free,拿到flag
EXP
1 | #!/usr/bin/env python2 |
参考链接:
https://ctf-wiki.org/pwn/linux/sandbox/c-sandbox-escape/