Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: ci

on:
push:
branches: ["**"]
pull_request:

jobs:
build-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
run: rustup toolchain install stable --profile minimal --component clippy
- name: Build
run: cargo build --all-targets --verbose
- name: Clippy
run: cargo clippy --all-targets -- -D warnings
- name: Test
# The end-to-end tests need ptrace; they self-skip where it is
# unavailable, so the suite stays green on constrained runners.
run: cargo test --verbose
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
**/*.rs.bk
*.jsonl
47 changes: 47 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
name = "wraith"
version = "0.1.0"
edition = "2021"
rust-version = "1.74"
description = "Signature-free runtime exploitation detection via syscall provenance verification"
license = "MIT"
repository = "https://github.com/grloper/cyber-rust"
readme = "README.md"
keywords = ["security", "edr", "exploit", "ptrace", "detection"]
categories = ["command-line-utilities"]

[[bin]]
name = "wraith"
path = "src/bin/wraith.rs"

# Self-contained targets used by the test-suite and for live demos.
# `benign` never triggers a detection; `shellcode-sim` stages and executes
# code from an RWX anonymous mapping the way a real exploit payload does.
[[bin]]
name = "benign"
path = "src/bin/benign.rs"

[[bin]]
name = "shellcode-sim"
path = "src/bin/shellcode_sim.rs"

[dependencies]
nix = { version = "0.29", features = ["ptrace", "process", "signal"] }
libc = "0.2"

[profile.release]
lto = true
codegen-units = 1
panic = "abort"
strip = true
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 grloper, pandaadir05

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
186 changes: 185 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,185 @@
# cyber-rust
# Wraith

**Signature-free runtime exploitation detection via syscall provenance verification.**

Wraith is a low-level Rust security sensor that answers a question most tools
can't: *is this process being exploited right now?* — without knowing the
vulnerability or the payload in advance. That makes it effective against
**zero-days and n-days alike**, because it keys on the *behaviour of
exploitation*, not the *identity of the bug*.

> Built as the runtime-defence companion to [`ghost`](https://github.com/pandaadir05/ghost).
> `ghost` finds weaknesses; `wraith` catches them being used.

---

## The idea

Defensive tooling usually answers one of two questions:

| Question | Needs to know | Blind to |
|---|---|---|
| *Does this code contain a known bug?* (SAST/scanners) | the vulnerability | zero-days |
| *Do these bytes match known-bad?* (AV/YARA) | the payload | novel payloads |

Wraith answers a **third** question — *is this process executing in a state
that only exploitation produces?* — and it needs to know neither the bug nor
the payload.

The observation behind it: **every** memory-corruption exploit, no matter the
root cause (stack overflow, UAF, type confusion, an unknown zero-day),
eventually converges on the same visible act. To accomplish anything — spawn a
shell, open a socket, read a secret — the attacker must issue **system calls**.
And at the instant those syscalls happen, the process is in a state that
legitimate execution *never* produces.

Wraith attaches to a process with `ptrace`, stops at the entry of every
syscall, and checks a handful of invariants that hold for all benign programs:

1. **Provenance** — a `syscall` instruction only ever runs from a *file-backed
executable page* (the program's own `.text`, a shared library, or the kernel
vDSO). Shellcode injected into the heap, stack, or an anonymous page
**breaks this**.
2. **W^X** — no benign program needs a page that is writable *and* executable,
nor to flip a writable page to executable. Payload staging **breaks this**.
3. **Stack integrity** — at syscall time the stack pointer lives inside a real
stack, never the heap or a file image. A ROP stack pivot **breaks this**.

Because these are invariants of *legitimate behaviour* rather than signatures
of *specific attacks*, any violation is evidence of exploitation regardless of
how the attacker got there.

---

## Quickstart

```bash
cargo build --release

# Monitor a program you launch:
./target/release/wraith run -- /usr/bin/some-service --flags

# Monitor a process that's already running:
sudo ./target/release/wraith attach 4242

# Emit machine-readable events for your SIEM/pipeline:
./target/release/wraith run --json events.jsonl -- ./target
```

Wraith exits `0` when clean, `1` on suspicious (HIGH) activity, and `3` when it
detects exploitation (CRITICAL) — so it drops straight into CI and fuzzing
harnesses as a behavioural oracle.

### See it work

The repo ships two self-contained targets (no real exploit required):

```console
$ wraith run --min info -- ./target/release/benign
wraith: monitoring `./target/release/benign` (provenance mode)
benign: ... normal work ...
wraith: 81 syscalls, 0 event(s); verdict: clean # <- false-positive control

$ wraith run -- ./target/release/shellcode-sim
HIGH pid=6586 wx_violation mmap @ 0x7fa8ad52534a `mmap` requests writable+executable memory — classic shellcode staging
CRITICAL pid=6586 foreign_origin_syscall socket @ 0x7fa8ad696011 [anon] sensitive syscall `socket` issued from wx-violation memory — injected code is now acting
CRITICAL pid=6586 exploitation_chain socket @ 0x7fa8ad696011 [correlated] EXPLOITATION CHAIN: executable payload staged (W^X) -> sensitive syscall from injected code
wraith: 69 syscalls, 3 event(s); verdict: EXPLOITATION DETECTED
```

`shellcode-sim` stages an RWX page, writes a payload into it, and issues a
syscall from that page — the exact tail end of a real exploit — and Wraith
catches every stage and correlates them into one verdict.

---

## What it detects

| Event | Severity | Meaning |
|---|---|---|
| `foreign_origin_syscall` | HIGH / CRITICAL | A syscall issued from non-code memory (heap/stack/anon/RWX). CRITICAL when the syscall is *sensitive* (execve, connect, ptrace, …). |
| `wx_violation` | HIGH | `mmap`/`mprotect` requesting writable **and** executable memory. |
| `wx_transition` | HIGH | A writable page being flipped to executable — payload staging. |
| `stack_pivot` | HIGH | Stack pointer sitting in the heap or a file image at syscall time — a ROP indicator. |
| `crash` | HIGH | Target took SIGSEGV/SIGILL/SIGBUS/SIGABRT — often a *failed* exploit worth investigating. |
| `exploitation_chain` | CRITICAL | Multiple primitives correlated into a single high-confidence verdict. |
| `sensitive_call` | INFO | Audit breadcrumb (with `--audit-sensitive`): a sensitive syscall from legitimate code. |

Tuning:

```
--jit-critical treat anonymous-exec pages as HIGH (targets that never JIT)
--no-stack-pivot disable the ROP stack-pivot heuristic
--audit-sensitive log sensitive syscalls from legitimate code too
--min <sev> floor: info|warn|high|critical (default warn)
```

---

## Architecture

Small, auditable, and dependency-light on purpose — a sensor others run should
carry the smallest supply chain you can manage. The engine links only `nix` and
`libc`; JSON is emitted by hand.

```
src/
├─ maps.rs parse /proc/<pid>/maps into typed regions
├─ provenance.rs classify an instruction/stack pointer against the map
├─ syscalls.rs the syscall table Wraith cares about
├─ detect.rs the invariants + the exploitation-chain correlator
├─ event.rs detection events + their JSONL form
├─ tracer.rs the ptrace engine (spawn/attach, syscall loop)
└─ bin/
├─ wraith.rs the CLI sensor
├─ benign.rs false-positive control target
└─ shellcode_sim.rs exploitation-behaviour simulator
```

The tracer adds no syscall of its own on the hot path beyond the unavoidable
`getregs`, and re-reads `/proc/<pid>/maps` only when a memory operation could
have changed it.

---

## Limitations & roadmap

Wraith is an honest research prototype with a clear production path; it does not
claim to be a finished EDR.

- **`ptrace` overhead.** Two stops per syscall suits high-value targets
(network daemons, parsers, fuzz targets), not the whole system. The
production path is the same logic on **eBPF** (`tracepoint/raw_syscalls` +
a page-provenance map) for near-zero overhead — the detection model is
transport-agnostic by design.
- **Multithreading.** The current engine focuses on single-threaded targets;
full `PTRACE_O_TRACECLONE` thread-following and per-thread stack tracking is
the next milestone.
- **Pure-ROP that never leaves legit code.** An attacker who only reuses
existing `.text` and never stages new executable memory won't trip the
provenance rule — that's what the stack-pivot heuristic is for, and why
return-address validation and a shadow stack are on the roadmap.
- **Legitimate JIT** (browsers, JVMs, .NET) runs code from anonymous
executable pages; hence `AnonExec` is WARN by default and configurable.

Roadmap: eBPF backend · thread-following · return-address/shadow-stack checks ·
ROP-chain length heuristics · per-process behavioural baselining · a policy DSL
for allow-listing legitimate JIT regions.

---

## Building & testing

```bash
cargo build --release
cargo test # 28 unit + 4 end-to-end tests
cargo clippy --all-targets
./demo.sh # side-by-side benign vs. exploitation run
```

The end-to-end tests drive the real ptrace engine over the `benign` and
`shellcode-sim` binaries; they self-skip where `ptrace` is unavailable.

## License

MIT — see [LICENSE](LICENSE).
39 changes: 39 additions & 0 deletions demo.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
# Side-by-side demonstration: Wraith stays silent on a benign program and
# catches the payload simulator executing a syscall from injected memory.
set -euo pipefail

cd "$(dirname "$0")"

echo "==> building (release)"
cargo build --release --quiet

WRAITH=./target/release/wraith
BENIGN=./target/release/benign
SIM=./target/release/shellcode-sim

echo
echo "============================================================"
echo " 1/2 BENIGN target — expect: clean, exit 0"
echo "============================================================"
set +e
"$WRAITH" run --min info -- "$BENIGN"
echo " -> wraith exit code: $?"
set -e

echo
echo "============================================================"
echo " 2/2 SHELLCODE-SIM target — expect: EXPLOITATION DETECTED, exit 3"
echo "============================================================"
set +e
"$WRAITH" run -- "$SIM"
code=$?
echo " -> wraith exit code: $code"
set -e

echo
if [ "$code" -eq 3 ]; then
echo "Demo OK: benign was clean; injected-code execution was detected and correlated."
else
echo "Demo WARNING: expected exit 3 from the simulator run (got $code)."
fi
35 changes: 35 additions & 0 deletions src/bin/benign.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! A deliberately ordinary program. It performs a spread of everyday syscalls —
//! including sensitive ones like `socket` — but always from legitimate,
//! file-backed code. Wraith must report it as clean. Used by the test-suite as
//! the false-positive control and handy for a live "nothing fires" demo.

use std::io::Read;

fn main() {
println!("benign: starting normal work");

// Filesystem: open + read a file (open/read/close from libc — legit code).
if let Ok(mut f) = std::fs::File::open("/proc/self/status") {
let mut buf = String::new();
let _ = f.read_to_string(&mut buf);
println!("benign: read {} bytes from /proc/self/status", buf.len());
}

// Network: create and immediately close a socket. `socket` is a sensitive
// syscall, but issued from legitimate code, so it must NOT be flagged
// unless the operator explicitly asks for --audit-sensitive.
unsafe {
let fd = libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0);
if fd >= 0 {
libc::close(fd);
println!("benign: opened and closed a socket from legitimate code");
}
}

// A little compute so the process lives long enough to be worth tracing.
let mut acc: u64 = 0;
for i in 0..100_000u64 {
acc = acc.wrapping_add(i.wrapping_mul(2654435761));
}
println!("benign: done ({acc:#x})");
}
Loading
Loading