由于沙箱规则比较多,规则制定比较自由,所以下文重点探讨绕过的技术。
使用_at/v/2_系统调用
这里分别指的是几个系统调用的后缀和前缀,比如:
- 使用
execveat
代替execve
,拿到shell
后,使用shell
内置命令读取flag
:echo *; read FLAG < /flag;echo $FLAG
,否则使用子shell
执行命令还是会被沙箱杀死。同样的,使用openat
代替open
。 - 使用
readv/writev
代替read/write
- 使用
mmap2
代替mmap
- 还有一些特殊的系统调用,使用
sendfile
,代替read/write
。这类的系统调用需要平时多关注、收集和整理。
随便找一个seccomp-tools
解析的沙箱规则:
$ seccomp-tools dump ./pwn |
这个沙箱规则判断了当前触发系统调用的时候,arch
是否为x64
,如果不是64
就会kill
;然后,判断了sys-number
是否大于等于0x40000000
,如果大于,程序也会被kill
;然后设置了黑名单,分别是:execve/execveat/fork/clone/rt_sigreturn
。处于黑名单的系统调用会被kill
掉,其他系统调用则会放行。
切换指令模式
如果没有0001: 0x15 0x00 0x08 0xc000003e if (A != ARCH_X86_64) goto 0010
这一句的检查,那么可以使用retf(return far)
指令实现架构切换,或者在x64
环境下直接调用int 0x80
陷入到内核态。
retf
相当于pop ip; pop cs
,cs
是段寄存器,寄存器为0x23
时表示32
位运行模式,0x33
表示64
位运行模式。
从64
位切换到32
位的模板如下:
xor esp, esp |
使用**0x40000000+X
**系统调用
如果没有限制:0003: 0x35 0x06 0x00 0x40000000 if (A >= 0x40000000) goto 0010
的话,那么可以使用0x40000000 + X
来执行系统调用。
关于x32 ABI
可查看x32 ABI - Wikipedia。
比如要执行read
:
xor eax, eax |
需要注意的是,从5.16
开始,linux
内核不支持x32 abi
了:Bug #1994516 “Kernels after 5.16 cannot execute x32-ABI binaries…” : Bugs : linux package : Ubuntu (launchpad.net)