Adaptive BadUSB/HID attack emulation + behavioral endpoint detection with the Flipper Zero.
A wraith typing at superhuman speed through the keyboard interface — the invisible injected-keystroke attacker. KeyWraith uses the Flipper Zero's USB-HID capability to build, then defeat, a keystroke-injection detector.
Turn a Flipper Zero into a controlled keystroke-injection adversary to build the labeled dataset nobody has, then train and stress-test a behavioral BadUSB detector — an end-to-end HID purple-team platform. The deliverable is a detector + explainable endpoint alert; the offensive emulation exists only to generate honest training data and to red-team the defense.
The Flipper Zero can present itself to a computer as a USB keyboard and "type" a scripted payload at machine speed (the classic Rubber Ducky / BadUSB attack). The OS trusts keyboards implicitly, so this bypasses most host defenses. The offensive side is well-trodden. The defensive side — reliably detecting that "the user" typing right now is actually an injection device, grounded in real behavioral data rather than device-ID allow-lists — is surprisingly thin.
KeyWraith builds both halves: a reproducible HID-injection emulator that manufactures a labeled injected-vs-human keystroke dataset, and a behavioral detector (the logic an EDR agent would run) that flags injection in real time from timing dynamics and produces an operator-readable alert. Then it closes the loop: can an adaptive attacker slow down and humanize the injection to evade the detector, and at what cost?
- A labeled human-vs-injected keystroke dataset — including humanized attack variants — which is scarce. The methodology + dataset is itself a contribution.
- Behavioral BadUSB detection grounded in data, versus the usual device-ID/policy heuristics.
- The evasion arms race. Quantifying the detection-vs-humanization-cost tradeoff — how slow an attacker must go to look human, and whether that's still detectable — is a real research result, not just a classifier.
- Keystroke dynamics repurposed for defense, not biometrics. Usually studied for user authentication; here it distinguishes human vs machine injection.
flowchart TB
FZ["Flipper Zero<br/>BadUSB emulator<br/>(benign payloads)"] -->|HID keystrokes| HA
HA["Host agent<br/>keystroke capture<br/>(timing, keycode class)"] --> CAP
FZ -.->|emission log| CAP
CAP["Capture + auto-labeling<br/>(align to Flipper ground truth)"] --> FE
SYN["Synthetic stream generator<br/>human / burst / jittered / humanized"] --> FE
FE["Feature extraction<br/>(IKI dist, cadence regularity,<br/>dwell, digraphs, corrections)"] --> DET
ATK["Adaptive / humanized attacker"] -->|red-team| DET
DET["Behavioral detector<br/>human vs injected"] --> ALERT["Endpoint alert +<br/>explanation (EDR-style)"]
DET --> ARMS["Evasion cost study<br/>(arms race)"]
Full module map in docs/architecture.md.
# 1. install (Tier 1 needs no hardware, no human data)
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# 2. end-to-end demo: trains the detector and prints live endpoint alerts
python -m keywraith.cli demo
# 3. regenerate every result artifact (metrics, figures, summary table)
python -m keywraith.cli report --outdir resultsOr with make: make setup && make demo && make report. Run the tests with
make test (30 tests).
Individual stages:
python scripts/generate_dataset.py --out data/sessions.ndjson --csv data/features.csv
python scripts/train_detector.py --model random_forest --out models/detector.pkl
python scripts/evaluate.py --out results/metrics.json
python scripts/evasion_arms_race.py --out results/arms_race.json
python scripts/make_figures.py --outdir figuresMore CLI commands:
python -m keywraith.cli adversarial # evasion-aware retraining vs a standard detector (Tier 3)
python -m keywraith.cli stream # real-time sliding-window detector: catch rate + time-to-detection
python -m keywraith.cli train --model gradient_boosting # alternative modelKeyWraith is scoped so the full research story runs in software, with hardware as an optional upgrade.
| Tier | Needs | What it delivers |
|---|---|---|
| 1 — software (default, in this repo) | Python only | Synthetic generator → features → detector → explainable alert, benchmarked against a naive speed-threshold baseline across humanization levels. A complete, novel result with no Flipper. |
| 2 — hardware + human data | Flipper Zero, consenting typists, pynput |
Wire in real injection (keywraith.host_agent + benign payloads, auto-labeled by keywraith.labeling) and real human captures; measure the sim-to-real gap. Same feature/detector code. |
| 3 — research payoff | Tier 1 | The adaptive attacker and evasion arms race: sweep humanization, plot detection vs attacker throttle cost, test whether an evasion-aware retrained detector wins the round. |
Regenerate with python -m keywraith.cli report. Full numbers in
results/results_summary.md and
results/metrics.json.
Detection quality — behavioral detector vs the standard speed-threshold defense:
Why it works — inter-key timing separates human from each injection mode:
The evasion arms race (headline figure) — the detector holds full detection through humanization level 0.9 while the naive baseline collapses once the attacker slows down; full evasion costs the attacker a ~40× longer exposure window:
Detection recall per injection mode — the baseline is defeated by humanization; the behavioral detector is not:
Winning the next round — evasion-aware retraining. A standard detector that only trained on weak humanization collapses once the attacker humanizes past ~0.5. An evasion-aware detector that also trained on strong humanization reclaims the lost region — up to 0.95 detection at humanization level 0.9 — with no increase in human false positives (both stay at FPR ≈ 0.00). Only full humanization (level 1.0, statistically indistinguishable from a human, at maximum attacker time cost) defeats both:
| Model | ROC-AUC | Precision | Recall | FPR |
|---|---|---|---|---|
| Behavioral detector | 1.000 | 1.000 | 1.000 | 0.000 |
| Speed-threshold baseline | 0.997 | 0.995 | 0.951 | 0.004 |
On the fully-synthetic Tier 1 data the behavioral detector is near-perfect; the honest, interesting result is the arms-race curve, where it maintains detection long after the speed baseline is blind, and the cost the attacker pays to eventually evade it. Absolute numbers will move on real hardware — that sim-to-real gap is measured, not hidden.
The defensive deliverable is an operator-readable alert. From keywraith.cli demo
on a humanized injection (full sample):
========================================
KEYWRAITH ENDPOINT ALERT [CRITICAL]
========================================
verdict : injected (P(injected)=1.000)
summary:
INJECTED keystroke stream: 240 keys in 40.49s (speed 5.9 cps), IKI CV 0.191
vs human median 0.55, 0.0% corrections, 0% of keystrokes <20ms apart.
top contributing features:
- dwell_std_ms = 1.25 (pushes-injected)
- dwell_mean_ms = 9.42 ms (pushes-injected)
- dwell_cv = 0.13 (pushes-injected)
- iki_iqr_ms = 43.97 (pushes-injected)
- typing_speed_cps = 5.93 (pushes-human) <- already looks human-paced
recommended action:
Isolate the host session and review the connected HID device; correlate with
USB enumeration logs at alert time.
========================================
Note how at humanization level 0.6 the speed already looks human (5.9 cps, a 40-second session) — a speed threshold is blind here — yet the machine-regular dwell time and low cadence variance still give the wraith away.
Beyond scoring whole sessions, keywraith.stream.StreamDetector
runs the detector the way an EDR would — over a sliding window of the most
recent keystrokes, with alert debouncing so a single noisy window doesn't fire.
Feed it live events one at a time (push) or scan a session offline
(scan_session); the two paths are exactly equivalent. On the Tier 1 data it
catches injection with a median time-to-detection of ~50 keystrokes, at a 0%
human false-alarm rate (python -m keywraith.cli stream).
19 timing/cadence features, chosen because human and injected typing separate
along them. The scale-free regularity features (iki_cv, iki_autocorr_lag1,
dwell_cv, digraph_var_ratio) are what keep the detector alive when an attacker
simply slows down to beat a speed threshold. Full table:
docs/feature_set.md.
- Detection quality: ROC/PR and false-alarm rate vs a speed-threshold baseline.
- Robustness: performance across injection modes and unseen humanization levels.
- Sim-to-real: does a detector trained on synthetic hold up on shifted / real captures? Quantified (Tier 1 ships a shifted-profile proxy; Tier 2 uses real data).
- Adversarial: the evasion arms race — attacker cost to evade, and whether retraining recovers detection.
- False positives on real humans: fast/metronomic typists are the key deployability risk and are measured explicitly.
Software-security posture (project responsible-use rules are in the threat model):
- Tamper-evident models. scikit-learn models are pickles, and unpickling runs
code.
BehavioralDetector.save()writes a.sha256sidecar;.load()verifies it by default and refuses a tampered, corrupt, or unverifiable model, and refusestrusted=False. Integrity, not authenticity — still only load models you trust. - Input validation on every prediction (shape + finite-value checks).
- No network I/O, no secrets, no untrusted deserialization of data (datasets are plain numeric NDJSON/CSV). Raw human captures are git-ignored.
- CI + CodeQL + Dependabot with least-privilege
GITHUB_TOKENpermissions (.github/). SeeSECURITY.mdfor reporting.
Defensive framing is central and binding. Injection is performed only against
hosts the author owns; the repo ships no weaponized payloads (DuckyScripts are
limited to benign, clearly-labeled marker strings), no turnkey attack tooling, and
no real credentials. Human data is consented and stored only as de-identified
timing features, never raw text. Read docs/threat_model.md
before running any hardware.
keywraith/ core library (importable, tested)
synthetic.py human/machine keystroke stream generator
attacker.py adaptive humanized attacker + evasion cost
features.py behavioral feature extraction (19 features)
detector.py train / predict / explain / tamper-evident persist
baseline.py naive speed-threshold defense
alert.py EDR-style explainable endpoint alert
stream.py real-time sliding-window detector (online + offline)
host_agent.py Tier-2 live keystroke capture (optional pynput)
labeling.py Tier-2 emission-window auto-labeling
experiments.py train / evaluate / arms-race / adversarial / sim-to-real
figures.py all figures
cli.py command-line interface
scripts/ thin CLI wrappers for each stage
flipper/payloads/ benign, clearly-labeled DuckyScript test payloads
docs/ threat model, feature set, dataset card, architecture
tests/ pytest suite (30 tests)
.github/ CI, CodeQL scanning, Dependabot
data/ de-identified feature CSV + small session sample
figures/ results/ generated artifacts (regenerable)
Flipper Zero BadUSB / DuckyScript documentation · HID keystroke injection (Rubber Ducky) attacks · keystroke-dynamics feature literature · USB/HID security and BadUSB mitigations · behavioral endpoint detection (EDR) concepts · adversarial evasion of behavioral detectors. This work sits where USB/HID hardware attacks meet behavioral ML detection meet endpoint defense.
See CITATION.cff.
Krishita Sanjay Choksi (@Krishita17) — sole author and contributor. Released under the MIT License.




