核心功能是将内核态与用户态做严格隔离,安全计算模式–seccomp,主要用于限制用户进程能够使用的系统调用;
模式
1 | SECCOMP_SET_MODE_STRICT |
它是 Linux 内核安全计算模块 (seccomp) 中的一个操作指令,用于将调用线程的系统调用权限限制到极致。启用此模式后,线程只被允许调用 read、write、_exit 和 sigreturn 这四个系统调用,任何其他系统调用都会导致线程被 SIGKILL 信号立即终止;
1 | SECCOMP_SET_MODE_FILTER |
filter模式的核心是允许开发者使用 BPF(Berkeley Packet Filter) 程序来定义哪些系统调用可以被执行、以何种方式被执行,或是直接被拒绝;
BPF
BPF有一套伪汇编,用于编写过滤规则,定长指令,总共64位,如下结构所示:
1 | struct sock_filter { |
code在BPF.h中拥有宏定义:linux/include/uapi/linux/bpf.h at master · torvalds/linux · GitHub
编写好的filter结构以数组方式挨着排放,是一个队列,可以继续添加,类似访问控制列表。然后用以下结构打包:
1 | struct sock_fprog { |
一般使用下面两个最常用的宏来进行BPF的编写:
1 | BPF_STMT(code, k) //用于操作BPF的寄存器 |
有个头文件能够便捷快速的去设置BPF规则,用这个网站:outflux.net/teach-seccomp/step-3/seccomp-bpf.h
里面定义了一些BPF宏,快速地去满足一些常见的PWN的要求:
1 |
使用上面的4个宏定义可以快速地去设置白名单的系统调用规则的过滤,具体过滤如下:
1 | struct sock_filter filter[] = { |
这样一来就设置好了只允许该进程使用read和write的系统调用;
prctl激活
设置模式使用到系统调用prctl:
1 | prctl(PR_SET_NO_NEW_PRIVS,1,0,0,0); //使seccomp对所有用户(包括root)生效 |
绕过
限制了system以后就无法提权,大部分时候(白名单)是通过shellcode或者ROP来写一个ORW,即open,openv,read,readv,write,writev;
如果是黑名单过滤,那么可以找功能相似的其他调用号;
设置了seccomp的程序可以通过工具seccomp-tools来获取过滤规则:
1 | seccomp-tools dump ./process |
未检查架构
同样也是黑名单;
32位和64位的系统调用号不同,如果没有检查架构,那么就可以钻空子;
可以利用retq指令修改cs寄存器的值,cs=0x23表示32位,cs=0x33表示64位:
1 | mov DWORD [rsp+4], 0x23 |
比如在64位下禁用了59号,也就是execve,但在32位下是另一个数字;
未检查大小
同样也是黑名单;
linux有个机制叫做x32-abi模式;有个掩码是__X32_SYSCALL_BIT,默认为0x40000000;
当系统调用号或上这个掩码的时候,意思是使用x32-abi模式来进行系统调用;
当禁用59号,那么或上掩码,没有检查调用号大小,就能正常执行系统调用,从而绕过;