从seccomp沙箱规则编写到CTF应用

admin 2023年2月21日16:55:23评论103 views字数 6557阅读21分51秒阅读模式
从seccomp沙箱规则编写到CTF应用
1. 基于libseccomp函数库

基于 prctl 函数自定义沙盒规则(系统调用过滤规则)时,需要掌握 BPF 指令,有一定复杂性。
libseccomp 库为 Linux 内核的系统调用过滤机制提供了一个易于使用、独立于平台的接口。libseccomp API 旨在抽象出基于 BPF 的底层系统调用过滤器语言,并提供更传统的基于函数调用的过滤接口,应用程序开发人员应该熟悉并易于采用。
  • 依赖环境

sudo apt install libseccomp-dev libseccomp2 seccomp
  • 关键函数使用

  1. 声明过滤器变量
#include <seccomp.h>
scmp_filter_ctx ctx;
  1. 初始化过滤器 过滤器初始函数原型:
#include <seccomp.h>
seccomp_init(uint32_t def_action)

def_action 是过滤器工作模式,比较常用模式:^ee067a

  • SCMP_ACT_ALLOW:黑名单模式,初始化后全部系统调用都允许调用
  • SCMP_ACT_KILL:白名单模式,初始化后全部系统调用都不允许调用
  1. 添加沙箱规则

man 文档中给出了几个添加规则的函数,一直用 seccomp_rule_add() 添加规则,其他函数未深入研究。seccomp_rule_add() 函数原型:
int seccomp_rule_add(scmp_filter_ctx " ctx "uint32_t " action ",int " syscall ",unsigned int " arg_cnt "" ... ");

    只要匹配系统调用,不管参数内容:arg_cnt=0

    seccomp_rule_add(ctx,SCMP_ACT_KILL,SCMP_SYS(execve),0);

    匹配系统调用后,根据参数内容决定是否执行 action :arg_cnt=N(规则条数)

    seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(write), 1,
             SCMP_A2(SCMP_CMP_GT,0x10));
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 3,
        SCMP_A0(SCMP_CMP_EQ, fd),
        SCMP_A1(SCMP_CMP_EQ, (scmp_datum_t)buf),
        SCMP_A2(SCMP_CMP_LE, BUF_SIZE));
    • ctx :初始化定义的过滤器

    • action :规则模式,匹配到系统调用后执行的操作,同初始化过滤器的 def_action

    • syscall :系统调用号,可以使用 SCMP_SYS(函数名) 代替具体调用号

    • arg_cnt :需要参数限制规则个数 seccomp_rule_add 限制参数接口定义在 seccomp.h.in 。主要是取参数 SCMP_AX ,运算比较 scmp_compare

  1. 加载沙箱规则

#include <seccomp.h>
seccomp_load(ctx);
  • GCC编译参数

由于使用了外部库 libseccomp ,编译时需要链接外部库:

gcc -lseccomp -o sandbox sandbox.c
  • Demo

1. 禁用一个或多个系统调用

/*
command     :   gcc seccomp_sandbox_demo.c -lseccomp -o seccomp_sandbox_demo
                ./seccomp_sandbox_demo
filename    :   seccomp_sandbox_demo.c
describe : 禁止调用 execve()、write()
*/

#include <unistd.h>
//#include <linux/seccomp.h>
#include <seccomp.h>

int main(){
    scmp_filter_ctx ctx;    //create filter
    ctx = seccomp_init(SCMP_ACT_ALLOW); //init filter
    
    seccomp_rule_add(ctx,SCMP_ACT_KILL,SCMP_SYS(execve),0); //add roles into filter
    seccomp_rule_add(ctx,SCMP_ACT_KILL,SCMP_SYS(write),0); //add roles into filter

    seccomp_load(ctx);  //load filter

    char *binsh = "/bin/sh";
    syscall(59,binsh,0,0);
    return 0;
}

从seccomp沙箱规则编写到CTF应用

2.有参数条件禁用一个或多个系统调用

seccomp_rule_add 限制参数接口在 seccomp.h.in ,主要是取参数 SCMP_AX ,运算比较 scmp_compare
/*
command     :   gcc seccomp_sandbox_demo.c -lseccomp -o seccomp_sandbox_demo
                ./seccomp_sandbox_demo
filename    :   seccomp_sandbox_demo.c
describe : 有参数条件禁用write()、open()
*/

