Skip to content

Add active enforcement and multi-process scanning to Wraith#3

Merged
grloper merged 2 commits into
mainfrom
claude/wraith-security-sensor-0pjajs
Jul 12, 2026
Merged

Add active enforcement and multi-process scanning to Wraith#3
grloper merged 2 commits into
mainfrom
claude/wraith-security-sensor-0pjajs

Conversation

@grloper

@grloper grloper commented Jul 12, 2026

Copy link
Copy Markdown
Owner

This PR adds two major features to Wraith's exploitation detection engine:

Summary

Wraith can now actively intervene to stop confirmed exploits (--block/--kill enforcement modes) and monitor multiple processes simultaneously (scan mode). Detection remains the default and only guaranteed side-effect-free mode.

Key Changes

Enforcement (Active Response)

  • Added Enforcement enum with three modes: Observe (default, detect-only), Block (neutralize syscalls), and Kill (terminate process tree)
  • --block overwrites the syscall number at entry stop so the kernel skips it and returns -ENOSYS, letting the process survive
  • --kill sends SIGKILL to all traced thread-groups before the offending syscall executes
  • Enforcement fires only on CRITICAL verdicts (injected code + sensitive syscall, or exploitation chain)
  • Added Blocked and Killed event kinds to report enforcement actions
  • New block_syscall() and kill_tree() methods in Tracer to implement enforcement

Multi-Process Scanning

  • Added Target::ScanAttached variant to support attaching to multiple processes at once
  • New Tracer::attach_many() method that attaches to a set of PIDs, skipping those we lack permission for
  • scan CLI mode with --match <substring> (repeatable) and --all filters to select processes by name/cmdline
  • enumerate_scan_pids() walks /proc to find matching processes; excludes self and PID 1
  • Scan mode does not set PTRACE_O_EXITKILL, so stopping Wraith leaves scanned processes running (non-destructive)

JIT Trust Regions

  • Added --trust-region A-B option to exempt operator-specified hex address ranges from provenance/W^X checks
  • Config::is_trusted() method checks if an address falls within a trusted region
  • Trusted regions suppress ForeignOriginSyscall events and W^X staging detection, allowing legitimate JIT engines to run without false positives
  • parse_region() parses hex ranges like 7f0000030000-7f0000031000

Tracer Engine Refactoring

  • Refactored set_options() to accept kill_on_exit parameter: only set PTRACE_O_EXITKILL for spawned processes we own, not for attached/scanned processes
  • New Inspection struct captures both syscall number and max severity from a single inspection, enabling enforcement decisions
  • inspect() now returns Inspection instead of Option<u64>, tracking the highest severity event raised
  • Updated main loop to seed and resume all initial tracees (not just one root), supporting both single-target and multi-target modes
  • Enforcement logic integrated into the syscall-stop handler, acting before resuming the tracee

CLI Enhancements

  • Updated help text and usage examples for all three modes (run/attach/scan)
  • Added enforcement mode indicator to startup messages
  • New unit tests for proc_matches() and parse_region() functions
  • Integration tests for --block and --kill enforcement modes

Notable Implementation Details

  • Enforcement decisions happen at syscall-entry stops, the only moment before the offending syscall executes
  • Multi-process scanning uses a single reap loop driving all tracees, with per-thread state tracking tgid for shared address spaces
  • Trusted regions are concrete address ranges (not wildcards), so a JIT respecting W^X (map RW → write → mprotect RX) is fully exempted while direct RWX elsewhere is still flagged
  • Kill enforcement sends one SIGKILL per distinct thread-group (tgid) to avoid redundant signals
  • Block enforcement tolerates ptrace failures after kill (racing with process death) but fails hard on other syscall-stop errors

https://claude.ai/code/session_01117vzFc51mLKYnKW2SnWzg

claude added 2 commits July 12, 2026 20:23
Wraith was detection-only: by the time it logged EXPLOITATION DETECTED the
payload's syscall had already run. It also had no nuanced way to tell it that a
given anonymous/JIT region is legitimate, so language runtimes could false-
positive. Both are addressed here on the existing ptrace engine, since the
syscall-entry stop is exactly the moment the offending syscall has not yet run.

Enforcement (Config::enforcement / --block / --kill), triggered only on a
CRITICAL verdict so HIGH/WARN anomalies never trip it:
  - --block neutralises the offending syscall in place — its number is
    overwritten at the entry stop so the kernel skips it and returns -ENOSYS;
    the process survives.
  - --kill SIGKILLs every traced thread-group before the syscall executes.
Both emit a `blocked`/`killed` event so the action is visible in the log/JSON.

JIT trust regions (Config::trusted_regions / --trust-region A-B, repeatable):
half-open hex ranges the operator vouches for. A syscall whose execution site,
or an mmap/mprotect whose target page, falls inside one is exempt from the
provenance and W^X rules — so a W^X-respecting JIT is fully exempted while a
direct RWX allocation elsewhere is still flagged.

Tests: trusted-region suppression + out-of-range still-flagged (unit); kill
terminates via SIGKILL, block neutralises yet exits clean, observe never
intervenes (end-to-end). 31 unit + 9 e2e, clippy clean. README/demo updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01117vzFc51mLKYnKW2SnWzg
`run` and `attach` watch a single process tree. `scan` attaches to a whole set
of already-running processes in one shot — selected by name/cmdline substring
(`--match`, repeatable) or everything traceable (`--all`) — and drives them all
through the existing single reap loop. Enforcement, JSON output and thread-
following work unchanged across the set.

Engine changes (tracer.rs):
  - Target gains a ScanAttached(Vec<Pid>) variant; run() seeds and kicks every
    initial tracee and no longer assumes a single distinguished root (exit
    status is recorded only for a lone spawned/attached target).
  - Tracer::attach_many attaches to each pid, skipping any it can't trace
    (permission / already gone) rather than failing the whole scan.
  - set_options takes kill_on_exit: spawned children keep PTRACE_O_EXITKILL
    (we own them); attach and scan drop it, so stopping the monitor never kills
    the processes it was only observing.

CLI (wraith.rs): `wraith scan (--match <s> | --all)`, with pid enumeration over
/proc split into a pure, unit-tested matcher (comm + cmdline). Help documents
the privilege requirement and the per-process ptrace cost (favour --match over
--all on a busy host).

Verified end-to-end: scan attaches to a named target and catches its full
exploitation chain, and stays silent monitoring a benign process. 36 unit + 9
e2e tests, clippy clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01117vzFc51mLKYnKW2SnWzg
@grloper
grloper merged commit 8e5642f into main Jul 12, 2026
3 checks passed
@grloper
grloper deleted the claude/wraith-security-sensor-0pjajs branch July 12, 2026 20:48
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.

2 participants