Sensor の仕組み
URSUS が Linux システムからイベントを収集する技術的アプローチの詳細
process Process Collector
netlink (cn_proc) でプロセス起動をカーネルから push 通知で受け取る
目的
URSUS が観測する 4 つのイベントタイプの中で最も重要なのが プロセス起動です。攻撃者の行動はほぼ必ず「何かのプロセスを起動する」という 形で表面化します。シェル、payload バイナリ、永続化のための cron タスク、 横展開の psexec — 全てプロセス起動として観測できます。
採用方式: cn_proc netlink
プロセス起動を観測する Linux 上の選択肢は複数あります。URSUS は kernel から直接 push 通知を受ける方式を採用しています。
| 方式 | 取りこぼし | 必要権限 | 備考 |
|---|---|---|---|
| cn_proc netlink (採用) | なし | CAP_NET_ADMIN | カーネルが exec(2) のたびに通知。リアルタイム |
| psutil ポーリング | あり (poll 間隔より短命) | 不要 | DESIGN.md の初版方針。実装簡単だが取りこぼし大 |
| auditd | なし | root | execve syscall を audit ルールで購読。出力フォーマットが煩雑 |
| eBPF (kprobe) | なし | CAP_BPF | 商用 EDR の主流実装。bcc / libbpf-CO-RE への依存が必要 |
cn_proc とは
cn_proc (process events connector) は、Linux カーネルが
connector
インタフェースを介して送出するマルチキャストイベントです。netlink ソケットの
NETLINK_CONNECTOR プロトコルファミリーで購読します。
カーネルは以下のイベントを通知します:
PROC_EVENT_FORK: fork(2) 完了時PROC_EVENT_EXEC: execve(2) 成功時PROC_EVENT_EXIT: プロセス終了時PROC_EVENT_UID,PROC_EVENT_GID: 権限変更時- その他 (sid, ptrace 等)
URSUS は EXEC のみを購読しています。検知に必要な情報 (プロセス名・コマンドライン・実行ファイル・親プロセス)は exec の タイミングで揃うためです。
bytes 0 ─┐
│ ┌──────────────────────────────┐
nlmsghdr │ │ len, type, flags, seq, pid │ 16 bytes
│ └──────────────────────────────┘
bytes 16 ─┤
│ ┌──────────────────────────────┐
cn_msg │ │ idx, val, seq, ack, len, fl │ 20 bytes
│ └──────────────────────────────┘
bytes 36 ─┤
│ ┌──────────────────────────────┐
evt_hdr │ │ what, cpu, timestamp_ns │ 16 bytes
│ └──────────────────────────────┘
bytes 52 ─┤
│ ┌──────────────────────────────┐
body (exec)│ │ pid, tgid │ 8 bytes
│ └──────────────────────────────┘
bytes 60 ─┘
PROC_EVENT_EXEC を含む netlink メッセージのレイアウト
出力されるイベント
非正規化カラム
| カラム | 値の例 | 用途 |
|---|---|---|
pid | 4567 | 同 PID で前後コンテキストを引く |
ppid | 1000 | プロセスツリー再構築 |
user | www-data | 権限昇格検知 |
process_name | bash | R001 (web→shell) 等で照合 |
parent_process_name | nginx | 親プロセス基準のルール |
cmdline | bash -c curl ... | sh | regex 検知 |
exe_path | /usr/bin/bash | /tmp 配下からの実行検知 (R010) |
制限と既知の挙動
- fork のみのイベントは取れない: EXEC を購読しているため。
- extreme short-lived プロセス: netlink 通知後、/proc 情報を読み取る前に終了したプロセスは詳細が取れない。
- 権限:
CAP_NET_ADMINが必要。
file File Collector
inotify (watchdog) によるファイル変更のリアルタイム検知
目的
攻撃者の永続化やデータ改竄は、最終的に「ファイル書き込み」として表面化します。
/etc/passwd の書き込み、SSH 鍵の改変、cron タスクの投入などを監視します。
採用方式: inotify (watchdog 経由)
| 方式 | PID/Process 取得 | 必要権限 | 備考 |
|---|---|---|---|
| inotify (採用) | ✗ | 不要 | カーネルの fs 通知。watchdog ライブラリで Python から扱う |
| fanotify | ◎ (PID/exe path) | CAP_SYS_ADMIN | 5.1+ で FAN_REPORT_FID |
| auditd file watch | ◎ | root | auditctl -w /etc/passwd -p wa |
URSUS の現在の実装では、ファイルが改変されたことは分かりますが 「どのプロセスが書き込んだか」は分かりません。
除外パターンと自己フィードバック防止
URSUS 自体の DB 更新による無限ループを防ぐため、DB ディレクトリは無条件で除外されます。 また、glob パターンによる除外設定も可能です。
出力されるイベント
非正規化カラム
| カラム | 例 |
|---|---|
file_path | /etc/passwd |
file_op | create / modify / delete / move |
network Network Collector
psutil による TCP 接続テーブル差分ポーリング
目的
新規 LISTEN(リバースシェル待ち受け)や新規 ESTABLISHED(C2 通信、データ送信)を観測します。
採用方式: psutil 差分ポーリング
network センサはポーリング方式です。2 秒間隔(既定)で /proc/net/tcp を読み取り、前回のスナップショットとの差分を抽出します。
ポーリング間隔より短命な接続(1秒以内に閉じる curl など)は見えないことがあります。
出力されるイベント
非正規化カラム
| カラム | 例 (ESTABLISHED) | 例 (LISTEN) |
|---|---|---|
pid | 4567 | 999 |
process_name | python3 | sshd |
local_port | 54321 | 22 |
remote_addr | 192.0.2.10 | NULL |
conn_state | ESTABLISHED | LISTEN |
auth Auth Collector
systemd-journald を journalctl 経由で購読し SSH/sudo を観測
目的
SSH ブルートフォース、初期アクセス確立、sudo による権限昇格を観測します。
採用方式: journalctl 購読
journalctl -f --output=json を子プロセスとして起動し、新規ログをリアルタイムに受け取ります。
journalctl -f --output=json ───── カーネル
──────────────────────────── │
│ │ inotify / signalfd
▼
stdout (1行 1JSON)
│
▼
Python: select.poll() ─────── stop_event チェック
│
▼
_handle_line(line): JSON 解析 → 正規表現で分類
出力されるイベント
非正規化カラム
| カラム | 値の例 |
|---|---|
auth_user | root |
auth_result | success / failure / sudo |
source_ip | 203.0.113.5 |
process_name | sshd |
cmdline | (sudo の場合) /usr/bin/cat /etc/shadow |