SunnyDayBPF 是一种基于 eBPF 的系统调用后用户缓冲区遥测欺骗研究技术,最初由 Azizcan Dastan 提出并研究。
该技术研究的是,用户态安全、日志或遥测代理所观察到的数据,是否可以在类似 read 的系统调用完成之后、但在代理解析、分析或将该数据转发到下游安全管道之前被篡改。
核心思想是:
事件仍然发生。
监控代理仍然读取数据。
但代理观察到的数据可能不再完全代表原始事件。
SunnyDayBPF 关注的是真实基准与观测遥测之间的差距。
SunnyDayBPF Hook Points
========================
Telemetry Agent Process +---------------------------------------------------------+ | | | read() pread64() recvfrom() | | | | | | +-----|----------------|------------------|---------------+ | | | ======|================|==================|======= KERNEL BOUNDARY | | | kprobe:ksys_read kprobe:_x64_sys kprobe:_sys (save buf ptr) pread64 recvfrom | (nested pt_regs) (save buf ptr) | (save buf ptr) | v v v [syscall executes — data enters user buffer] | | | kretprobe kretprobe kretprobe | | | +--------+-------+---------+--------+ | | read buffer into initialize BPF scratch space scan_state | v +------------------+ | TAIL CALL CHAIN | | | | scan_g0: SECURITY (4 rules, scan=177 bytes) | scan_g1: SECURITY (4 rules, scan=173 bytes) | scan_g2: SEVERITY (4 rules, scan=177 bytes) | scan_g3: SEVERITY (1 rule, scan=251 bytes) | scan_g4: PATH (4 rules, scan=132 bytes) | scan_g5: AUTH (4 rules, scan=190 bytes) | scan_g6: AUTH (1 rule, scan=249 bytes) | scan_g7: NETWORK (3 rules, scan=249 bytes) | scan_g8: PROCESS (4 rules, scan=173 bytes) | scan_g9: CUSTOM (2 rules, scan=243 bytes) | | | emit_event: | | perf event | | + stats | +------------------+ | v bpf_probe_write_user() (modify agent's buffer) | v read-back verification (confirm write succeeded) | v Agent continues with modified data
### 系统调用覆盖
| Syscall | 内核钩子 | 参数提取 | 覆盖范围 |
|---------|------------|----------------|----------|
| `read()` | `ksys_read` | `PT_REGS_PARM2`(直接) | 文件读取、管道、`/proc`、日志文件 |
| `pread64()` | `__x64_sys_pread64` | 通过 `bpf_probe_read_kernel` 读取嵌套的 `pt_regs`(偏移量 104/RSI) | 随机访问文件读取、journald |
| `recvfrom()` | `__sys_recvfrom` | `PT_REGS_PARM2`(直接) | 网络套接字、syslog 转发 |
### BPF 验证器约束
BPF 验证器强制每个程序最多 8,192 个条件分支的跳转序列限制。SunnyDayBPF 通过以下方式绕过此限制:
- **BPF 尾调用**(`BPF_PROG_ARRAY`):31 条规则分布在 10 个独立程序中,每个程序都有自己的验证器预算
- **不区分大小写优化**:`(d[i]|32)==lower` 将字母字符的每字节跳转次数从 2 减少到 1
- **动态扫描限制**:每个组的扫描窗口计算为 `min(BUF_SIZE - max_pat, 7800 / jumps_per_iter)`,以保持在验证器限制内
- **每 CPU 数组**:`BPF_PERCPU_ARRAY` 用于临时缓冲区和扫描状态,在尾调用的程序之间共享
---
## 概述
现代 Linux 安全系统通常依赖用户空间代理,从文件、套接字、管道、API、内核接口或事件流中收集遥测数据。
这些代理可以将遥测数据转发到:
- SIEM 平台
- EDR/XDR 后端
- 审计管道
- 日志收集器
- 运行时安全引擎
- 检测工程系统
- 可观测性平台
一个常见的假设是:```text
actual system behavior == collected telemetry == observed security data
SunnyDayBPF 挑战了这一假设。
该研究探索了一种系统调用后的欺骗模型,其中监控进程正常接收数据,但包含该数据的缓冲区会在进程消费该数据之前被修改。```text actual system behavior != observed telemetry
---
## 技术定义
SunnyDayBPF 是一种后系统调用遥测欺骗技术,研究对选定的遥测消费进程所属的用户空间缓冲区进行操纵。
从高层来看,该技术遵循以下模型:```text
sys_enter_*:
identify a target telemetry-consuming process
record the user-space buffer pointer involved in the read-like operation
sys_exit_*:
verify that the read-like operation completed successfully
inspect the returned user-space buffer
selectively alter telemetry-relevant content
verify write success via read-back
allow the target process to continue execution normally
这会导致以下内容之间的不匹配:```text what happened on the system
以及:
``````text
what the monitoring agent later observes, parses, and forwards
SunnyDayBPF 通过 5 字符的命令名称前缀匹配来识别目标进程。
| 代理 | 前缀 | 读取方式 | 有效? |
|---|---|---|---|
| Wazuh | wazuh | 对日志文件、syslog、审计日志执行 read() | 是 |
| OSSEC | ossec | 对日志文件执行 read() | 是 |
| Splunk UF | splun | 对受监控文件执行 read() | 是 |
| Elastic Agent | elast | 对日志源执行 read() | 是 |
| Datadog Agent | datad | 对日志和指标执行 read() | 是 |
| Cribl | cribl | 使用 read() 进行日志路由 | 是 |
| 代理 | 前缀 | 读取方式 | 有效? |
|---|---|---|---|
| rsyslog | rsysl | 对 syslog 执行 read() / recvfrom() | 是 |
| syslog-ng | syslo | 对 syslog 执行 read() / recvfrom() | 是 |
| Filebeat | fileb | 对日志文件执行 read() | 是 |
| Fluent-bit | fluen | 对输入执行 read() / recvfrom() | 是 |
| Fluentd | fluen | 对输入执行 read() / recvfrom() | 是 |
| Logstash | logst | 对管道执行 read() / recvfrom() | 是 |
| Promtail | promt | 对日志文件执行 read()(Loki) | 是 |
| Vector | vecto | 对日志源执行 read() | 是 |
| 代理 | 前缀 | 读取方式 | 有效? |
|---|---|---|---|
| Falco | falco | 通过 read() 从 perf 缓冲区收集 eBPF 事件 | 是 |
| osquery | osque | 对 /proc、日志文件、系统表执行 read() | 是 |
| 代理 | 前缀 | 读取方式 | 有效? |
|---|---|---|---|
| Snort | snort | 对数据包捕获执行 recvfrom() | 是 |
| Suricata | suric | 对数据包捕获执行 recvfrom() | 是 |
| Zeek | zeek_ | 对数据包捕获执行 recvfrom() | 是 |
| 代理 | 前缀 | 读取方式 | 有效? |
|---|---|---|---|
| auditd | audit | 对审计 netlink 套接字执行 read() | 是 |
| audisp | audisp | 对审计分发执行 read() | 是 |
| journalctl | journ | 对日志文件执行 read() / pread() | 是 |
| Telegraf | teleg | 对指标源执行 read() | 是 |
| collectd | colle | 对系统指标执行 read() | 是 |
| Metricbeat | metrc | 对系统指标执行 read() | 是 |
| Packetbeat | packe | 对网络执行 recvfrom() | 是 |
| Winlogbeat | winlo | 对事件日志执行 read() | 是 |
| Heartbeat | hbeat | 对运行状态检查执行 read() / recvfrom() | 是 |
Falco 使用 eBPF 探针捕获内核事件,但决策(规则匹配、告警)发生在用户空间。Falco 进程通过 read() 从 perf/ring 缓冲区读取事件。SunnyDayBPF 会在读取完成后、Falco 解析之前修改该缓冲区中的数据。```text
Kernel: Falco eBPF probe captures syscall event
|
v
perf buffer (kernel memory)
|
v
User: falco process calls read() on perf fd
|
v <-- SunnyDayBPF modifies buffer here
|
falco parses modified event
|
rule matching on altered data
|
no alert (or wrong alert)
### 哪些不易受攻击
| 工具 | 原因 | 说明 |
|------|-----|-------------|
| **Cilium Tetragon** | 内核空间执行 | 策略决策和终止/拒绝操作在 eBPF 程序内部完成,发生在数据到达用户空间之前 |
| **Tracee (Aqua)** | 内核空间检测 | 事件过滤及部分检测逻辑在内核 eBPF 程序中运行 |
| **内核审计模块** | 内核空间日志记录 | 审计记录在内核中生成;尽管 auditd 守护进程通过 `read()` 读取它们(该阶段存在漏洞) |
---
## 脱敏规则(31 条生效)
### 安全告警关键字(8 条规则)
| 模式 | 替换为 | 不区分大小写 | 效果 |
|---------|------------|-------------------|--------|
| `exploit` | `nominal` | 是 | 掩盖漏洞利用告警 |
| `malware` | `cleaner` | 是 | 掩盖恶意软件检测 |
| `backdoor` | `maindoor` | 是 | 掩盖后门引用 |
| `rootkit` | `toolkit` | 是 | 掩盖 rootkit 检测 |
| `trojan` | `module` | 是 | 掩盖木马告警 |
| `overflow` | `dataflow` | 是 | 掩盖缓冲区溢出事件 |
| `payload` | `dataset` | 是 | 掩盖载荷投递 |
| `shellcode` | `usercode ` | 是 | 掩盖 shellcode 执行 |
### 严重性降级(5 条规则)
| 模式 | 替换为 | 效果 |
|---------|------------|--------|
| `critical` | `debug ` | SIEM 看到的是 debug 而不是 critical |
| `emergency` | `debug ` | 紧急事件变为 debug |
| `alert` | `info ` | 告警级别变为信息级别 |
| `warning` | `notice ` | 警告降级为通知 |
| `error` | `debug` | 错误事件变为 debug |
### 敏感路径(4 条规则)