SkillAgentSearch skills...

Rustinel

Rust Windows EDR (user-mode, no driver): ETW → Sysmon-style normalization → Sigma/Yara/IOC detection → ECS NDJSON alerts.

Install / Use

/learn @Karib0u/Rustinel

README

Rustinel

High-performance, user-mode Windows EDR in Rust

<p align="center"> <a href="https://karib0u.github.io/rustinel/"><img src="https://img.shields.io/badge/docs-available-brightgreen" alt="Docs"></a> <img src="https://img.shields.io/badge/platform-Windows-blue?logo=windows" alt="Platform Windows"> <img src="https://img.shields.io/badge/language-Rust-orange?logo=rust" alt="Language Rust"> <img src="https://img.shields.io/badge/license-Apache%202.0-green" alt="License"> <img src="https://img.shields.io/badge/status-Alpha-yellow" alt="Status"> </p>

Rustinel is a high-throughput Windows EDR agent written in Rust. It collects kernel telemetry via ETW, normalizes events into a Sysmon-compatible schema, detects threats using Sigma + YARA, and outputs alerts as ECS NDJSON for straightforward SIEM ingestion.

✅ No kernel driver
✅ User-mode ETW pipeline
✅ Sigma behavioral detection, YARA scanning, IOCs detection ✅ Local hot reload for Sigma/YARA/IOC files
✅ ECS NDJSON alerts + operational logs

<p align="center"> <img src="docs/images/demo.gif" alt="Rustinel Demo" width="900"> </p>

Why Rustinel?

Rustinel is built for defenders who want:

  • Kernel-grade telemetry without kernel risk (ETW, user-mode)
  • Performance under volume (async pipeline + caching + noise reduction)
  • Detection compatibility (Sysmon-style normalization for Sigma)
  • Operational simplicity (NDJSON alerts on disk, easy to ship to a SIEM)

What it does

Rustinel monitors Windows endpoints by:

  • Collecting kernel events via ETW (process, network, file, registry, DNS, PowerShell, WMI, services, tasks)
  • Normalizing ETW events into Sysmon-compatible fields
  • Detecting threats using Sigma rules and YARA scanning
  • Detecting atomic IOCs (hashes, IP/CIDR, domains, path regex)
  • Hot-reloading local Sigma/YARA/IOC files without process restart
  • Writing alerts in ECS NDJSON format

Key features

  • User-mode only: no kernel driver required
  • Dual detection engines:
    • Sigma for behavioral detection
    • YARA for file scanning on process start
  • Atomic IOC detection: hashes, IP/CIDR, domains, path regex
  • Local hot reload: Sigma, YARA, and IOC files are reloaded in-place with atomic swaps
  • Noise reduction:
    • keyword filtering at the ETW session
    • router-level filtering for high-volume network events
    • optional network connection aggregation
  • Hot-path optimizations:
    • Sigma rules are filtered at load time (category/product/service)
    • Sigma conditions are transpiled + precompiled at startup and on hot reload
    • process-context enrichment is attached on alerts, not every event
  • Enrichment:
    • NT → DOS path normalization
    • PE metadata extraction (OriginalFileName/Product/Description)
    • parent process correlation
    • SID → DOMAIN\User resolution
    • DNS caching and reverse mapping
  • Windows service support (install/start/stop/uninstall)
  • ECS NDJSON alerts for SIEM ingestion
  • Optional active response (dry-run or terminate on critical alerts)

Requirements

  • Windows 10/11 or Server 2016+
  • Administrator privileges (ETW + service management)
  • Rust 1.92+ (build from source)

Quick start

Run from an elevated PowerShell.

Option 1: Download Release (Recommended)

  1. Download the latest release from GitHub Releases.
  2. Extract the archive.
  3. Run from an elevated PowerShell:
    .\rustinel.exe run --console
    

Option 2: Build from Source

# Build
cargo build --release

# Run (console output)
.\target\release\rustinel.exe run --console

Running without arguments is equivalent to rustinel run.


2-minute demo

Sigma demo

This repo ships with an example rule: rules/sigma/example_whoami.yml

  1. Start Rustinel (admin shell):
cargo run -- run --console
  1. Trigger the rule:
whoami /all
  1. Verify an alert was written:
  • logs/alerts.json.YYYY-MM-DD

YARA demo

This repo ships with an example rule: rules/yara/example_test_string.yar

  1. Build the demo binary:
rustc .\examples\yara_demo.rs -o .\examples\yara_demo.exe
  1. Run it:
.\examples\yara_demo.exe
  1. Verify an alert includes the rule name:
  • ExampleMarkerString

Note: The demo binary runs in a loop to demonstrate active response. With response enabled and prevention_enabled = true, the process will be automatically terminated when the YARA rule triggers (YARA matches are treated as critical severity).


Service mode

.\target\release\rustinel.exe service install
.\target\release\rustinel.exe service start
.\target\release\rustinel.exe service stop
.\target\release\rustinel.exe service uninstall

Notes

  • service install registers the current executable path — run it from the final location.
  • Config and rules paths resolve from the working directory; for services, prefer absolute paths or env overrides.
  • Service runtime does not receive CLI flags; set log level via config.toml or EDR__LOGGING__LEVEL.

