Outsider Stage 0.5
Replay and verify a real, sealed Outsider Stage 0.5 intervention chain.
Translation pending
Self-hosted Windows EDR focused on infostealers: install, build, deploy and operate.
Translation pending
SecurityRust · TypeScriptGitHub @lawbyteOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Self-hosted Windows EDR for infostealers, ransomware, credential dumping and persistence. Rust agent with on-device detection and containment, web console, remote response.
Taken from the repository README
Loading the full README.
Read the full README ↗Kestrel is a self-hosted endpoint detection and response (EDR) product for Windows, built to catch infostealers (Lumma, Vidar, StealC, Rhadamanthys and similar malware) while they work. These programs read browser passwords, cookies, crypto wallets and messenger sessions, then upload them. Kestrel watches for exactly that behaviour, contains the process, and gives IT one incident that says what was taken and what to rotate.
Since v1.1 it also covers the steps around a hands-on intrusion: ransomware preparation and encryption, credential dumping (LSASS, hives, NTDS) and persistence (Run keys, Startup, tasks, services, WMI, IFEO, Winlogon, COM), plus a curated SigmaHQ pack in Audit.
It has four parts:
kestrel-agent): a Windows service written in Rust. It reads ETW sensors, runs the detection engine on the device, and contains threats.kestrel-server): one Docker Compose stack per organisation, with PostgreSQL and optional ClickHouse. All telemetry stays on your own infrastructure.TP-06 alert and an audit entry, and IT can turn it back on from the console).Status: v1.1.7 (release candidate), user-mode only. Validated on Windows 11 (26100) in a disposable lab VM. Read the limits section before you rely on it.
Console screenshots use the built-in demo data (npm run dev:mock). The tray and remote-desktop images marked "real" come from a Windows 11 lab VM. Every console page, in light and dark, is in the console tour.
Detection on the device. 55 built-in behavioural rules: stealer (ST-01…ST-16), ransomware (RW-xx), credential dumping (CD-xx), persistence (PS-xx) and tamper (TP-01…TP-06), plus 70 SigmaHQ rules in Audit. They run in the agent, so detection and containment still work while the device is offline.
Suspend on suspicion. When an untrusted process opens a credential store or decrypts browser secrets with DPAPI, the agent freezes it for a short window (3 s by default). It kills the process if a stronger rule confirms the theft, and resumes it otherwise.
Response actions. These can be automatic (rules in Protect mode) or issued by an analyst from the console:
isolation_auto_release_hours without server contact, default 72 h; local break-glass kestrel-agent release-isolation --token …)Actions issued while a device is offline are queued and delivered when it reconnects. They expire after 7 days.
Canary credential stores. The agent plants decoy browser profiles of defunct Chromium forks that stealers enumerate (Kometa, 7Star: Local State, Login Data, a fake MetaMask vault) and decoy .aws\credentials. It never plants under a mainstream browser, so it does not make Chrome look installed to other browsers' import wizards. ST-16 fires only when secret material in a decoy is read (not directory listings or incidental files such as profile pictures) by anything other than the agent or an allow-listed, validly signed security/search/backup tool. In Protect mode it kills the reader, blocks its network and quarantines it; the host is isolated only when the reader is unsigned and runs from a user-writable folder. A genuine browser or password manager installed under Program Files (valid signature by its vendor, no remote-debugging / headless / extension-loading switches, never injected into) may be importing data and gets an Audit alert only; per-user installs and every other reader get the full response. There is no general "never kill this app" list: false positives are fixed per rule.
Hardening pack. A policy sets the pack's mode (Off, Audit or Enforce) and switches each control on or off:
TP-* rules and the agent's watchdog detect and repair much of this, but a user-mode agent cannot fully defend itself against an admin.| Id | Rule | Default |
|---|---|---|
| ST-01 | Credential-store read by untrusted process | Audit |
| ST-02 | Smash-and-grab credential collection (≥ 3 secret categories in 60 s) | Protect |
| ST-03 | Foreign DPAPI unprotect of browser secrets | Audit |
| ST-04 | Steal-then-send (credential read, then external connection within 300 s) | Protect |
| ST-05 | Browser debugging abuse (--remote-debugging-* with headless/alt profile) | Audit |
| ST-06 | Local CDP client (non-browser connects to the DevTools port) | Audit |
| ST-07 | Browser process tampering (memory access, injection, debugger attach) | Protect |
| ST-08 | Chrome elevation-service abuse | Audit |
| ST-09 | Foreign crypto library load (nss3.dll) | Audit |
| ST-10 | ClickFix Run/Address-bar artefact (RunMRU / TypedPaths) | Audit |
| ST-11 | Paste-and-run process chain | Audit |
| ST-12 | Untrusted execution from user paths | Audit |
| ST-13 | .NET LOLBin hollowing | Audit |
| ST-14 | Dead-drop / exfil service from non-browser (Telegram, Steam, Discord webhooks, …) | Audit |
| ST-15 | Trusted-binary sideload from user path | Audit |
| ST-16 | Canary tripwire | Protect |
| TP-01 | Telemetry session tampering | Audit |
| TP-02 | WFP filter tampering | Audit |
| TP-03 | AMSI tampering | Audit |
| TP-04 | Agent service or file tampering | Audit |
| TP-05 | Telemetry loss | Audit |
| TP-06 | Protection turned off on the device (tray switch, uninstall token) | Audit (never acts) |
Ransomware, credential dumping and persistence (v1.1):
| Id | Rule | Default |
|---|---|---|
| RW-01 | Shadow copy / backup catalog deletion | Protect |
| RW-02 | Boot recovery disabled | Protect |
| RW-03 | Safe-mode boot staging | Protect |
| RW-04 | Event log cleared or shrunk | Audit |
| RW-05 | Service / process kill spree | Protect |
| RW-06 | Mass file encryption behaviour | Protect |
| RW-07 | Ransomware canary tampered | Protect |
| RW-08 | Ransom-note fan-out | Protect |
| RW-12 | Ransomware kill-chain correlation | Protect |
| CD-01 | LSASS memory dump via comsvcs MiniDump | Protect |
| CD-02 | Known LSASS dumping tool | Protect |
| CD-03 | Suspicious LSASS handle request | Audit |
| CD-04 | LSASS handle followed by a dump file | Protect |
| CD-05 | Debug library dump helper reading LSASS | Protect |
| CD-06 | Blocked LSASS access attempt | Audit |
| CD-07 | LSASS dump preparation via SilentProcessExit / IFEO | Protect |
| CD-08 | LSASS dump via Windows Error Reporting | Protect |
| CD-09 | Malicious LSA security package | Audit |
| CD-10 | Credential hive save or copy | Protect |
| CD-11 | NTDS.dit extraction | Audit |
| CD-12 | WDigest cleartext credential downgrade | Protect |
| CD-13 | LSA protection / Credential Guard tamper | Audit |
Persistence rules in Protect remove the autorun, after backing up the original in the quarantine vault. They act on the writing process only when Kestrel knows exactly which process wrote it. The 70-rule SigmaHQ pack (SIG-<uuid8>) is seeded in Audit; the console shows each rule's authors and the DRL 1.1 licence.
The Protect rules are the "safe on day one" set. Run everything else in Audit for about two weeks first. The console's Rules page shows each rule's logic, its ATT&CK mapping, its Protect action and known false positives.
More diagrams: detection flow, remote-access ceiling and deployment (HTML sources next to them in docs/diagrams/).
For data flow, the detection pipeline and the security model, see docs/ARCHITECTURE.md.
The illustrated, step-by-step version of this section, with a screenshot for every step, is the Step-by-step install guide.
git clone https://github.com/lawbyte/kestrel-edr.git
cd kestrel-edr/deploy
cp .env.example .env
nano .env # set the four values below
mkdir -p installers && cp /path/to/KestrelAgent.msi installers/
docker compose up -d --build
docker compose logs kestrel-server | grep "SETUP CODE"
.env setting | Value |
|---|---|
KESTREL_MASTER_KEY | openssl rand -base64 32. Keep a copy outside the server. |
KESTREL_PUBLIC_HOSTNAME | The name or IP your PCs use to reach the server. The first one in the list goes into the installers. |
POSTGRES_PASSWORD, CLICKHOUSE_PASSWORD | Long random values. |
KestrelAgent.msi comes from the kestrel-windows artifact of the latest green CI run, or from building it yourself. PCs reach the server on TCP 443 (console, installer download) and 8443 (agents).
Open https://<your server>/, accept the self-signed certificate warning, and enter the setup code, your organisation name and the first administrator account.
Install-Kestrel-<profile>.cmd.Add-MpPreference -ExclusionPath 'C:\Program Files\Kestrel'..cmd and approve the administrator prompt, or deploy it with Group Policy, Intune or an RMM as Install-Kestrel-<profile>.cmd /quiet.The simplest way to uninstall is Remove device on the device page (danger zone): the PC uninstalls itself, now or when it next connects. Locally, uninstalling needs the device's uninstall token (device page, Uninstall token, Reveal; the reveal is audit-logged). On the PC, open Start menu > Kestrel EDR > Uninstall Kestrel EDR, paste the token and click Uninstall; for scripts, msiexec /x <product code> /qn UNINSTALL_TOKEN=<token>. See Enroll devices for mass deployment, upgrades and uninstall, and Deploy the server for TLS, backups, upgrades and sizing.
To try everything on Linux without Windows, run the end-to-end test:
# Linux: build, then run the full end-to-end test (server + 3 simulated endpoints)
./scripts/e2e.sh # starts a throwaway Postgres if DATABASE_URL is unset
E2E_KEEP=1 ./scripts/e2e.sh # ...and leave the server up at https://127.0.0.1:18443
It replays recorded attacks (crates/kestrel-agent/replays/*.jsonl) through the real agent core and the real server. It then checks the incidents, data at risk, response-action delivery, the audit chain and hunting.
To work on the console, the tray and Windows builds:
cd console && npm run dev:mocknpm run dev. It proxies /api.cd tray && npm run dev:mockcargo wincheck -p kestrel-agent (see docs/BUILDING.md)scripts/windows-smoke.ps1 checks that the ETW sensors produce eventsscripts/vm/ builds a QEMU/KVM Windows 11 guest inside WSL2 and runs the benign emulations (scripts/vm/emulate.ps1); see docs/TESTING-ON-WINDOWS.md. Never run the agent's response actions on your own PC..github/workflows/ci.yml defines these checks:
cargo fmt --check, clippy -D warnings and cargo test --workspacescripts/e2e.sh and the server imagecargo deny, no copyleft)The workflow has not run on GitHub yet because no remote is configured. The same checks were run locally for v1.1.0-rc1 (see docs/HANDOFF.md).
crates/
kestrel-common shared types: events, alerts, policy, response actions, wire + tray protocols
kestrel-detect detection engine: rules ST/RW/CD/PS/TP, process table, IOCs, exclusions, Sigma, canaries
kestrel-platform Platform trait (sensors, response, hardening, canaries, tray) the agent runs on
kestrel-win Windows implementation: ETW, enrichment, WFP, quarantine vault, AppLocker, service, tray pipe
kestrel-agent agent binary: enrollment, mTLS transport, SQLite outbox, engine loop, privacy filter,
simulator (SimPlatform) + replays/, signed updates
kestrel-scan on-demand YARA-X scanner (separate process, kept out of the always-running agent)
kestrel-msica MSI custom actions (tokens go to the agent on stdin, never on a command line)
kestrel-uninstall "Uninstall Kestrel EDR" window (Win32): asks for the uninstall token, removes the product;
--quiet --token-stdin mode for Remove device (started by the agent)
kestrel-server axum REST + tonic gRPC, internal CA, Postgres/ClickHouse, incidents, RBAC, audit, notifications, agent updates
console/ React admin console (Vite); `npm run build` → console/dist (served by the server)
tray/ Tauri 2 employee tray app (own Cargo workspace in tray/src-tauri)
proto/ kestrel.proto (agent ↔ server gRPC)
deploy/ Dockerfile, docker-compose.yml, .env.example, operations README
installer/ Kestrel.wxs (WiX v6 MSI)
scripts/ e2e.sh (Linux end-to-end), windows-smoke.ps1 (Windows ETW smoke test), vm/ (disposable Windows lab)
docs/ contracts.md, ARCHITECTURE.md, BUILDING.md, TESTING-ON-WINDOWS.md, HANDOFF.md, DECISIONS.md, research/, screenshots/
TP-SRV-HEARTBEAT (missed heartbeats), TP-SRV-SEQ-RESET (outbox sequence reset), TP-SRV-OUTBOX-RESET (outbox database recreated), TP-SRV-EVENTS-LOST, TP-SRV-ETW and TP-SRV-DUP-ENROLL (hardware UUID reused by a new enrollment). Cross-device correlation is not implemented.kestrel-agent update-keygen / update-sign); none is configured out of the box.v1.1 review. A second independent review of the v1.1 code (docs/security-review-v1.1-2026-10-21.md) found 0 critical, 10 high, 12 medium and 18 low issues. They included:
Two fix rounds followed, and a fresh re-review confirmed the fixes. The residual risks are listed in docs/HANDOFF.md.
v1.0 review. An independent review on 2026-10-21 (docs/security-review-2026-10-21.md) found 0 critical, 10 high, 12 medium and 12 low issues. Unit and integration tests and scripts/e2e.sh cover the fixes. The Windows-only parts were also run in the lab VM.
| Finding | Status | Notes |
|---|---|---|
| H1 Process made unkillable by name or path | Fixed | One shared protected list: exact path on the system volume + valid Microsoft signature; PathClass is volume-aware |
H2 Identity takeover via hw_uuid | Fixed | Enrollment always creates a new device; duplicates flagged (TP-SRV-DUP-ENROLL) and merged / dismissed by an admin |
| H3 Certificates never renewed | Fixed | Renewal over the mTLS stream with atomic swap and rollback; the server asks when < 30 days remain |
| H4 Pre-created state directory | Fixed | Owner and DACL verified at install, enroll and every start; untrusted directories moved aside |
| H5 OIDC login by email match | Fixed | Bound by (issuer, sub) only, email_verified required, admins link accounts explicitly |
| H6 Empty OIDC domain list fail-open | Fixed | Empty list denies; default role can never be an admin role |
| H7 First run unhashed / unsigned | Fixed | First-run images held ≤ 750 ms for hash + signature; late facts re-run IOCs and rules; Unknown never trusted |
| H8 Quarantine / restore follow junctions | Fixed | No reparse points or extra hard links, final-path check, hash before ACL changes, handle-relative restore |
| H9 AppLocker allow-all over GPO policy | Fixed | Effective policy inspected; GPO/MDM-managed policies never touched |
| H10 Isolation can strand a host | Fixed | Permits re-planned every minute, server permit agent-only, auto-release (isolation_auto_release_hours) and release-isolation break-glass (lab-verified). While isolated, DNS is allowed only for the agent (image + service SID) |
M1 Exclusions ignore the drive / * spans folders | Fixed | Drive kept; * within a segment, ** across |
| M2 Review approval creates path exclusions | Fixed | sha256 or valid-signer exclusions only, for the alerted rule, 90-day expiry |
| M3 Tray pipe abuse | Fixed | Requests attributed to the pipe client's token; per-user activity; rate limits |
Apache-2.0. See Cargo.toml (license = "Apache-2.0"). The dependency licence policy is in deny.toml. GPL, AGPL, SSPL and ELv2 dependencies are rejected.
crates/kestrel-detect/src/catalog.rs and on the console's Rules page.author as rule_author on every alert and shows it in the console. The server recognises DRL/SigmaHQ rules by their license and references fields.rsigma-parser/rsigma-eval (MIT). ETW uses ferrisetw. On-demand scanning uses YARA-X (BSD-3-Clause).jpeg-encoder (MIT OR Apache-2.0). This software is based in part on the work of the Independent JPEG Group.mshta and for executables in Temp/DownloadsRemoteDebuggingAllowed=falseIncidents, not alert floods. The server groups alerts by device and process tree into incidents. Each incident gets:
Evidence of what was taken. When ST-02 or ST-04 fires, the agent keeps a sealed evidence bundle on the PC: copies of the files the process staged (for example Passwords.txt or a zip), the list of secrets it read, copies of those secrets, and a packet capture (.pcapng) of its connections. The incident shows the evidence in plain language (which stores were read, what was decrypted with DPAPI, where data was sent). An admin with evidence.download collects the bundle and downloads it as an AES-256 zip whose password is shown once. Every step is audited, the employee sees a tray notice, and bundles expire after 14 days by default. The policy switches each part on or off.
Measured false-positive rate and coverage. Every build replays 127 malware behaviour scenarios, 16 benign look-alikes and 47 anonymised slices of a real workstation through the engine. The build fails if any malware scenario is missed or any benign case alerts beyond what it allows (docs/DETECTION-QUALITY.md).
Hunting. A small filter language over telemetry, for example event_type = dns_query and dns_qname ~ telegram. Telemetry is stored in ClickHouse, or in Postgres for small sites.
Custom Sigma rules. Rules are compiled and validated by the server, then pushed to agents and evaluated there with rsigma.
Operations features:
admin, analyst and readonlyRemove device (remote uninstall): an admin removes Kestrel from a PC from the console. The agent checks the device's uninstall token (sent by the server only with the command, never shown or stored with it), tells the employee in the tray and uninstalls itself through Windows Installer; PCs that are offline do it when they next connect. The device then leaves the device list (history kept under "Show removed devices") and its certificates are revoked a week later (a PC whose uninstall did not finish can still report back until then). Admin only, interactive console session only, typed hostname, MFA step-up when the admin has MFA, at most 20 removals per admin per hour, every step in the audit log. Optionally also deletes quarantined files and logs on the PC.
Admin remote access: remote desktop and a remote file manager, from the device page of the console.
Install-Kestrel-<profile>.cmd with one of three remote-access profiles:
Privacy controls for PDPA (Singapore) and UU PDP (Indonesia).
SoftwareSASGeneration allows services to send it.| PS-01 | Run key to untrusted payload | Audit |
| PS-02 | Autorun via script host or LOLBin | Protect |
| PS-03 | Startup-folder drop | Protect |
| PS-04 | Startup folder redirection | Audit |
| PS-05 | Scheduled task with untrusted action | Audit |
| PS-07 | Suspicious service install | Protect |
| PS-08 | WMI permanent event subscription | Protect |
| PS-09 | IFEO / SilentProcessExit hijack | Protect |
| PS-10 | Winlogon / AppInit / AppCert / BootExecute change | Protect |
| PS-11 | COM hijack in user hive | Audit |
| PS-15 | Persistence rollback | Protect |
| M4 Login lockout / spoofable IP key |
| Fixed |
Backoff counted on failures only; X-Forwarded-For only from KESTREL_TRUSTED_PROXIES |
| M5 Uninstall token never escrowed | Fixed | Server-issued at enrollment, audited reveal, rotation over the wire (SetUninstallToken), hidden from MSI logs |
| M6 Enrollment tokens leak via command lines | Fixed (residual) | The MSI custom action (kestrel-msica) passes tokens to the agent on stdin, and none appear in the verbose MSI log (lab-verified). Local process monitors can still see the admin's own msiexec … TOKEN= command line |
| M7 Future heartbeat disables processing | Fixed | Dedup by seq, timestamps clamped to server time |
| M8 Outbox reset discards messages | Fixed | Hello.outbox_epoch; a new epoch resets the high-water mark and raises TP-SRV-OUTBOX-RESET |
| M9 Incomplete privacy minimisation | Fixed | Health, review and alert users hashed; alert process, ancestry and summaries scrubbed like events |
| M10 Permanent host-wide IOC blocks, any CIDR | Fixed | Wide / private ranges need confirmation, ≤ /8 refused; blocks per offending image with a TTL (ioc_block_ttl_secs); deleting an IOC lifts the blocks it caused |
| M11 ST-16 auto-isolates on any reader | Fixed | Signed backup / AV / indexer readers ignored; isolation only for unsigned readers in user-writable paths |
| M12 No revocation checks | Fixed | Revocation checked in the background (cache, then online); revoked signers become Invalid |
| L1 TOTP replay, password oracle | Fixed |
L2 API tokens on /me, no expiry | Fixed |
| L3 Unauthenticated first-run setup | Fixed | One-time setup code (KESTREL_SETUP_CODE or server log) |
| L4 OIDC start amplifier | Fixed |
| L5 Sessions and SSE not cut off | Fixed |
| L6 Audit chain rewritable | Fixed | HMAC chain keyed from the master key, head anchored to log / syslog |
| L7 Notification SSRF / injection / exposure | Fixed |
| L8 Review requests for other devices' alerts | Fixed |
| L9 ST-11 kills the parent shell | Fixed |
| L10 Exclusions without guardrails, silence TP rules | Fixed |
| L11 Different kill-safety lists | Fixed |
| L12 Canary planting follows junctions | Fixed |
| Info: update manifest signs only the hash | Fixed | Signature over kestrel-update-v1\n{version}\n{url}\n{sha256} |
Info: monotonic policy.version after a DB restore | Fixed | The server raises its policy versions to the highest version agents report (bounded) and raises TP-SRV-VERSION |