Tycoon 2FA — AiTM Phishing Detection
A research prototype that detects Tycoon 2FA and other adversary-in-the-middle phishing email. It reads raw RFC-822 messages from a Gmail mailbox in read-only mode, runs them through four independent detection layers, and combines the result into a single composite risk score with a deliver, warn or block verdict.
This is a research prototype at repository-bootstrap stage. The architecture, data contracts and scoring design are specified in full and core/models.py is implemented, but the detection layers themselves are not yet built — there are no measured results to report and no detection capability to claim. It is read-only and takes no remediation action.
The Problem
Adversary-in-the-middle phishing kits defeat multi-factor authentication by proxying the real login page in real time, so the victim's second factor is captured along with their password. The messages that deliver these kits are engineered to survive conventional filters: the payload URL hides behind redirect chains, CAPTCHA gates and QR codes, and the body is obfuscated well enough to pass keyword matching. A verdict from such a filter is also a bare number, which is useless to an analyst who has to justify why a message was blocked.
Detection layers
Four layers examine the message independently, each emitting signals with human-readable evidence rather than an opaque score. Because no layer is authoritative on its own, a message that looks clean to one can still be caught by another.
| Layer | Focus |
|---|---|
| L1 — Header & domain intelligence | Authentication results, Reply-To mismatch, domain age, display-name impersonation |
| L2 — URL & redirect chain | Redirect depth, base64-encoded email parameters, CAPTCHA gating, QR-embedded URLs |
| L3 — NLP / ML body analysis | Unicode obfuscation, urgency classification, perplexity and burstiness |
| L4 — Threat intelligence | URL and domain IOC lookups, hosting reputation correlation |
Explainability as a hard requirement
Every signal is a frozen DetectionSignal record carrying its layer, a layer-local name, a normalized 0–1 score, a severity, and an evidence string that is validated non-empty. The evidence is the point: a bare score demonstrates nothing to the analyst reading the verdict, so the UI renders those strings directly. A signal records what was observed and is never edited afterwards.
- One generic signal model shared by all four layers, with no per-layer subclasses
- Qualified names such as L2/redirect_depth identify a signal's origin
- An error field records signals that could not be computed, rather than silently dropping them
- Generic grouping and filtering by layer is what makes the planned ablation study possible
Scoring design
Weights live in configuration, never on the signals themselves and never hardcoded. A signal is an observation; how much that observation counts toward the composite is a policy decision owned by the scoring module. Keeping the two separate means refitting the model changes one YAML file rather than every layer, and the same recorded signals can be rescored under different weightings.
- Phase A uses hand-assigned weights — L1 0.30, L2 0.30, L3 0.20, L4 0.20
- Phase B fits a logistic regression over the full signal vector on a labelled corpus
- Thresholds are re-derived from the ROC curve rather than guessed
- The operating point weights false positives more heavily than false negatives — a blocked legitimate invoice is a business incident, a missed phish is one more item for the next control
Risk and action are separate stages
The system keeps two vocabularies deliberately distinct. Composite scoring produces a risk level — low, medium or high — answering how risky a message is. A separate policy stage maps that classification onto an operational action of deliver, warn or block, answering what should be done about it. The mapping is a deployment policy, not a property of the message, so the two are never conflated.
Ingest
Messages are pulled from Gmail strictly read-only. Nothing is moved, labelled, quarantined or deleted, and the prototype is not publicly deployed. Design goals throughout are defensibility and reproducibility — every claim the README makes is intended to be backed by a measurement the evaluation harness produces.
Next Project
SkinAIBot — Medical AI Diagnosis Platform