Monitors certificate transparency logs for AitM phishing infrastructure. Tracks both the well-known Duo-SSO-hijacking pattern (evilginx-style api-<duoid>. subdomains) and keyword-based identification for organizations that may not use Duo, or may only use it for some users. Uses a local certstream-server-go instance (via Docker) as the CT data source.
| File | Purpose |
|---|---|
targets.json |
Targeted organizations (Duo ID or keyword → name + email). Must be manually populated. |
.env.example |
Template for required environment variables. Copy to .env before running. |
email_template.example.txt |
Example email template. Copy to email_template.txt to customize. |
known_domains.txt |
Known attacker domains, so new certs on these are flagged high-confidence. |
expired_domains.txt |
Expired/unregistered attacker domains. Watcher silently skips these. Managed by expiry_checker.py. |
known_ips.txt |
Known attacker IPs, so low-confidence matches resolving here are upgraded. |
watched_org_ids.txt |
Optional. Org IDs (one per line) whose alerts are also sent to DISCORD_WEBHOOK_WATCHED. |
targets.json supports two entry formats:
Duo target (original format — entries with no "type" field):
{
"<duo id>": {
"name": "<university name>",
"email": "<university email>"
}
}Keyword target (for organizations that may not use Duo, or may only use it for some users):
{
"<keyword id>": {
"type": "keyword",
"name": "<university name>",
"email": "<university email>",
"keywords": ["<keyword1>", "<keyword2>"]
}
}typemust be"keyword"(Duo entries don't need this field — backwards compatible).keywordsis a list of strings to match as substrings within subdomain parts (case-insensitive). If omitted, the JSON key itself is used as the keyword.
- Clone the repository and navigate to the project directory.
- Create a
.envfile based on the.env.exampleand fill in the required environment variables.CERTSTREAM_WS_URL- WebSocket URL of your certstream server. With the included Docker setup on the same machine:ws://127.0.0.1:8080/
docker-compose up -dto start the certstream server.- (Optional) Create and activate a virtual environment:
python3 -m venv .venv source .venv/bin/activate - Install dependencies:
pip install -r requirements.txt
- Run the watcher script:
python watcher.py
If you prefer to run certstream-server-go as a standalone binary instead of Docker:
- Download the release binary from certstream-server-go releases.
- Run it with the included config:
./certstream-server-go -config config.yaml
The CERTSTREAM_WS_URL is the same either way: ws://127.0.0.1:8080/
The email template controls the body of both automated SMTP emails and the mailto links embedded in Discord alerts.
-
Copy the example and customize:
cp email_template.example.txt email_template.txt
-
The
{IOCS_LIST}placeholder is required — it gets replaced at runtime with defanged domains (up to 50) and non-CDN IPs (up to 20). -
(Optional) Set
AUTOMATED_EMAIL_DISCLAIMERin your.envto append a footer to every automated SMTP email. A default disclaimer is used if not set.
Attackers register domains and let them expire after their campaigns. When
expired domains get re-registered by unrelated parties, the watcher may
fire false-positive alerts. Use expiry_checker.py to periodically audit
your known domains and move expired ones to expired_domains.txt (which
the watcher silently skips).
python expiry_checker.py # check all domains, auto-move expired ones
python expiry_checker.py --dry-run # preview results without modifying filesAfter running, restart the watcher for the changes to take effect.
If a domain was moved by mistake, move it back from expired_domains.txt
to known_domains.txt and restart.
python -m pytest tests/ -vThese send real alerts to verify delivery. Skipped in CI by default.
# Set your test webhook/Apprise URLs
export TEST_DISCORD_WEBHOOK="https://discord.com/api/webhooks/..."
export TEST_APPRISE_URL="discord://webhook_id/webhook_token"
# Run integration tests
python -m pytest tests/test_integration_alerts.py -vApprise supports 80+ notification services. See Apprise docs for URL formats.