
SunnyDayBPF は、Azizcan Dastan によって提案・研究された、eBPF ベースの システムコール後のユーザバッファテレメトリー欺瞞 研究手法です。
この手法は、ユーザ空間のセキュリティ、ログ、またはテレメトリーエージェントによって観測されるデータが、読み取り系システムコールが完了した後、かつエージェントがそのデータを解析、分析、または下流のセキュリティパイプラインに転送する前に、改ざん可能かどうかを調査します。
核となる考え方は次のとおりです。
イベントは依然として発生する。
監視エージェントは依然としてデータを読み取る。
しかし、エージェントが観測するデータは、もはや元のイベントを完全には反映していない可能性がある。
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
### システムコールカバレッジ
| システムコール | カーネルフック | 引数抽出 | カバレッジ |
|---------|------------|----------------|----------|
| `read()` | `ksys_read` | `PT_REGS_PARM2` (direct) | ファイル読み取り、パイプ、`/proc`、ログファイル |
| `pread64()` | `__x64_sys_pread64` | `bpf_probe_read_kernel` 経由のネストされた `pt_regs` (オフセット104/RSI) | ランダムアクセスファイル読み取り、journald |
| `recvfrom()` | `__sys_recvfrom` | `PT_REGS_PARM2` (direct) | ネットワークソケット、syslog転送 |
### BPF検証器の制約
BPF検証器は、プログラムごとに8,192個の条件分岐というジャンプシーケンス制限を課します。SunnyDayBPFはこれに対応するために以下を使用します:
- **BPFテールコール** (`BPF_PROG_ARRAY`): 31のルールを10の独立したプログラムに分割し、それぞれが独自の検証器予算を持つ
- **大文字小文字を区別しない最適化**: `(d[i]|32)==lower` により、英字における1バイトあたりのジャンプ数が2から1に減少
- **動的スキャン制限**: 各グループのスキャンウィンドウは `min(BUF_SIZE - max_pat, 7800 / jumps_per_iter)` として計算され、検証器の制限内に収まるようにする
- **Per-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 | read() on log files, syslog, audit logs | Yes |
| OSSEC | ossec | read() on log files | Yes |
| Splunk UF | splun | read() on monitored files | Yes |
| Elastic Agent | elast | read() on log sources | Yes |
| Datadog Agent | datad | read() on logs and metrics | Yes |
| Cribl | cribl | read() for log routing | Yes |
| エージェント | プレフィックス | 読み取り方法 | 有効? |
|---|---|---|---|
| rsyslog | rsysl | read() / recvfrom() on syslog | Yes |
| syslog-ng | syslo | read() / recvfrom() on syslog | Yes |
| Filebeat | fileb | read() on log files | Yes |
| Fluent-bit | fluen | read() / recvfrom() on inputs | Yes |
| Fluentd | fluen | read() / recvfrom() on inputs | Yes |
| Logstash | logst | read() / recvfrom() on pipeline | Yes |
| Promtail | promt | read() on log files (Loki) | Yes |
| Vector | vecto | read() on log sources | Yes |
| エージェント | プレフィックス | 読み取り方法 | 有効? |
|---|---|---|---|
| Falco | falco | eBPF events collected via read() on perf buffer | Yes |
| osquery | osque | read() on /proc, log files, system tables | Yes |
| エージェント | プレフィックス | 読み取り方法 | 有効? |
|---|---|---|---|
| Snort | snort | recvfrom() on packet capture | Yes |
| Suricata | suric | recvfrom() on packet capture | Yes |
| Zeek | zeek_ | recvfrom() on packet capture | Yes |
| エージェント | プレフィックス | 読み取り方法 | 有効? |
|---|---|---|---|
| auditd | audit | read() on audit netlink socket | Yes |
| audisp | audisp | read() on audit dispatch | Yes |
| journalctl | journ | read() / pread() on journal files | Yes |
| Telegraf | teleg | read() on metric sources | Yes |
| collectd | colle | read() on system metrics | Yes |
| Metricbeat | metrc | read() on system metrics | Yes |
| Packetbeat | packe | recvfrom() on network | Yes |
| Winlogbeat | winlo | read() on event logs | Yes |
| Heartbeat | hbeat | read() / recvfrom() on uptime checks | Yes |
Falco は eBPF プローブを使用してカーネルイベントをキャプチャしますが、意思決定 (ルールマッチング、アラート発行) はユーザースペースで行われます。Falco プロセスは read() を介して perf/リングバッファからイベントを読み取ります。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)
### 脆弱ではないもの