Server · #231 trending MCP repo
Cti Expert
7onez/cti-expert
CTI Expert — Cyber Threat Intelligence & OSINT analysis skill for Claude Code / Codex. 120+ commands, 57 techniques, 79 typed MCP tools, deterministic case pipeline + ICD-203 reports. No API keys required for core.
- Stars
- 622
- This week
- —
- Forks
- 91
- Open issues
- 3
- Skills inside
- 1
Documentation
README
CTI Expert
Cyber Threat Intelligence & OSINT Analysis Toolkit
Transform Claude into a trained intelligence analyst — 120+ commands, 57 techniques, zero API keys required for core functionality.
Installation | Quick Start | Commands | Contribute
🇬🇧 English · 🇻🇳 Tiếng Việt · 🇨🇳 中文
Built by Hieu Ngo • hieu.ngo@chongluadao.vn • chongluadao.vn
Core contributor • Zeroska • khuong.nguyen@chongluadao.vn
🤝 Sponsors & Supporters
CTI Expert is built in the open. These organisations back the work — with data, tooling, and hard-won investigative tradecraft.
| Supporter | What they bring | In the toolkit |
|---|---|---|
| Rexxfield | Cybercrime investigation and victim-side casework since 2008 — the real-world tradecraft the case workflow and attribution standards are modelled on | Tradecraft & methodology |
| APWG | Anti-Phishing Working Group — the global cross-industry coalition against phishing and e-crime, and host of the eCrime research symposium whose 2026 papers ground eight of this toolkit's techniques | Phishing detectors (ClickFix, domain survival, kit fingerprint, visibility-aware HTML, PhishTrace, APK permission scope) · /threat-model AAM overlay |
| Hudson Rock | Infostealer-infection intelligence — which machines leaked which credentials, and when | /breach-deep · /stealer-log |
| ParanoidLab | Dark-web, Initial-Access-Broker and infostealer-log monitoring across forums, markets and private Telegram | Dark-web collection & review |
| ANY.RUN | Interactive malware sandbox + TI Lookup — sandbox-observed C2 and real endpoints from packed samples | /binary · /hash-id |
| ZETAlytics | Global passive DNS with rare geographic diversity — historical resolution and co-tenancy pivots | /webpivot · /cti-pivot |
| IntelX | Intelligence X — paste sites, leaks, darknet and phonebook selector search | /webpivot · /email-deep |
| Shodan | Internet-connected host & service intelligence — open ports, banners, tags and known CVEs, passive-first via InternetDB | /webpivot · /appliance-scan · /cert-pivot |
| Censys | Internet-wide host & certificate scanning — the server-side view; every host on an exact leaf certificate (works on the free plan) | /censys · /cert-pivot |
| URLScan.io | Passive website scanning — what a page served and who it talked to, captured without touching the target | /webpivot · /impersonate |
| SerpApi | Search-engine + Google Ads Transparency results API — who paid to send traffic, plus multi-engine dork results | /serp · /search-pivot |
| GrayHatWarfare | Open cloud-bucket & exposed-file search (S3/Azure/GCS/Spaces), the link-shortener corpus (historical long URLs behind goo.gl/bit.ly links) and the public-git corpus (committers, token-in-code, leaked-secret files) — graded exposure, not a same-operator pivot | /grayhatwarfare · /secrets · /docleak · auto-fired per apex in pipeline open |
| Social Links | OSINT investigation platform — 1000+ methods across social media, blockchain and the dark web (SL Professional / Crimewall, Maltego transforms) | OSINT methodology & data |
| Validin | DNS + certificates + favicon + response-body hashes in one graph — passive DNS, subdomain enumeration, reverse-IP and host-response hash pivots on a free community key | Native in /webpivot (domain lookup, reputation, cert & favicon hosts) · MO-neighbour source · /cti-pivot |
| Netlas | Independent internet-asset index — DNS, scan responses, WHOIS and certificate collections behind one key; domains a:<origin-ip> reverses a non-CDN origin to every apex with dates |
/webpivot MO-neighbour source · intel.py netlas · entitlement probe |
[!IMPORTANT] ANY.RUN lookups are read-only; case binary submissions are automatic when keyed.
/ctidownloads APK/installer links tocases/<case>/bin/, runs static analysis and, whenANYRUN_API_KEYis set in a spending run, submits each complete file through the private-by-default sandbox client.--free-only, caseno_spend, defaultloop(unless--full) and hostile/no-direct-contact posture block submission; plan checks and privacy read-back still apply. A public-only account is refused unlessANYRUN_ALLOW_PUBLIC=1; public tasks are world-readable and irreversible. Directanyrun_submitremains ask-first and requiresHARNESS_ALLOW_SUBMIT=1. SeeSKILL.md.
Listing here reflects support for the project and does not imply affiliation, endorsement, or any verification of this tool by the organisations named. Integrations marked above are optional and key-gated — every core technique still runs with zero API keys. Always respect each provider's terms of service. The full list of open-source projects and free public-interest services this skill depends on is in Acknowledgments & Credits.
What is CTI Expert?
A Claude Code skill that transforms Claude into a trained cyber threat intelligence and open-source intelligence analyst. It runs structured intelligence collection using 120+ commands across 57 techniques — no API keys required for core functionality. To take full advantage, add your own free or paid API keys to the skill's .env — each is auto-detected and unlocks higher-tier access (e.g., Wigle, VirusTotal, URLScan.io, Shodan, Censys, SecurityTrails, WhoisXML).
[!TIP] Keyless by default, more powerful with your keys. Every core technique runs with zero API keys. Add any free or paid keys to
.env(or run/apikeys set <service> <KEY>) and the skill auto-detects them, unlocking higher-tier pivots: reverse favicon→host, passive DNS, certificate search, sibling-domain discovery. A missing or bad key never breaks a run — it just degrades to a note. Setup guide: handbook/api-keys.md.
[!TIP] One skill, two layers. cti-expert is the broad collector — the wide net (
/sweep,/webpivot,/subdomain,/username,/email-deep…). Built into the repo is a deep pipeline (intel_engine/) that turns raw collection into a real case: a persistent knowledge base, versioned cases, cross-case correlation, and calibrated assessment. The flow reads like a sentence — collect broadly → "seen this operator before?" → cluster → filter false positives → assess. No external setup: the backend resolves toSELF, and as of v2.9 the bundled installer (scripts/install.{sh,ps1}) provisions the deep layer automatically (or by hand:uv venv && uv pip install -r requirements.txt). Architecture: connectors/intel-backend.md.
Core Capability Multi-vector reconnaissance on any target type — person, domain, organization, username, email, IP, WiFi — with automated finding validation, exposure scoring, and structured intelligence delivery. |
AEAD Workflow Acquire raw data → Enrich with pivot expansion → Assess findings → Deliver structured reports — the base bundle (Markdown + JSON + CSV + IOC bundle) always saves, then you pick a presentation report: PDF, DOCX, HTML, or all. |
What's New in v2.12
The release where the premium keys start pulling their weight — and the report writes itself. v2.11 shipped detectors. v2.12 turns an eight-finding audit of the paid vendors into behaviour (every metered leg gated, bought once per case instead of once per host), adds Netlas as an independent index, makes the editorial PDF/DOCX a deterministic composition from the case directory, and closes the last honesty gap in the ANY.RUN docs: detonation exists, and it is gated five ways.
| Category | What's New | Details |
|---|---|---|
| Cross-type auto-pivot loop | Every selector type is a frontier node; metered spend stays behind one digest-bound approval | Typed nodes (selector_types + rungs.json owner/kit/neighbour) → case_state.propose → required Plan phase (PLAN_TOOLS, read-only) → auto free/urlscan pivots with ≤ N=25 hits, else proposal.json gated in audit.gate (harness approve · case_approve · TTY). Dispatcher ops propose + corpus-search (corpus_search tool). Assessment gains additive kit_siblings / neighbours / mentions — collected, never attributed. Rail: collecting ≠ attributing |
| MO-neighbour pivot | Reverse a non-CDN origin to its co-tenants — and only ever seed on a registrant join key | wp_mo_neighbours.discover reverses the estate's non-CDN origin IP (also the MX origin) through Netlas · Validin · urlscan, WHOIS-verifies each candidate apex, and classifies it same_registrant (current registrant e-mail/phone join key only — a proxied record contributes no phone) / same_mo (reference-data policy) / unrelated / unverifiable. Only same_registrant seeds the frontier; same-MO personas render as a rung-10 Related personas table in the report (--mask-personas to aggregate). A bulk-hosting guard fires before any spend; per-origin lock + disk cache + a per-run WhoisXML ledger hold across collector subprocesses, so an origin is bought once per case. RULE 5 rails intact: facts-only KB ingest, never an edge, never an operator_lead |
| WHOIS eras · urlscan Pro lifecycle | The timeline now knows who held the name before this operator | WHOIS history is an explicit --whois-history purchase scoped to seeds and cluster members (the render path never buys it); the house report renders a Registrant eras table (same-day flaps folded, registrar placeholders classed) and uses the current era's start as the archive-capture cutoff. urlscan Pro supplies the hostname lifecycle index — A/NS eras, CT and zonefile firsts, left-censored on a truncated walk — feeding the temporal view; verdict rows appear only on signal, and the API key is sent to urlscan.io only |
| Entitlement measured, not assumed | A key measured free skips the calls that would 403 | wp_capabilities.discover_plans probes each vendor's free account endpoint (urlscan quotas, Netlas plan, SecurityTrails/DNSLytics usage, IntelX /authenticate/info, Validin /api/paths) once per case into cases/<id>/capability_plans.json; enrich_live reads the store and gates only on a positive free verdict — unknown keeps the productive call as the probe. Censys search records its own verdict. A failed probe is reported this run and never frozen, so the next process re-measures |
| Netlas + the rest of the vendor wiring | Every registered key now has a pivot behind it | wp_netlas.py — Bearer client over the domains · responses · whois_domains · whois_ip · certs collections (search, count, facet, reverse-IP, plan), keyless query builder + web-UI links, ledgered, Cloudflare-safe UA; intel.py netlas ip|ns|spf|domain|san|title|plan|raw. Live-measured: domains a:<origin> returned 32 apexes on a 32-domain estate (15 members + same-MO siblings under other personas). Alongside: SecurityTrails DNS-history eras + DSL reverse-WHOIS (diffed against WhoisXML), DNSLytics reverse-IP under its own co-tenancy-routed key, GrayHatWarfare exposure lead + report section, Shodan cert/JARM search, once-per-case Censys cert search with a shared-cert fan-out guard, IntelX auto-fire in pipeline open (loop: --full only; role mailboxes excluded), and Validin wired natively (domain lookup, reputation, cert and favicon hosts). Frontier ranks owner-link candidates above lookalikes |
| The house report, composed deterministically | The editorial PDF/DOCX no longer needs a model to write it | intel.py house-report <CASE-ID> composes the IntelReport document from the case dir: sections I–XI, both confidence scales + the ICD-203 × Admiralty scatter, relationship graph + entity-relationship map, attribution inference chain, temporal view + registration heatmap + domain × shared-indicator matrix (which honours the §2.5 false-positive control and carries the WHOIS join keys), a landing-page capture per estate host (proxy-gated egress; a page that will not render falls back to the newest public web-scan, then a web-archive snapshot — captioned, dated by the archive, and labelled a previous owner's page when it predates the current registration), per-domain dossiers, a glossary, Appendices A–E. Rule 12 scrub of tool/vendor/path names; third parties masked through one gate (scripts/cti_third_party_mask.py). One figure source (scripts/cti_report_figures.py) feeds both the dashboard DOCX and the house PDF, so the two deliverables cannot disagree. --no-screenshots is fully offline; --no-archive-fallback forbids the stand-ins |
| ANY.RUN: the gate is real, and the docs now say so | Detonation exists — behind five gates, each of them code | SKILL.md, the README callouts, .env.example and the key registry claimed "no submit path". False since the gated layer landed. anyrun_submit is gated by: per-submission confirmation (a confirm=false briefing first — a data-driven approval_briefing exemption in tool_policy.json now lets that step through the MCP approval gate, fail-closed to fully gated if the file is unreadable); private by default, public refused unless ANYRUN_ALLOW_PUBLIC=1 grants a standing authorization, and then only as an explicit, recorded downgrade; a fail-closed plan check (/user private quota — zero is denied and no attestation overrides it — else a prior private task, else --i-have-a-paid-plan); a polled post-submit privacy read-back that withdraws and flags a task that still landed public (verify-privacy <uuid> finishes the check if the task outlives the wait); and the harness deny unless HARNESS_ALLOW_SUBMIT=1. 103 stubbed checks in test_intelx_anyrun §7b–7c; test_tool_gate §2 pins briefing-allowed / confirm-denied / fail-closed |
| Repo hygiene | The tree checks itself for pasted keys | audit.sh §5b greps every tracked file for vendor key shapes and KEY=value lines — proven to fire on a planted value and clean on the tree, so a key can only live in .env. AGENTS.md rewritten as the cross-agent Repository Guidelines. Key-alias registries reconciled with every tool's _secret() lookup and locked (tests/test_key_alias_registry.py); SecurityTrails / DNSLytics / CertSpotter registered so the capability banner stops going silent on keys it uses. The three workflow diagrams re-rendered from current sources — 79 @tools, 9 commands, Netlas, gated ANY.RUN |
| Every documented command is real | 26 SKILL.md commands that had no code behind them now resolve to a tool — or say what they are | 18 new keyless-first CLIs under scripts/osint/ (hash_id — MD5 vs NTLM before you submit, vuln_check, username_enum, phone_osint, github_osint, reputation_check, msft_recon, cn_recon, traffic_rank, sharelink_resolve, dork_builder, exposure_score, signature_scan, kb_crossref, case_drift, deep_profile, wifi_ssid …) plus 8 existing scripts/webpivot/ shims (cert_pivot, email_hygiene, wayback_fetch/harvest, rank_relations, sensitive_paths, pivot_suggest, crypto_balance) registered as @tools and intel.py ops; every SKILL.md command row now names its T2 CLI and T1 tool, or is labelled [model] / [unimplemented]. 79 @tools, and the SDK allowlists are locked to the decorated set (test_tool_registry.py). Clustering enforces the two-artifact rule: each cluster carries corroboration, and one shared artifact is flagged LEAD ONLY, never an attributed estate. The vendored engine's own tests now run in audit.sh §9–10 and CI — they ran nowhere before |
| ChongLuaDao in the pipeline · subdomains · one timeout ceiling | The first-party feeds grade every host, live subdomains join the case, and no call hangs a run | wp_cld.py runs per collected host inside enrich_live — denylist verdict + IoC analysis land as reputation facts (never a cluster edge) and CLD WHOIS is the primary source for .vn names. subdomain_enum unions subfinder (auto-keyed from .env), amass, assetfinder and findomain, DNS-verifies the names into cases/<id>/subenum/<apex>.json, and the frontier queues the live ones for the next round (hoster plumbing is a fact, not a seed). A single per-call ceiling — CTI_CALL_TIMEOUT, default 1800 s from WebPivot/references/timeouts.json — floors every fetch, vendor call, collector and renderer subprocess; raw DNS/TLS probes keep their fail-fast bounds, and a running MCP server keeps its value until restarted |
| Registrable apexes · landlord IPs · kit binders | The frontier and the KB agree on what an apex is and which IP is a landlord | A vendored Public Suffix List (public_suffix_list.json, wp_psl_update.py) drives one _registrable() reducer, so shop.id.vn and panel.co.uk fold to the right apex everywhere — frontier, ingest, report, Blueprint. shared_hosting_max_cohosts (12) is the one number that marks an IP as shared/bulk hosting: its hosted_on edges become facts, the cluster partition ignores them, and co-tenants are held back as leads. Expansion depth is anchored: a host owner-linked to a previous hop is related_hosts, never a member. Scraped social links resolve to the account — per-network reserved paths, second-segment routers and library/vendor credit accounts are reference data, and a bare platform apex (t.me, facebook.com) is never a handle |
| Blueprint folds to apex level | A hundred-host estate no longer silently drops the HTML report's architecture tab | Archify's architecture type draws ≤ 12 nodes · 18 edges, so cti_archify.py now collapses hosts under their registrable apex (Estate · N hosts), folds the long tail into +N more apexes, ranks finding-bearing apexes first and places the operator hub mid-row with spokes above and below. CTI_ARCHIFY=1 (auto: full graph if it fits, else the fold) · force (the widest 25-node grid) · 0; --plan prints both outcomes offline, and every HTML export now confirms the Blueprint mode first (Step C — --yolo keeps Auto). Locked by tests/test_archify_blueprint.py |
/cti on every coding agent |
One registration step wires the commands into every agent on the machine, not only Claude Code | scripts/register_agents.py (run by register.sh) registers the 9 commands with omp · pi · OpenCode · Gemini CLI · Qwen Code · Factory droid · Windsurf and installs /cti as the shared ~/.agents/skills/cti skill for Cursor · Copilot CLI (/cti), Codex ($cti — Codex removed custom prompts in 0.117, so the old codex/cti-expert.md is gone) and the ChatGPT desktop app (@cti). Every file is a pointer stub to this clone, so a git pull updates all agents at once. Install and --uninstall touch only what they wrote — marked stubs, and ~/.claude symlinks that resolve into this repo — never a same-named command or skill of yours. Locked by tests/test_register_agents.py |
| Mermaid figures without a browser | mermaid-cli + Puppeteer + headless Chrome replaced by one ~700 KB module | IntelGraph's render_mermaid.py now lays every Mermaid figure out with Pele (MIT) under Node 20+ or Bun — labels as real SVG <text>, no <foreignObject>, roughly 3–70 ms per figure. The npm release is pinned in IntelGraph/references/pele.json, sha512-verified and fetched into the gitignored IntelGraph/vendor/pele/ (as pele.mjs, so every Node 20 loads it as ESM) by the installers (default install, no longer behind --all) or on first render. Pele's CSS variables are resolved to the house theme (theme.PELE_VARS) so the SVG renders the same in librsvg, cairosvg, pandoc and Word; PNG + vector PDF come from rsvg-convert (librsvg, now a default install) → headless Chrome/Edge → cairosvg. That order keeps the node type icons (🌐 💬 📧): cairosvg draws a label in one font and drops every glyph it lacks, so it is the last resort and warns. references/diagram.css and the mmdc flag shims are gone, and render_mermaid.py drops --theme / --css / --no-css / --puppeteer-config |
What's New in v2.11
What's New in v2.11
The eCrime-2026 follow-up batch — planned, then cooked. Four deferred phases from the v2.10 research pass ship as keyless, offline, tested modules (planned in
plans/260826-1935-ecrime-2026-followup-analyzers/, implemented via/ak:cook). Same contracts: zero-dep tests inaudit.sh, attribution-safety (RULE 5). Full mapping: docs/ecrime-research-integration.md §6.
| Category | What's New | Details |
|---|---|---|
| APK permission-scope scoring | On-device-fraud capability from the manifest | scripts/apk_permission_scope.py scores dangerous-permission combinations (accessibility+overlay+SMS = banking-trojan profile) — the combination is the signal, not any single grant, so banking/AV apps aren't blindly flagged; result is capability, not guilt. Handles plaintext manifests and binary AXML (UTF-8 and UTF-16 string pools; a zero-permission decode degrades to a note, never a false "clean"). Extends BinaryPivot. Grounded in "The 'Allow' Reflex" (Kandagadla Srinivasamurthy, Dupuis — UW). See techniques/apk-permission-scope.md |
| Kit-template attribution | Same-kit lineage across rotating hosts | scripts/kit_template_fingerprint.py fingerprints DOM structure + harvest-form field-set + asset skeleton and grades similarity — but a commodity template (WordPress/Wix/Shopify) match is graded noise, not a same-operator link (§2.5 trap), and no grade ever auto-merges. Grounded in the Vicomtech tree-structured attribution paper. See techniques/kit-template-attribution.md |
| Renderer-level confirmation | Dynamic confirmation of the static ClickFix/visibility verdicts | scripts/render_confirm.py feeds renderer-captured runtime clipboard writes and computed-style hidden elements into the existing detectors (via new additive seams) and reconciles static vs rendered — promoting a JS-assembled payload the static pass missed, or weighing a static-only HIGH. Renderer (Playwright/agent-browser) is optional and degrades to a note. Grounded in PasteJacked + Visibility-Aware HTML. See techniques/renderer-confirmation.md |
| PhishTrace dynamic features | Characterize a page from its runtime trace | scripts/phishtrace_features.py turns a runtime trace (requests, redirects, form POSTs, cloaking) into a verdict + exfil-endpoint IOCs — where a thin/cloaked trace on a flagged page reads as cloaked, never benign, and exfil hosts are pivot leads, not attribution. Grounded in "PhishTrace" (Islam, Mannan). See techniques/phishtrace-dynamic-features.md |
| AAM actor-modeling overlay | Anticipate the next move, not just attribute the past | handbook/aam-actor-modeling.md adds an optional /threat-model overlay (OODA faces + Mirror/Twin/Opposite/Lever) alongside ACH. Grounded in ISECOM's "Modeling Adversaries Through Chaos" training. Docs-only |
| Tested & gated | Four more zero-dep suites in audit.sh |
test_apk_permission_scope (combo-vs-single + benign control + AXML zero-perm degrade), test_kit_template_fingerprint (commodity-trap RULE 5 guard + never-auto-merge), test_render_confirm (promotion + seam-additivity + no-renderer degrade), test_phishtrace_features (cloaked-not-benign) |
What's New in v2.10
What's New in v2.10
The release that turns APWG eCrime 2026 research into working, tested detectors. Three keyless, offline, deterministically-tested analyzers land at the Enrich→Assess boundary, each mapped to a named eCrime 2026 paper — closing the two audited gaps: page-content threat classification, and an explicit maliciously-registered-vs-compromised call. Full mapping + repo audit: docs/ecrime-research-integration.md.
| Category | What's New | Details |
|---|---|---|
| Phishing-domain survival profiling | Registration/DNS strategy → maliciously-registered vs compromised, plus takedown-resilience | scripts/phish_domain_survival.py turns collected WHOIS/DNS into a two-axis, fully auditable judgement (every signal carries a weight + reason; seed lists tunable via --refs). The compromised/malicious split is attribution-safety: a long-lived domain at an established registrar serving unrelated legitimate content is a victim, never named as the operator (RULE 5). Grounded in "Built to Last? Registration and DNS Strategies in Phishing Domain Survival" (Lim et al., eCrime 2026). See techniques/phishing-domain-survival.md |
| ClickFix / PasteJacking detection | Clipboard-hijack lures + -EncodedCommand decode → C2 IOCs |
scripts/clickfix_detect.py scores three co-occurring families (clipboard write · social lure · OS-command payload) so a coupon "copy" button or the word powershell in prose never reads as HIGH; it decodes PowerShell -enc UTF-16LE base64 to surface the hidden C2 URL, and maps to ATT&CK T1204.004/T1059. Grounded in "PasteJacked: Detection and Characterization of Clipboard-Hijacking Attacks" (Nabeel, Melicher, Starov — Palo Alto, eCrime 2026). See techniques/clickfix-clipboard-hijack.md |
| Visibility-aware HTML analysis | Surface hidden credential forms / off-origin links / off-screen brand text | scripts/html_visibility_analysis.py catches inline + class-based hiding a naive parser misses, severity-ranked by concealed intent + origin (a hidden off-origin credential form is HIGH; a benign type=hidden CSRF field is not flagged). Static approximation with the renderer-level path (agent-browser/Playwright) noted as the upgrade. Grounded in "Visibility-Aware HTML Analysis through Renderer-Level Extraction" (Betts et al., Auckland, eCrime 2026). See techniques/visibility-aware-html.md |
| Tested & gated | Three zero-dep suites wired into audit.sh |
tests/test_phish_domain_survival.py, tests/test_clickfix_detect.py, tests/test_html_visibility_analysis.py — with explicit false-positive guards (coupon-copy, prose mention, CSRF field) and the RULE 5 compromised/malicious split — run in scripts/audit.sh §6 |
What's New in v2.9
What's New in v2.9
The release where "install it" and "install everything" become the same command — and the pivot loop stops leaving identifiers on the floor. v2.8 moved the safety rails to where the harness runs. v2.9 fixes the layer beneath the collector: the bundled installer now provisions the whole two-layer stack, not just the light collector — and the last four auto-pivot dead-ends, where a collected identifier was typed but never chased, are wired shut.
| Category | What's New | Details |
|---|---|---|
| The installer provisions the deep layer | "Install once" now installs the pipeline, not just the collector | Both scripts/install.{sh,ps1} installed only scripts/requirements.txt (the light collector) and never the root |
…