@@ -62,6 +62,9 @@ cargo build --release
6262# Monitor a process that's already running:
6363sudo ./target/release/wraith attach 4242
6464
65+ # Monitor every process matching a name at once:
66+ sudo ./target/release/wraith scan --match nginx
67+
6568# Emit machine-readable events for your SIEM/pipeline:
6669./target/release/wraith run --json events.jsonl -- ./target
6770```
@@ -104,18 +107,96 @@ catches every stage and correlates them into one verdict.
104107| ` crash ` | HIGH | Target took SIGSEGV/SIGILL/SIGBUS/SIGABRT — often a * failed* exploit worth investigating. |
105108| ` exploitation_chain ` | CRITICAL | Multiple primitives correlated into a single high-confidence verdict. |
106109| ` sensitive_call ` | INFO | Audit breadcrumb (with ` --audit-sensitive ` ): a sensitive syscall from legitimate code. |
110+ | ` blocked ` | CRITICAL | Enforcement neutralised the offending syscall in place (` --block ` ). |
111+ | ` killed ` | CRITICAL | Enforcement killed the traced tree on confirmed exploitation (` --kill ` ). |
107112
108113Tuning:
109114
110115```
111116--jit-critical treat anonymous-exec pages as HIGH (targets that never JIT)
117+ --trust-region A-B treat the hex range [A,B) as legitimate JIT (repeatable)
112118--no-stack-pivot disable the ROP stack-pivot heuristic
113119--audit-sensitive log sensitive syscalls from legitimate code too
114120--min <sev> floor: info|warn|high|critical (default warn)
115121```
116122
117123---
118124
125+ ## Active response (enforcement)
126+
127+ By default Wraith only * detects* — it never touches the tracee. Because it sits
128+ at the syscall-entry stop, though, it is standing at the one moment the
129+ offending syscall has not yet run, so it can also * stop* the attack:
130+
131+ ``` bash
132+ # Neutralise the offending syscall in place — the injected code's execve/connect
133+ # returns an error and never takes effect; the process lives on so you can watch
134+ # what it does next.
135+ wraith run --block -- ./target
136+
137+ # Terminate the whole traced tree the instant exploitation is confirmed, before
138+ # the offending syscall executes.
139+ wraith run --kill -- ./target
140+ ```
141+
142+ Both fire only on a ** CRITICAL** verdict — injected code issuing a * sensitive*
143+ syscall, or a correlated exploitation chain — so a HIGH/WARN anomaly (a JIT
144+ page, a lone RWX mapping) never trips enforcement. ` --block ` overwrites the
145+ syscall number at its entry stop so the kernel skips it and returns ` -ENOSYS ` ;
146+ ` --kill ` sends ` SIGKILL ` to every traced thread-group.
147+
148+ ### Trusting a JIT
149+
150+ Language runtimes (Node.js, the JVM, .NET, browsers) execute JIT-compiled code
151+ from anonymous executable pages and flip writable pages to executable as they
152+ compile — behaviour that looks, syscall-for-syscall, like payload staging. When
153+ you know where a runtime places its code, hand Wraith the range and it treats
154+ provenance and W^X inside it as legitimate:
155+
156+ ``` bash
157+ # Trust one or more JIT arenas (repeat --trust-region as needed).
158+ wraith run --trust-region 7f2a10000000-7f2a14000000 -- ./node-service
159+ ```
160+
161+ Trust applies to concrete addresses — the syscall's execution site and an
162+ ` mprotect ` target page — so a JIT that respects W^X (map RW, write, ` mprotect `
163+ RX) is fully exempted, while a * direct* RWX allocation elsewhere is still
164+ flagged.
165+
166+ ---
167+
168+ ## Scanning many processes at once
169+
170+ ` run ` and ` attach ` watch a single process tree. ` scan ` attaches to a whole set
171+ of already-running processes in one shot — select them by name/cmdline
172+ substring, or take everything you have permission to trace:
173+
174+ ``` bash
175+ # Attach to every process whose name or command line contains "nginx"
176+ # (repeat --match to widen the net); follows the children they spawn too.
177+ sudo wraith scan --match nginx --match redis
178+
179+ # Attach to every process we're allowed to trace (heavy — see below).
180+ sudo wraith scan --all --min high
181+ ```
182+
183+ One tracer drives all of them through a single reap loop, and enforcement
184+ (` --block ` /` --kill ` ) and the JSON stream work exactly as they do for a single
185+ target. Caveats worth knowing:
186+
187+ - ** Needs privilege.** Attaching to a process you don't own requires
188+ ` CAP_SYS_PTRACE ` (run as root); processes you can't attach to are skipped, not
189+ fatal.
190+ - ** It has a cost.** Every traced process pays the two-stops-per-syscall
191+ ` ptrace ` tax, so ` --all ` on a busy host is expensive — prefer ` --match ` .
192+ Whole-system, near-zero-overhead monitoring is the eBPF backend's job (below).
193+ - ** Non-destructive by default.** Unlike ` run ` , ` scan ` /` attach ` do * not* set
194+ ` PTRACE_O_EXITKILL ` : stopping Wraith leaves every scanned process running.
195+ - ** Post-attach threads only.** As with ` attach ` , sibling threads that already
196+ existed before Wraith attached aren't picked up automatically (see below).
197+
198+ ---
199+
119200## Architecture
120201
121202Small, auditable, and dependency-light on purpose — a sensor others run should
@@ -129,7 +210,7 @@ carry the smallest supply chain you can manage. The engine links only `nix` and
129210 ├─ syscalls.rs the syscall table Wraith cares about
130211 ├─ detect.rs the invariants + the exploitation-chain correlator
131212 ├─ event.rs detection events + their JSONL form
132- ├─ tracer.rs the ptrace engine (spawn/attach, thread-following loop )
213+ ├─ tracer.rs the ptrace engine (spawn/attach/scan , thread-following, enforcement )
133214 └─ bin/
134215 ├─ wraith.rs the CLI sensor
135216 ├─ benign.rs false-positive control target
@@ -173,20 +254,28 @@ claim to be a finished EDR.
173254 provenance rule — that's what the stack-pivot heuristic is for, and why
174255 return-address validation and a shadow stack are on the roadmap.
175256- ** Legitimate JIT** (browsers, JVMs, .NET) runs code from anonymous
176- executable pages; hence ` AnonExec ` is WARN by default and configurable.
177-
178- Roadmap: eBPF backend · return-address/shadow-stack checks · ROP-chain length
179- heuristics · per-thread stack tracking · seizing pre-existing threads on attach
180- · per-process behavioural baselining · a policy DSL for allow-listing
181- legitimate JIT regions.
257+ executable pages; hence ` AnonExec ` is WARN by default, ` AnonExec ` can be
258+ raised with ` --jit-critical ` , and known JIT arenas can be exempted outright
259+ with ` --trust-region ` .
260+ - ** Coverage vs. cost.** ` scan --match ` /` --all ` can watch many processes at
261+ once, but every traced process pays the two-stops-per-syscall ` ptrace ` tax, so
262+ this suits a handful of high-value targets rather than a busy whole system.
263+ Near-zero-overhead, watch-everything monitoring is the eBPF backend's job (see
264+ below), not something the ` ptrace ` engine should attempt.
265+
266+ Roadmap: eBPF backend (near-zero-overhead, system-wide) · return-address/
267+ shadow-stack checks · ROP-chain length heuristics · per-thread stack tracking ·
268+ seizing pre-existing threads on attach · per-process behavioural baselining ·
269+ richer JIT policy (auto-learn a runtime's arenas rather than hand-supplied
270+ ranges).
182271
183272---
184273
185274## Building & testing
186275
187276``` bash
188277cargo build --release
189- cargo test # 28 unit + 6 end-to-end tests
278+ cargo test # 36 unit + 9 end-to-end tests
190279cargo clippy --all-targets
191280./demo.sh # side-by-side benign vs. exploitation run
192281```
0 commit comments