Division-36/Z-Jail

GitHub: Division-36/Z-Jail

Z-Jail 是一个零依赖、约 130 KiB 的轻量级 Linux 多层沙箱,结合 namespace、seccomp-BPF 和审计日志,用于安全且可审计地隔离执行不可信原生代码。

Stars: 72 | Forks: 12

Z-Jail

Z-Jail

Multi-layer sandbox for native code execution on Linux.
Seven independent defence layers — no external dependencies, ~130 KiB PIE binary.

``` ┌──────────────────────────────────────────────────────┐ │ Z-Jail │ ├──────────────────────────────────────────────────────┤ │ Truthimatics PV (evidence-based verdict engine) │ │ Namespaces (mount, pid, net, ipc, uts) │ │ pivot_root (chroot on steroids) │ │ Capabilities (drop all, lock securebits) │ │ NO_NEW_PRIVS (no privilege escalation) │ │ seccomp-BPF (whitelist: 15 syscalls only) │ │ Audit (JSON logging + BLAKE2b hashing) │ └──────────────────────────────────────────────────────┘ ``` ## 目录 - [快速开始](#quick-start) - [为什么选择 Z-Jail](#why-z-jail) - [架构](#architecture) - [层级](#layers) - [用法](#usage) - [构建与安装](#build--install) - [测试](#testing) - [性能](#performance) - [威胁模型](#threat-model) - [文档](#documentation) - [路线图](#roadmap) - [许可证](#license) ## 快速开始 ``` git clone https://github.com/Division-36/Z-Jail.git cd Z-Jail make sudo ./z_jail --root=/path/to/rootfs --seccomp-enforce -- /bin/ls ``` `--root` 目录应包含一个带有目标二进制文件及其依赖项的最小文件系统(对于静态二进制文件,仅需该文件即可)。 ## 为什么选择 Z-Jail 现有的沙箱解决方案各有取舍: | | **Z-Jail** | **Firecracker** | **gVisor** | **bwrap** | **nsjail** | |--------------------|-------------|-----------------|------------|-----------|------------| | 外部依赖 | **零** | libc, seccomp | Go runtime | libc | libc, protobuf | | 二进制文件大小 | **~130 KiB**| 20+ MiB | 40+ MiB | ~70 KiB | ~1 MiB | | VM 隔离 | 否 | 是 (microVM) | 否 (沙箱)| 否 | 否 | | seccomp 白名单 | **是** | 否 | 是 | 可选 | 是 | | 内容哈希 | **是** | 否 | 否 | 否 | 否 | | 审计 JSON | **是** | 否 | 是 | 否 | 部分 | | 构建复杂度 | **一次 `make`** | 复杂 | 复杂 | 极简 | 中等 | Z-Jail 填补了 `bwrap`(极简,默认不带 seccomp)和 `nsjail`(功能丰富,依赖繁重)之间的空白。它专为 **CI pipeline、CTF jail 挑战和轻量级代码评估**而设计,让您无需引入 container runtime 即可实现纵深防御。 ## 架构 ### 数据流 ``` flowchart LR CLI[CLI args] --> P[parse_args] P --> C{clone namespaces} C -->|child| CR[child_run] C -->|parent| W[waitpid] CR --> RL[setrlimit] RL --> FD[close fds >= 3] FD --> DUMP[PR_SET_DUMPABLE=0] DUMP --> PV[pivot_root] PV --> NNP[PR_SET_NO_NEW_PRIVS] NNP --> CAP[drop capabilities] CAP --> SC[seccomp-BPF] SC --> SIG[signal parent] SIG --> EX[execve target] W --> A[audit JSON] A --> EXIT[exit] ``` ### 层级顺序 每一层的顺序都经过精心安排,使得前面的层无法被后面的层撤销: 1. **setrlimit** — 在执行任何操作前限制 CPU、地址空间、文件数量、进程数 2. **fd 清理** — 关闭所有继承的文件描述符 (fd),报告管道除外 3. **PR_SET_DUMPABLE=0** — 禁用核心转储,锁定 /proc/self/mem 4. **pivot_root** — 脱离主机文件系统;旧根目录以懒加载方式卸载 5. **PR_SET_NO_NEW_PRIVS** — 此后不再有 setuid,也无法通过 `capset` 提权 6. **drop_caps** — 清除所有 capabilities,锁定 securebits 7. **seccomp-BPF** — 将系统调用限制为仅限白名单 8. **signal parent** — 告诉父进程沙箱已就绪 9. **execve** — 用目标二进制文件替换当前进程 ``` sequenceDiagram participant P as Parent participant C as Child P->>C: clone (NEWNS|NEWPID|NEWNET|NEWIPC|NEWUTS) Note over C: setrlimit(CPU, AS, NOFILE, NPROC) Note over C: close(all fds > 2) Note over C: PR_SET_DUMPABLE=0 Note over C: pivot_root → chdir("/") → umount -l Note over C: PR_SET_NO_NEW_PRIVS Note over C: capset(all zero) + securebits Note over C: seccomp(SECCOMP_MODE_FILTER, whitelist) C->>P: write(pipe, ready=1) Note over C: execve(target) P->>P: waitpid P->>P: write audit JSON ``` ## 层级 ### Truthimatics 公开版本 基于证据的判定引擎。收集有关已执行二进制文件的加权观察结果,并确定最终判定(`DETERMINISTIC`、`REJECT` 或 `UNCERTAIN`)。每个观察结果都有一个权重;任何单个权重超过总权重 50% 的观察结果将直接决定最终判定。 ### Namespaces 通过 `clone()` 创建五个 namespace: | Namespace | Flag | 用途 | |-----------|------|---------| | Mount | `CLONE_NEWNS` | 隔离的文件系统树 | | PID | `CLONE_NEWPID` | 进程 ID 空间(子进程为 pid 1) | | Net | `CLONE_NEWNET` | 无网络接口 | | IPC | `CLONE_NEWIPC` | 无共享内存 / 信号量 | | UTS | `CLONE_NEWUTS` | 独立的 hostname | 初始 namespace 中需要 `CAP_SYS_ADMIN` 权限。 ### pivot_root 使用 `--root` 目录替换 mount namespace 的根目录: 1. 将根目录绑定挂载到自身(`MS_BIND|MS_REC`) 2. `pivot_root(new_root, put_old)` — 交换挂载树 3. `chdir("/")` — 进入新根目录 4. `umount2("/.pivot_old", MNT_DETACH)` — 分离旧根目录 5. `rmdir("/.pivot_old")` — 清理 这比 `chroot(2)` 要严格得多 — 沙箱进程绝无可能逃回主机根目录,即使在沙箱内部使用 `CLONE_NEWNS` 也不行(这已经被 seccomp 阻止了)。 ### Capabilities 所有 capabilities 都将通过以下方式丢弃: ``` capset(hdr, data) // data = {0, 0, 0} prctl(SECBIT_KEEP_CAPS_LOCKED | SECBIT_NO_SETUID_FIXUP | ...) ``` 进程在 `capset` *之前*丢弃 `setuid`/`setgid`,以便在仍持有 `CAP_SETUID` 时使 uid 变更生效。执行 `capset` 后,所有 caps 都将消失且 securebits 被锁定 — 无法再次启用。 ### NO_NEW_PRIVS ``` prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); ``` 防止进程或其子进程通过 `setuid` 二进制文件、文件 capabilities 或 `LSM` 转换获取新特权。此操作不可逆。 ### seccomp-BPF (whitelist-v1) 包含 15 个系统调用的允许列表 — 任何不在此列表上的系统调用都会触发 `SECCOMP_RET_KILL`: | Syscall | 编号 | 备注 | |---------|--------|-------| | `read` | 0 | stdin | | `write` | 1 | stdout/stderr + 报告管道 | | `openat` | 257 | 文件访问(非 `open`) | | `close` | 3 | — | | `lseek` | 8 | — | | `brk` | 12 | 堆内存管理 | | `mmap` | 9 | 参数受限:`flags & 4 == 0` (无 MAP_SHARED), `flags == 0x22` (MAP_PRIVATE\|MAP_ANONYMOUS) | | `munmap` | 11 | — | | `execve` | 59 | 启动时的单次执行 | | `exit_group` | 231 | 进程正常退出 | | `rt_sigaction` | 13 | 信号处理程序 | | `rt_sigprocmask` | 14 | 信号掩码 | | `getrandom` | 318 | 随机数源 | | `clock_gettime` | 228 | 计时 | | `fstat` | 5 | 文件元数据 | BPF 过滤器是动态生成的:对于每个白名单条目,都会生成一个跳转链,允许(如果系统调用匹配)或落入 KILL。首先会检查架构(`AUDIT_ARCH_X86_64`)。 该过滤器由一个独立测试(`tests/seccomp_filter_test.c`,8/8 通过)进行独立验证,该测试针对真实的 `prctl(PR_SET_SECCOMP)` 执行测试用例的 fork+execves,无需 root 权限。 ### 7. 审计 每次执行都会生成一条 JSON 审计记录: ``` { "schema": "z-jail.audit/v1", "build_id": "Z-Jail/v1+dev", "timestamp": 1749000000, "duration_ns": 8500000, "executable": "/bin/ls", "verdict": "DETERMINISTIC", "exit_code": 0, "sandbox": { "seccomp_filter": "whitelist-v1", "seccomp_whitelist_size": 15, "seccomp_arg_rules_size": 2, "namespaces": ["mount","pid","net","ipc","uts"], "pivot_root": "/var/run/z-jail/roots/default", "no_new_privs": true, "capabilities_dropped": true }, "content_fingerprint": "0e5751c026e543b2e8ab2eb06099daa1..." } ``` 写入 `build/audits/.audit.json`。`content_fingerprint` 是目标二进制文件的 BLAKE2b-256 哈希值,由父进程在子进程结束后计算得出。 ## 用法 ``` z_jail --root= [--seccomp-enforce] [--self-hash=] [--quiet] [--verbose] -- [args...] ``` | Flag | 描述 | |------|-------------| | `--root=` | 沙箱根目录(必填) | | `--seccomp-enforce` | 启用 seccomp-BPF 系统调用白名单 | | `--self-hash=` | 验证二进制文件是否与预期的 BLAKE2b-256 哈希匹配 | | `--quiet` | 抑制审计输出 | | `--verbose` | 启用 debug 日志 | | `--version` | 显示构建 ID(`Z-Jail/v1+dev`) | | `--help` | 显示用法并退出 | ### 示例 ``` # 运行具有所有保护的 static binary sudo z_jail --root=./roots --seccomp-enforce -- bin/hello_static # 运行具有 binary 完整性验证 sudo z_jail --root=./roots --seccomp-enforce \ --self-hash=$(sha256sum z_jail | cut -c1-64) -- bin/program # 安静模式(无 audit JSON) sudo z_jail --root=./roots --quiet -- bin/program ``` ### 退出码 | 代码 | 含义 | |------|---------| | 0 | 子进程正常退出(判定:DETERMINISTIC) | | 1 | 子进程被信号杀死(判定:REJECT) | | 2 | Self-hash:十六进制字符串错误或文件不可读 | | 3 | Self-hash:不匹配(二进制文件已被篡改) | | 101 | 子进程设置错误(rlimit 等) | | 102 | 子进程 seccomp 过滤器安装失败 | | 103 | 子进程 execve 失败(找不到二进制文件,无执行权限) | | 104 | 子进程 pivot_root 失败 | | 105 | 子进程 capability 丢弃失败 | | 125 | Namespace 创建失败(是否以 root 运行?内核是否支持?) | ## 构建与安装 ### 要求 - Linux kernel ≥ 5.4(namespaces、seccomp-BPF、pivot_root) - GCC ≥ 11(已在 11.4、13.2、15.2 上测试) - 无需外部库 — 仅需标准 C 工具链 ### 命令 ``` make # build z_jail (~130 KiB PIE binary) make install # install to /usr/local/bin + man page make clean # remove build artifacts make dist # create release tarball make check # smoke test (--version + --help) ``` 该二进制文件被构建为位置无关可执行文件,启用了 `-fstack-protector-strong`、`-D_FORTIFY_SOURCE=2`、完全 RELRO 以及 `-z now`。 ### 编译时选项 ``` make CC=clang CFLAGS="-O3 -march=native" # custom compiler/flags ``` ## 测试 ### 快速测试(无需 root) ``` # seccomp 过滤逻辑(8 项测试) tests/build/seccomp_filter_test # BLAKE2b 已知答案测试 tests/build/blake2b_known ``` 这些测试不需要 root,运行时间在 100 毫秒以内。 ### 完整测试套件 ``` make -C tests setup # build payloads + test roots sudo bash tests/run_tests.sh # 17 scenarios ``` 需要 root 权限来创建 namespace。测试套件涵盖: | # | 场景 | 类型 | 测试内容 | |---|----------|------|---------------| | 0 | blake2b_regress | 已知答案 | BLAKE2b 实现的正确性 | | 1 | seccomp_filter | 独立 BPF | 8 个针对 BPF 过滤器逻辑的子测试 | | 2 | hello_static | ok | 基本静态二进制文件执行 | | 3 | hello_dynamic | ok | 带有 ld-linux + libc 的动态二进制文件 | | 4 | execve_replacement | ok | 沙箱中的 execve(被 seccomp 阻止) | | 5 | fd_inherited_read | ok | stdin/stdout 正确继承 | | 6 | mmap_bad_flags | killed | 阻止带有 MAP_SHARED 的 mmap | | 7 | mmap_good_allowed | ok | 允许带有 MAP_PRIVATE\|ANONYMOUS 的 mmap | | 8 | mmap_prot_exec | killed | 阻止带有 PROT_EXEC 的 mmap | | 9 | mmap_self_modify | killed | 阻止自修改代码 | | 10 | ptrace | killed | 阻止 ptrace | | 11 | socket | killed | 阻止 socket 创建 | | 12 | chroot_escape | killed | 阻止 chroot 系统调用 | | 13 | double_chroot | killed | 阻止双重 chroot | | 14 | mount_replay | killed | 阻止 Mount 系统调用 | | 15 | cpu_exhaust | killed | RLIMIT_NPROC 阻止 fork 炸弹 | | 16 | signal_parent | killed | 阻止向父进程发送信号 | | 17 | self_hash | ok | 二进制完整性验证 | ## 性能 数据来自 WSL2(Kali Linux,GCC 15.2.0,-O2 -g): | 指标 | 数值 | |--------|-------| | 二进制文件大小 | ~130 KiB | | 平均沙箱延迟 | ~8 ms | | 峰值 RSS | ~4 MiB | | 代码行数(核心) | ~900 | | 测试套件运行时间 | ~5 s | 延迟细分(约):clone + namespaces ~3 ms,pivot_root ~2 ms,seccomp + caps ~1 ms,execve ~1 ms,waitpid + 审计 ~1 ms。 ## 威胁模型 ### 覆盖范围 - 不可信 payload 执行任意本机代码 - 通过 `chroot`、`mount`、`ptrace`、`socket`、`process_vm_writev` 逃逸 - Fork 炸弹、CPU 耗尽(`RLIMIT_CPU`)、内存耗尽(`RLIMIT_AS`) - 跨 `execve` 的文件描述符泄漏- `setuid` / 动态链接器 / `LD_PRELOAD` 提权 - 移除 seccomp 过滤器或重新启用 capability ### 不覆盖范围 - 允许的系统调用面之外的内核 0-day 漏洞 - 硬件侧信道攻击(Spectre、Meltdown) - 通过共享 `/proc`、`/sys` 挂载进行的同置 VM 逃逸 - 超出 `CLONE_NEWNET` + 阻止 `socket` 所提供范围的网络出口 - 同级沙箱的资源耗尽(需要 cgroup 支持) ### 假设前提 - 主机 kernel 是未修改的 Linux ≥ 5.4 - `clone(CLONE_NEWNS|CLONE_NEWPID|...)` 成功(需要 `CAP_SYS_ADMIN`) - 目标二进制文件是静态链接的(或者 `--root` 中提供了动态库) - 生产部署环境中已配置 `--self-hash=` ## 文档 | 文件 | 描述 | |------|-------------| | `README.md` | 本文件 | | `docs/ARCHITECTURE.md` | 架构概述 | | `docs/SANDBOX.md` | 逐层沙箱内部机制 | | `docs/SECCOMP.md` | seccomp-BPF 白名单设计 | | `docs/AUDIT_SCHEMA.md` | 审计 JSON schema 参考 | | `docs/THREAT_MODEL.md` | 安全假设与范围 | | `docs/BLAKE2B.md` | BLAKE2b 实现细节 | | `docs/BENCHMARKS.md` | 性能基准测试 | | `docs/BUILD.md` | 构建说明 | | `docs/adr/` | 架构决策记录 (ADR)(4 份文档) | | `man/z_jail.1` | Man 手册页 | | `SECURITY.md` | 安全策略与报告 | | `CONTRIBUTING.md` | 如何贡献 | | `CHANGELOG.md` | 发布历史 | | `ROADMAP.md` | 未来计划 | | `TODO.md` | 已知差距与计划工作 | ## 路线图 ### v1(当前) - 7 层纵深防御沙箱 - BLAKE2b-256 内容指纹识别 - 审计 JSON 输出 - 17 个测试场景 - man 手册页,命令补全(bash、zsh、fish) ### v2(计划中) - 外部 seccomp 策略文件(JSON 或 BPF 源码) - 每个沙箱实例的自定义 namespace 标志 - 通过 CLI 配置系统调用白名单 - 用于 CI 集成的性能分析 hook - 发布签名(minisign/signify) ## 状态 [![build](https://static.pigsec.cn/wp-content/uploads/repos/cas/05/052bc9eaea1744e860f382f618f1146be38e27dfd499bb29c253bcd1827fad60.svg)](https://github.com/Division-36/Z-Jail/actions/workflows/build.yml) [![coverage](https://static.pigsec.cn/wp-content/uploads/repos/cas/20/205aa58460681c9eb52bfdd9cd19c4561a7df19586b17fe6f2890c45586d02b3.svg)](https://github.com/Division-36/Z-Jail/actions/workflows/coverage.yml) ## 许可证 **MIT** — 完整文本请参见 `LICENSE`。 *Z-Jail 构建于 WSL2(Kali Linux,GCC 15.2.0),目标平台为 Linux 5.4+。* *由 [Division-36](https://github.com/Division-36) 维护。请在[问题跟踪器](https://github.com/Division-36/Z-Jail/issues)报告问题。*
标签:子域名枚举, 客户端加密, 沙箱, 系统安全, 隔离执行