~/sabarish.dev
← all posts

~/posts/sih-2026-email-forensics

SIH 2026: AI Email Threat Detection & Forensic Intelligence

Sabarish R5 min read
  • sih-2026
  • hackathon
  • ai-ml
  • security
  • email-forensics

For Smart India Hackathon 2026, my team (AICTE problem statement) built a platform that attacks email threats from two directions at once: detect the bad email the moment it lands, and prove it with sender-level forensics that a security team can actually use.

This is the post-mortem — the stack, the pipeline, the real numbers, and what I'd do differently next time.

The problem

Phishing, business email compromise (BEC) and malware-laden email are still the cheapest, most effective way into an organization. Most filters only tell you "this is malicious, score: 87" — with no way to explain the verdict, see where the sender actually was, or export evidence for an incident report.

So the brief we chased was: detect it, explain it, and hand over forensics.

The pipeline

1. Detection — an adaptive ML ensemble (the real numbers)

We trained on 7 curated datasets totaling ~34,000 emails and leaned on an ensemble instead of a single model:

  • XGBoost (400 trees, depth 7) — 97.39% accuracy, AUC 0.9957
  • LightGBM (500 trees, 63 leaves) — 97.56% accuracy, AUC 0.9959
  • DistilBERT (fine-tuned, 3 epochs) — 98.92% accuracy, AUC 0.9991
  • Weighted fusion: 0.6·bert + 0.2·xgb + 0.2·lgb when BERT is available, falling back to 0.5·xgb + 0.5·lgb when it isn't

The backend auto-detects whether DistilBERT is installed and degrades gracefully — the free-tier Render deployment runs XGB+LGB at ~200 MB RAM, the full ensemble needs the ~600 MB tier. Same API either way.

On top of 30,000 TF-IDF features (n-grams 1–3) we engineered 19 handcrafted signals — caps ratio, suspicious TLD detection, credential-harvesting phrase counts, urgency language, IP-in-URL flags — appended to the sparse matrix so the tree models see structure, not just vocabulary.

2. Training data — poisoning the poisoners

The dataset mix is the part I'm proudest of. Beyond the Kaggle corpora, we included 1,000 human-crafted phishing emails and 1,000 LLM-generated phishing emails (plus matching legitimate sets from both sources).

Why: classic keyword filters are trained on template phishing. Modern spear-phishing is written by LLMs — evasive, well-grammar'd, personalized. If your model has never seen AI-crafted attacks, it will bless them. Ours was trained on them.

(Also a lesson in data engineering: llm_phishing.csv had unquoted commas in email text, so the Colab notebook ships a custom CSV parser that splits on the last comma only — plus fixes for four deprecated PyTorch/NLTK/XGBoost API calls.)

3. Intelligence fusion — a weighted six-signal risk score

A single model is easy to fool, so the risk score is a weighted composite of six signals (0–100): ML verdict, threat intel, email authentication, sender geolocation, forensic findings, and content analysis.

risk = 0
for source, weight in SOURCES:      # VirusTotal, Safe Browsing,
    risk += weight * source.score   # PhishTank, RDAP, AbuseIPDB
risk = clamp(risk, 0, 100)

Each source contributes a weighted vote, so a URL that VirusTotal flags but Safe Browsing hasn't seen yet still moves the needle — just not as far as one everyone agrees on.

4. Forensics — the part filters never do

  • Header forensics: SPF, DKIM and DMARC analysis on every email, plus a custom parser for the authentication results
  • Sender geolocation: per-hop tracing of the Received chain instead of trusting the From header — MaxMind GeoLite2 with an ipapi.co fallback
  • Live threat map (Leaflet.js) for the SOC view, and a threat-intel dashboard (Chart.js) tracking phishing trends, auth-failure rates and a country heatmap
  • Two intake paths: scan a Gmail inbox via OAuth 2.0, or drag-and-drop a .eml file for analysis with zero credentials
  • PDF forensic reports (ReportLab) that read like evidence, not logs

5. Engineering hygiene

  • 95 unit tests across the ML pipeline, forensic analyzer, and services — all synthetic data, no network, no models required
  • Docker one-command deploy, plus render.yaml with auto-deploy
  • The dashboard ships as Jinja templates and an optional React + Vite + TypeScript SPA that talks to the same Flask APIs (FRONTEND_SPA=1/0)
  • LLM API enrichment for explainable classification on top of the score

Stack

Python · Flask · Flask-SocketIO · SQLAlchemy (SQLite) · scikit-learn · XGBoost · LightGBM · PyTorch/Transformers (DistilBERT) · LLM API · VirusTotal · Safe Browsing · AbuseIPDB · MaxMind GeoLite2 · ReportLab · Leaflet · Chart.js · React/Vite (optional SPA) · Docker · Render

My role: ML integration + architecture

I owned model training and the ML pipeline — dataset merging across 7 sources with explicit column mapping, the feature-engineering layer, training all three models on Colab T4s, and the ensemble's graceful-degradation logic that lets the same codebase run on a free-tier VM or with full BERT depth. I also wired the deployment path: models flow from Colab → Drive → backend/ml/models/ → Render.

What I'd do differently

  1. Score explainability earlier. We added verdict explanations near the end; the demo landed harder once a user could see why an email scored 92.
  2. Lock the eval set on day one. Ensemble tuning burned hours we could have saved with a frozen benchmark.
  3. PDF generation is a rabbit hole. ReportLab is powerful, but it ate a full day — next time I'd prototype the report template before the model.

Links

Sabarish R (ML integration + architecture) · Vignesh (geolocation) · Shafeeq (forensic intelligence) · Rohith (dashboard + demo).