Skip to content

feat(agent): privilege-change probe — fentry on security_task_fix_setuid (JEF-54)#25

Merged
thejefflarson merged 1 commit into
mainfrom
jef-54-privilege-change-probe
Jun 21, 2026
Merged

feat(agent): privilege-change probe — fentry on security_task_fix_setuid (JEF-54)#25
thejefflarson merged 1 commit into
mainfrom
jef-54-privilege-change-probe

Conversation

@thejefflarson

Copy link
Copy Markdown
Owner

What

A new eBPF probe captures privilege escalation to root and emits a new Behavior::PrivilegeChange { from_uid, to_uid } — Falco privilege-escalation-rule parity (ADR-0014).

How

  • eBPF probe (agent/protector-agent-ebpf/src/main.rs): fentry on security_task_fix_setuid(struct cred *new, const struct cred *old, int flags). Reads new->uid.val and old->uid.val via bpf_probe_read_kernel (the cred -> kuid_t { val: u32 } pointer chase, same safe pattern as is_tmpfs; never bpf_d_path per JEF-68). Filters in-kernel to the escalation case (new_uid == 0 && old_uid != 0) so the ring carries only real gains of root. record_drop() on a reserve failure.
  • common (agent/common/src/lib.rs): KIND_PRIV_CHANGE = 5 (wire value 4 reserved) + #[repr(C)] PrivEvent { header, old_uid, new_uid }.
  • behavior (behavior/src/lib.rs): Behavior::PrivilegeChange { from_uid, to_uid }; fingerprint_keypriv:{to_uid} (stable); summaryprivilege change uid {from_uid} -> {to_uid}.
  • userspace (agent/protector-agent/src/observer.rs): RawEvent::PrivChange + a decode arm for KIND_PRIV_CHANGE + into_behavior; attached via the best-effort fentry table (fix_setuid / security_task_fix_setuid); stays on the decoupled drain→worker path (JEF-64).
  • engine (engine/src/engine/reason/proof.rs): corroborates() returns false for PrivilegeChange — model evidence, not blanket corroboration. Per-objective corroboration is JEF-49.

Validation

  • cargo +nightly check (eBPF crate): clean.
  • Repo root + agent/: cargo build / test / clippy --all-targets -- -D warnings / fmt — all green (warnings = errors).
  • New unit test: KIND_PRIV_CHANGE decodes to PrivilegeChange { from_uid, to_uid } and into_behavior maps it through.
  • docker build -f agent/Dockerfile --target builderEXIT 0 (compiles the eBPF object via bpf-linker and builds protector-agent --features ebpf).

Concerns

  • Verifier-accept is on-node only (JEF-68): the Docker build confirms compile + bpf-linker, but the in-kernel verifier only runs when the probe attaches on a real node.
  • Hook symbol: security_task_fix_setuid is the chosen LSM hook (the credential-fix path where setuid lands). It's attached best-effort — if it isn't fentry-attachable on 6.8, the agent logs and continues; security_capset / the setuid path remain documented fallbacks. The escalation-to-root signal is the goal.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP

…uid (JEF-54)

A new eBPF probe captures privilege escalation to root and emits a new
Behavior::PrivilegeChange { from_uid, to_uid } — Falco privilege-escalation
parity (ADR-0014).

- eBPF: fentry on security_task_fix_setuid(new, old, flags). Reads new->uid.val
  and old->uid.val via bpf_probe_read_kernel (cred -> kuid_t { val: u32 } chase,
  same pattern as is_tmpfs; never bpf_d_path per JEF-68). Emits ONLY on
  escalation (new_uid == 0 && old_uid != 0) to keep ring volume low and the
  signal meaningful. record_drop() on reserve failure.
- common: KIND_PRIV_CHANGE = 5 (wire 4 reserved) + repr(C) PrivEvent
  { header, old_uid, new_uid }.
- behavior: Behavior::PrivilegeChange; fingerprint_key "priv:{to_uid}";
  summary "privilege change uid {from} -> {to}".
- userspace: RawEvent::PrivChange + decode arm for KIND_PRIV_CHANGE +
  into_behavior; attach via the best-effort fentry table; stays on the
  decoupled-drain/worker path (JEF-64).
- engine: proof.rs corroborates() => PrivilegeChange is NON-corroborating
  (per-objective corroboration is JEF-49).
- test: KIND_PRIV_CHANGE decodes to PrivilegeChange { from, to }.

Verifier-accept is on-node only (JEF-68); validated via cargo +nightly check,
workspace build/test/clippy/fmt, and the Docker builder target (EXIT 0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP
@thejefflarson thejefflarson force-pushed the jef-54-privilege-change-probe branch from bd5ab38 to cc721e9 Compare June 21, 2026 18:44
@thejefflarson thejefflarson merged commit d491683 into main Jun 21, 2026
4 checks passed
@thejefflarson thejefflarson deleted the jef-54-privilege-change-probe branch June 21, 2026 18:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant