Add active enforcement and multi-process scanning to Wraith#3
Merged
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds two major features to Wraith's exploitation detection engine:
Summary
Wraith can now actively intervene to stop confirmed exploits (
--block/--killenforcement modes) and monitor multiple processes simultaneously (scanmode). Detection remains the default and only guaranteed side-effect-free mode.Key Changes
Enforcement (Active Response)
Enforcementenum with three modes:Observe(default, detect-only),Block(neutralize syscalls), andKill(terminate process tree)--blockoverwrites the syscall number at entry stop so the kernel skips it and returns-ENOSYS, letting the process survive--killsendsSIGKILLto all traced thread-groups before the offending syscall executesBlockedandKilledevent kinds to report enforcement actionsblock_syscall()andkill_tree()methods inTracerto implement enforcementMulti-Process Scanning
Target::ScanAttachedvariant to support attaching to multiple processes at onceTracer::attach_many()method that attaches to a set of PIDs, skipping those we lack permission forscanCLI mode with--match <substring>(repeatable) and--allfilters to select processes by name/cmdlineenumerate_scan_pids()walks/procto find matching processes; excludes self and PID 1PTRACE_O_EXITKILL, so stopping Wraith leaves scanned processes running (non-destructive)JIT Trust Regions
--trust-region A-Boption to exempt operator-specified hex address ranges from provenance/W^X checksConfig::is_trusted()method checks if an address falls within a trusted regionForeignOriginSyscallevents and W^X staging detection, allowing legitimate JIT engines to run without false positivesparse_region()parses hex ranges like7f0000030000-7f0000031000Tracer Engine Refactoring
set_options()to acceptkill_on_exitparameter: only setPTRACE_O_EXITKILLfor spawned processes we own, not for attached/scanned processesInspectionstruct captures both syscall number and max severity from a single inspection, enabling enforcement decisionsinspect()now returnsInspectioninstead ofOption<u64>, tracking the highest severity event raisedCLI Enhancements
proc_matches()andparse_region()functions--blockand--killenforcement modesNotable Implementation Details
SIGKILLper distinct thread-group (tgid) to avoid redundant signalshttps://claude.ai/code/session_01117vzFc51mLKYnKW2SnWzg