⭐ GitHub: github.com/grloper/Wraith · open source (MIT) · by @grloper
grloper / Wraith
Wraith is a low-level Rust security sensor that answers a question most tools can't: "is this process being exploited right now?" By focusing entirely on anomalous runtime behavior rather than known signatures or payloads, Wraith detects live exploits and uncovers zero-day vulnerabilities completely in advance of a patch.
Runtime security, through a different lens.
Observe execution. Inspect its origin. Understand the anomaly.
Quickstart / Real demo / Design / Threat model / Contribute
Concept artwork, not telemetry. Still version · Art direction & motion
Wraith
A small Rust runtime sensor that inspects where Linux syscalls come from.
Injected code can change its bytes. It still has to execute somewhere. Wraith uses ptrace and /proc/<pid>/maps to flag sensitive syscalls from suspicious executable memory, inspect W^X requests, and correlate signals across threads. No payload signatures cloud service, or model required. Only two direct Rust dependencies: nix and libc.
Continuously developed, not a finalized endpoint product. The focus is reproducible Linux security investigation with honest coverage and workload evidence—not an antivirus or Nmap replacement. See the development program and Linux/Kali tool comparison.
| Inspect the origin Executable mappings and syscall provenance. No payload signature required. | Follow the execution Shared process |
The payload changed its bytes again. The hash didn't match, the signature didn't fire, and the exploit ran anyway. Every payload can change what it looks like. None of them can change where it runs from. So I built a sensor that stops a process at every syscall and asks one question: is the instruction that issued this syscall sitting in file-backed code, or in RWX memory, the heap, the stack, or nowhere at all?
That is how a sensor like this is supposed to catch a zero-day: not by targeting the exact weakness, but by flagging the one thing every exploit has to do, run code from where code should never run.
Real output of bash demo.sh, which I ran on WSL2 Ubuntu (Linux 5.15) for this post; the GIF replays the captured text line by line. Twenty per-thread log lines from one benign case are trimmed in the GIF only. The "injection" is a safe simulator that maps an RWX page and opens a socket from it. No real exploit is involved.
Six simulator cases: benign runs clean, the injection gets flagged CRITICAL with sensor exit 3, block mode returns -ENOSYS instead of running the payload syscall, and kill mode terminates the tree first.
The idea
On Linux x86-64, Wraith uses ptrace to stop a target at each syscall entry, reads the registers, and consults a cached copy of /proc/<pid>/maps. It then checks three invariants that ordinary programs normally satisfy:
- Provenance. The instruction issuing a sensitive syscall should sit in an accepted executable mapping, meaning file-backed code or the vDSO. If it comes from RWX memory, the heap, the stack or an anonymous mapping, that is flagged.
- W^X. Requests for writable and executable memory, and writable-to-executable transitions, are reported.
- Stack placement. A stack pointer in the heap or a file mapping is a heuristic HIGH.
A violation is a signal for review, not proof of exploitation. It needs no payload signature, but it also does not know the vulnerability, so it only sees exploitation that leaves one of these artifacts.
What it can show today
The repo ships safe local simulators, not exploits. bash demo.sh runs six cases and asserts the outcomes:
| Case | What happened |
| Benign program | No detections: 80 syscalls, 0 events, exit 0 |
| Benign multi-threaded program | No detections across worker threads |
| Shellcode simulator | HIGH wx_violation, then CRITICAL foreign_origin_syscall, then CRITICAL exploitation_chain; sensor exit 3 |
| Same on a worker thread | Detected, exit 3 |
| With --block | Syscall returns -ENOSYS (the simulator prints socket() -> -38), fixture survives |
| With --kill | Traced tree terminated before the payload syscall runs |
The script ends with PASS: both controls clean; main/worker injection detected; block and kill verified. That is the extent of the detection evidence. I have not run it against a real CVE exploit or real malware, and no false-positive rate has been established.
Quickstart
Linux 5.3+ on x86-64, Rust 1.74+, procfs mounted, and permission to trace your own child. WSL2 works; restricted containers may deny ptrace. Windows and macOS are not supported.
git clone https://github.com/grloper/Wraith.git && cd Wraith cargo build --release --locked ./target/release/wraith doctor bash demo.sh ./target/release/wraith run --json events.jsonl -- ./your-target Sensor exit codes: 0 no HIGH/CRITICAL, 1 HIGH, 2 operational error, 3 CRITICAL. That is the sensor's status, not the target's.
How it is built
An engine module owns policy, map lookup and statistics; a tracer module owns ptrace and enforcement. Provenance lookup is a binary search over parsed mapping intervals. Worker threads share per-address-space state. There are two direct dependencies, nix and libc, and no async runtime or serialization crate. Output is human-readable on stderr plus JSONL evidence, with schema versions, and a strict triage helper validates complete logs.
For this post I built the repo at its current main and ran the whole Rust suite in WSL2 with WRAITH_REQUIRE_PTRACE=1, so skipped ptrace tests would fail instead of passing quietly: 134 tests passed, 0 failed. (The README and docs/verification.md still say 130; the docs have not caught up with a few newer tests.)
What broke
This is the useful part. The first real Java control completed successfully, but two handled SIGSEGV delivery stops were reported as HIGH crash events, because the JVM uses signals internally. The fix was to treat signal delivery as INFO and require a confirmed terminal status before calling something a crash. JVMs also request RWX pages, and the benign Java control still produced four HIGH findings for those RWX requests. Legitimate JITs look like attackers to this check, which is why the README says to baseline first and never to turn on --jit-critical or enforcement blindly.
Another bug: the kill path raced with queued stops while targets died, and a test using 64 alternating single-thread and worker-thread targets failed with ESRCH at iteration 5 before the fix. The repo keeps the failing-then-passing evidence in docs/verification.md.
The honest part
- Maturity: pre-1.0, tested against its own simulators rather than real exploits.
- Blind spots: ROP/JOP entirely inside accepted file-backed code, data-only attacks, malicious shared libraries, memfd-backed executable images and map races. The threat model lists them.
- Overhead: every traced syscall stops the target. On two WSL workloads the median slowdown was 3.36x (loopback HTTP, 20 requests) and 3.00x (SQLite, 200 transactions); a syscall-only loop was 140x. Use it on a few high-value targets, not a whole host.
- JIT runtimes (JVM, browsers, .NET) will produce findings. Baseline first.
- Environment: all measurements, mine and the repo's, come from WSL2, not native Linux. No fleet compatibility claim. An eBPF backend is not implemented.
- Enforcement can break legitimate applications and is not a substitute for a sandbox.
- A clean result means no configured signal was observed, not that an exploit is absent.
- It is not an EDR.
Try it, and help
The most useful contributions are a benign program that trips it, a regression fixture for it, or timing numbers from a real service on native Linux. Start with CONTRIBUTING.md and docs/roadmap.md at github.com/grloper/Wraith. Please report security issues through SECURITY.md.
What benign program on your machine trips it first?