#include <unistd.h>
//#include <linux/seccomp.h>
#include <seccomp.h>

int main(){
    scmp_filter_ctx ctx;    //create filter
    ctx = seccomp_init(SCMP_ACT_ALLOW); //init filter
    
    /*write文件描述符不能小于等于0x10*/
    seccomp_rule_add(ctx,SCMP_ACT_KILL,SCMP_SYS(write),1,SCMP_A0(SCMP_CMP_LE,0x10)); //add roles into filter
    /*read文件描述符不能小于等于0x10,长度不能大于0x8*/
    seccomp_rule_add(ctx,SCMP_ACT_KILL,SCMP_SYS(read),2,
                    SCMP_A0(SCMP_CMP_LE,0x10),
                    SCMP_A2(SCMP_CMP_GT,0x8)); //add roles into filter

    seccomp_load(ctx);  //load filter

    char *binsh = "/bin/sh";
    syscall(59,binsh,0,0);
    return 0;
}

从seccomp沙箱规则编写到CTF应用

从seccomp沙箱规则编写到CTF应用
2. 基于prctl函数 

prctl 是系统内置实现的 seccomp 配置函数,诞生比 libseccomp 早,但是由于配置复杂,个人很少用。就算一定要用 prctl ,可以用 libseccomp 导出 prtcl 过滤器配置,导入 prctl 运行。
之所以学习 prctl 函数主要是为了注入汇编代码开启沙盒,是因为 prctl 可以通过 syscall 系统调用,libseccomp 通过外部引入没得。
prctl() 是系统内置函数,相关文档可以在 linux kernel 维护的 man7 查看,函数原型:
#include <sys/prctl.h>
int prctl(int option, unsigned long arg2, unsigned long arg3,
                 unsigned long arg4, unsigned long arg5)
;
  • 关键函数使用

prctl 配置规则主要看 option ,下面记录最常用的(高级配置个人感觉用 libseccomp 方便快捷):

  • 使沙箱规则在所有用户、所有子进程都能生效

    #include <sys/prctl.h>
    prctl(PR_SET_NO_NEW_PRIVS,1,0,0,0);
  • 沙箱严格模式,即只允许调用 read、write、_exit(not exit_group)、sigreturn

    #include <sys/prctl.h>
    prctl(PR_SET_SECCOMP,SECCOMP_MODE_STRICT);
  • 加载 BPF 编写的沙箱规则

    #include <sys/prctl.h>
    #include <linux/filter.h>

    struct sock_filter sfi[] = {
    {0x20,0x00,0x00,0x00000004},
    {0x15,0x00,0x05,0xc000003e},
    {0x20,0x00,0x00,0x00000000},
    {0x35,0x00,0x01,0x40000000},
    {0x15,0x00,0x02,0xffffffff},
    {0x15,0x01,0x00,0x0000003b},
    {0x06,0x00,0x00,0x7fff0000},
    {0x06,0x00,0x00,0x00000000}
    };
    struct sock_fprog sfp = {8,sfi};

    prctl(PR_SET_SECCOMP,SECCOMP_MODE_FILTER,&sfp);
  • BPF 自定义高级规则

使用 BPF 编写规则也能让 prctl 过滤特定系统调用、参数。之所以不用 BPF 写,其一是 BPF 的宏定义确实没掌握,其二是可以用 libseccomp 导出 BPF 规则给 prctl 使用。

  • 使用 libseccomp 生成 BPF 规则

1.使用场景

  1. 需要使用汇编进行 syscall 调用来启动沙盒,如植入 PWN 沙盒通防等。
  2. 单纯想用 prctl

2. 生成脚本

/*
command     :   gcc seccomp_sandbox_demo.c -lseccomp -o seccomp_sandbox_demo
                ./seccomp_sandbox_demo
filename    :   seccomp_sandbox_demo.c
describe : 生成并导出沙箱规则
Copyright (C) 2021 skye231 <[email protected]>
*/

#include <unistd.h>
#include <seccomp.h>
#include <stdio.h>
#include <linux/seccomp.h>
#include <fcntl.h>

