System Overview#
Infrastructure Diagram#

InfraGuard sits between the internet and your C2/phishing backends. IG Redirectors handle all inbound traffic — filtering, scoring, and proxying only legitimate requests to the Command Post or Decoy Server. Defenders see only the redirector layer; backend infrastructure is never directly exposed.
Request Flow#
Internet
│
▼ HTTPS (443)
┌──────────────────────────────────────────────────────┐
│ InfraGuard │
│ │
│ TLS Termination │
│ │ │
│ ▼ │
│ SNI/Host routing → Domain config lookup │
│ │ │
│ ▼ │
│ Pipeline Filters (ordered, scored) │
│ ├── IPFilter │
│ ├── BotFilter │
│ ├── HeaderFilter │
│ ├── GeoFilter │
│ ├── DNSFilter │
│ ├── ProfileFilter (C2/phishing profile match) │
│ ├── SandboxFilter (headless browser detection) │
│ ├── JA3Filter (TLS fingerprint) │
│ ├── ReplayFilter (dedup window) │
│ └── EnumerationFilter (path count per IP) │
│ │ │
│ ├── score ≥ threshold │
│ │ └── drop_action (redirect/proxy/404) │
│ │ │
│ └── score < threshold │
│ │ │
│ ▼ │
│ Content route match? │
│ ├── Yes → Guard stack → Backend │
│ │ (tokens, rate limit, UA check) │
│ └── No → Proxy to upstream C2/phishing │
└──────────────────────────────────────────────────────┘
│ │
▼ ▼
Upstream C2 Payload backend
(Cobalt Strike, (Mythic files,
Mythic, GoPhish...) PwnDrop, filesystem)Components#
Listeners (infraguard/listeners/)#
ASGI servers — one per listeners[] entry. Terminate TLS with the operator’s cert. Pass requests to the router via the domain map.
Router (infraguard/core/router.py)#
Core request handler. Domain lookup, filter pipeline construction, upstream proxy, content route dispatch.
Pipeline Filters (infraguard/pipeline/)#
Each filter is a subclass of BaseFilter. Returns FilterResult(action, score, reason). Filters are ordered; total score accumulates. First filter to return BLOCK in strict mode stops evaluation.
C2 Profiles (infraguard/profiles/)#
Parsers for each profile_type. Parse the profile file into a normalized C2Profile model (URI patterns, methods, headers, User-Agent). Used by ProfileFilter.
Intel (infraguard/intel/)#
Background tasks: threat feed refresh, CT monitor, reputation monitor, burn detector. Write to the shared DB and fire BurnIndicator events via the plugin dispatcher.
Tracking (infraguard/tracking/)#
SQLite-backed. Every request → requests table. Payload tokens → payload_tokens. Replay hashes → replay_tokens. Whitelist/blocklist in-memory with SQLite persistence.
Plugins (infraguard/plugins/)#
Receive RequestEvent objects from the router via EventRecorder. Built-ins: Discord, Slack, syslog. Custom plugins implement InfraGuardPlugin.on_event().
Integrations (infraguard/integrations/)#
Inbound webhooks from third-party platforms. phishingclub.py handles phishing.club HMAC-signed POST events.
Concurrency Model#
InfraGuard runs on asyncio. All filter evaluation and upstream proxying is async. SQLite writes use aiosqlite. Background tasks (intel monitors) run as asyncio.Task objects started in the ASGI lifespan.
Database Schema#
-- All inbound requests (passed and blocked)
CREATE TABLE requests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp INTEGER NOT NULL,
ip TEXT NOT NULL,
domain TEXT NOT NULL,
method TEXT NOT NULL,
path TEXT NOT NULL,
score REAL NOT NULL,
filter_result TEXT NOT NULL,
filter_reason TEXT,
user_agent TEXT,
metadata TEXT -- JSON blob
);
-- Payload download tokens
CREATE TABLE payload_tokens (
token TEXT PRIMARY KEY,
beacon_ip TEXT NOT NULL,
route_path TEXT NOT NULL,
issued_at INTEGER NOT NULL,
expires_at INTEGER NOT NULL,
max_uses INTEGER NOT NULL DEFAULT 1,
used_count INTEGER NOT NULL DEFAULT 0
);
-- Replay dedup hashes (when replay_persist: true)
CREATE TABLE replay_tokens (
hash TEXT PRIMARY KEY,
seen_at INTEGER NOT NULL
);Hot Reload#
Send SIGHUP (or docker compose kill -s HUP infraguard) to trigger config reload. InfraGuard:
- Re-parses the config file
- Rebuilds the domain map and filter pipelines
- Re-parses C2 profiles
- Preserves: SQLite DB, in-memory whitelist, running connections
In-memory filter state (replay window, enumeration counters, rate limit windows) resets on reload — this is acceptable since reloads are operator-initiated.