Configuration

Configuration precedence:

  1. CLI flags (highest, run mode only)
  2. Environment variables
  3. config.toml
  4. Built-in defaults

Example config.toml:

[scanner]
sigma_enabled = true
sigma_rules_path = "rules/sigma"
yara_enabled = true
yara_rules_path = "rules/yara"

[reload]
enabled = true
debounce_ms = 2000

[allowlist]
paths = [
  "C:\\Windows\\",
  "C:\\Program Files\\",
  "C:\\Program Files (x86)\\",
]

[logging]
level = "info"
directory = "logs"
filename = "rustinel.log"
console_output = true

[alerts]
directory = "logs"
filename = "alerts.json"
match_debug = "off" # off | summary | full

[response]
enabled = false
prevention_enabled = false
min_severity = "critical"
channel_capacity = 128
allowlist_images = []

[network]
aggregation_enabled = true
aggregation_max_entries = 20000
aggregation_interval_buffer_size = 50

[ioc]
enabled = true
hashes_path = "rules/ioc/hashes.txt"
ips_path = "rules/ioc/ips.txt"
domains_path = "rules/ioc/domains.txt"
paths_regex_path = "rules/ioc/paths_regex.txt"
default_severity = "high"
max_file_size_mb = 50

allowlist.paths is shared by default across:

  • response.allowlist_paths
  • ioc.hash_allowlist_paths
  • scanner.yara_allowlist_paths

If a module-specific list is explicitly set, it overrides the shared list for that module only.

Match debug output:

  1. alerts.match_debug = "off" disables match details in alerts (default).
  2. alerts.match_debug = "summary" adds rule condition + matched fields/patterns.
  3. alerts.match_debug = "full" adds matched values and YARA string snippets.

Environment overrides:

set EDR__LOGGING__LEVEL=debug
set EDR__SCANNER__SIGMA_RULES_PATH=C:\rules\sigma
set EDR__RELOAD__DEBOUNCE_MS=2000
set EDR__ALLOWLIST__PATHS=["C:\\Windows\\","C:\\Program Files\\"]
# optional module-specific override:
set EDR__SCANNER__YARA_ALLOWLIST_PATHS=["C:\\Windows\\","D:\\Trusted\\"]

CLI override (highest precedence, run mode only):

rustinel run --log-level debug

Note: rule logic evaluation errors are only logged at warn, debug, or trace levels (suppressed at info).

Hot reload

| Option | Default | Description | |--------|---------|-------------| | enabled | true | Enable local file-based hot reload for Sigma, YARA, and IOC inputs | | debounce_ms | 2000 | Debounce window for coalescing burst file changes before rebuild/swap |

Notes:

  • Poll cadence is max(reload.debounce_ms, 2000ms).
  • Empty reload results are rejected for safety (previous compiled engines remain active).

Active response

| Option | Default | Description | |--------|---------|-------------| | enabled | false | Enable active response engine | | prevention_enabled | false | If false, logs dry-run actions only | | min_severity | critical | Minimum severity to respond to (Sigma uses rule level, YARA is always treated as critical) | | channel_capacity | 128 | Queue size for response tasks (drops on overflow) | | allowlist_images | [] | Image basenames or full paths to skip | | allowlist_paths | inherits allowlist.paths | Prefix paths to skip (case-insensitive). Optional module-specific override |

More details: docs/active-response.md.

Atomic IOC detection

| Option | Default | Description | |--------|---------|-------------| | enabled | true | Enable atomic IOC detection | | hashes_path | rules/ioc/hashes.txt | Hash IOC file (MD5/SHA1/SHA256) | | ips_path | rules/ioc/ips.txt | IP and CIDR IOC file | | domains_path | rules/ioc/domains.txt | Domain IOC file | | paths_regex_path | rules/ioc/paths_regex.txt | Path/filename regex IOC file | | default_severity | high | Severity assigned to IOC alerts | | max_file_size_mb | 50 | Skip hashing files larger than this (MB) | | hash_allowlist_paths | inherits allowlist.paths | Prefix paths to skip hashing (case-insensitive). Optional module-specific override |


Rules

Sigma

  • Place .yml / .yaml files under rules/sigma/
  • Rules are compiled at startup and hot-reloaded when files change
  • Supported categories include: process_creation, network_connection, file_event, registry_event, dns_query, image_load, ps_script, wmi_event, service_creation, task_creation

YARA

  • Place .yar / .yara files under rules/yara/
  • Rules compile at startup and are hot-reloaded when files change
  • Scans trigger on process creation (runs in a background worker)
  • Files under allowlisted path prefixes are skipped (allowlist.paths by default or scanner.yara_allowlist_paths override)

Atomic IOCs

Place indicator files under rules/ioc/:

  • hashes.txt — MD5, SHA1, SHA256 hashes (auto-detected by

Related Skills

View on GitHub
GitHub Stars100
CategoryDevelopment
Updated7d ago
Forks16

Languages

Rust

Security Score

100/100

Audited on Mar 23, 2026

No findings