int main(){
 /* 创建、初始化、添加规则 */
    scmp_filter_ctx ctx;    //create filter
    ctx = seccomp_init(SCMP_ACT_ALLOW); //init filter
    seccomp_rule_add(ctx,SCMP_ACT_KILL,SCMP_SYS(execve),0); //add roles into filter
    
    /* 输出沙箱结构体 */
    int fd;
    fd = open("./bpf.out",O_WRONLY|O_CREAT);
    seccomp_export_bpf(ctx,fd);
    close(fd);

    /* 验证,可删除 */
    seccomp_load(ctx);  //load filter
    char *binsh = "/bin/sh";
    // system(binsh);
    syscall(59,binsh,0,0);
    
    return 0;
}

在脚本用 libseccomp 添加自己需要的规则,编译后运行程序,在当前目录下会生成 BPF 规则字节码:

从seccomp沙箱规则编写到CTF应用

稍加处理,转换成结构体:

从seccomp沙箱规则编写到CTF应用

最后套入加载过滤器的 prtcl 调用模版:

#include <unistd.h>
#include <seccomp.h>
#include <stdio.h>
#include <linux/seccomp.h>
#include <fcntl.h>
#include <sys/prctl.h>
#include <linux/filter.h>

struct sock_filter sfi[] = {
    {0x20,0x00,0x00,0x00000004},
    {0x15,0x00,0x05,0xc000003e},
    {0x20,0x00,0x00,0x00000000},
    {0x35,0x00,0x01,0x40000000},
    {0x15,0x00,0x02,0xffffffff},
    {0x15,0x01,0x00,0x0000003b},
    {0x06,0x00,0x00,0x7fff0000},
    {0x06,0x00,0x00,0x00000000}
};
struct sock_fprog sfp = {8,sfi};//{BPF规则条数,BPF结构体}

int main(){
    prctl(PR_SET_NO_NEW_PRIVS,1,0,0,0);
    prctl(PR_SET_SECCOMP,SECCOMP_MODE_FILTER,&sfp);
    
    ……
    
    return 0;
}
  • Demo

1.所有进程沙箱严格模式

/*
command     :   gcc seccomp_sandbox_demo.c -o seccomp_sandbox_demo
                ./seccomp_sandbox_demo
filename    :   seccomp_sandbox_demo.c
*/

#include <unistd.h>
// #include <seccomp.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>

int main(){
    char *binsh = "/bin/sh";
    prctl(PR_SET_NO_NEW_PRIVS,1,0,0,0);
    prctl(PR_SET_SECCOMP,SECCOMP_MODE_STRICT);
    syscall(59,binsh,0,0);
    return 0;
}

2.所有进程使用 BPF 沙箱规则

查看前文:使用 libseccomp 生成 BPF 规则

从seccomp沙箱规则编写到CTF应用
3. CTF出现过的沙盒形式

从一开始的禁用 execve、system 到同功能函数替代,再到参数限制,花样越来越多。s0xzordIn 写的文章总结有对应类型例题,可以看一看。

  1. 禁用 execve、system:orw 读取 flag
  2. 禁用 open、read、write 其中一个或多个函数:
    • open -> openat
    • read -> readv
    • write -> writev
  3. 限制 orw 函数参数:
    • 限制 fd 下限 -> 多 open 几次
    • 限制 fd 上限 -> close 原有 fd ,再 open
  4. 限制 sys_number 都不能调用:retfq 切换 32 位 shellcode 绕开 64 位限制
  • 关于 Ida 反编译

无论是 libseccomp 还是 prctl 编写规则的时候,使用的是易于理解宏定义而不是数字。IDA 反编译没有相关宏定义,显示的自然是宏定义对应数字。

从seccomp沙箱规则编写到CTF应用
4. 参考文章

  • https://blog.csdn.net/Necrolic/article/details/106009382
  • https://github.com/seccomp/libseccomp
  • https://xz.aliyun.com/t/11480
       

原文始发于微信公众号(山石网科安全技术研究院):从seccomp沙箱规则编写到CTF应用

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年2月21日16:55:23
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   从seccomp沙箱规则编写到CTF应用http://cn-sec.com/archives/1263914.html

发表评论

匿名网友 填写信息