The daily cron cut a fresh dated branch (data/clickgrab-${DATE}) and opened
a new PR every run. Since each PR edits the tail + meta of the same growing
_data/clickgrab_trends.yml, they mutually conflict the moment one merges,
leaving a pileup of stuck PRs (#146/#147/#148).
Fixes the structure rather than the symptom:
- Publish to a fixed rolling branch (data/clickgrab-auto), force-pushed each
run and refreshed via `gh pr edit`, so at most one ClickGrab PR is ever
open and it always shows a clean append-only diff vs main.
- Drop cadence from daily to weekly (Mondays 06:00 UTC). The generator's
14-day lookback + watermark dedup backfills every daily bucket regardless,
so weekly captures the same data with far less churn.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YQzGNdzZK4svMZPTK9BikA
- [x] Attack Chokepoints accordion: <details> markup per stage, prereqs in first stage;
pages without Chokepoints field (browser-credential-theft, edr-bypass-techniques,
web-shells) get PLACEHOLDER comment
- [x] The Constant: both standalone boxes removed; invariant text lives in accordion stages
- [x] Variations: EvolutionTimeline entries and chip strip removed; all variants collapsed
by default via <details>; source links rendered or TODO placeholder
- [x] Detection Strategy: Pre-Execution tab added as first tab; EarlyDetections content
rendered for clickfix; PLACEHOLDER for all other pages; Research/Hunt/Analyst tabs
converted to det-tab/det-panel/det-meta-row pattern
- [x] Raw Log Samples: per-item <details class="log-item"> collapsed by default
- [x] Emulation: native <details class="emulation-wrapper"> collapsed by default;
lab warning visible in summary row
- [x] Intel Resources: section removed globally
- [x] OSINT renamed to OSINT Pivots; osint-grid/osint-top/osint-source/osint-desc markup
- [x] Prerequisites standalone section: removed globally; content absorbed into accordion
- [x] CSS: all new classes (chokepoint-item, variant-item, det-tab, det-panel, log-item,
emulation-wrapper, osint-grid, etc.) added; old chain-stage/var-card/intel-flat CSS removed
- [x] JS: switchTab() and copyCode() named functions; event delegation removed
PLACEHOLDERS added:
- browser-credential-theft — Attack Chokepoints missing (no Chokepoints field in YAML)
- edr-bypass-techniques — Attack Chokepoints missing (no Chokepoints field in YAML)
- web-shells — Attack Chokepoints missing (no Chokepoints field in YAML)
- all pages except clickfix-techniques — Pre-Execution tab placeholder
- variants without SourceURL across all pages — "Source link needed" placeholder
(TerminalFix, DownloadFix on clickfix; multiple on browser-credential-theft,
edr-bypass, web-shells, renamed-rmm, remote-execution-tools)
https://claude.ai/code/session_01LPLZjqeDGL5pBXwtvi4fvs
- Replace SVG-based TTP diagram with interactive HTML vertical filter
on all attack chain pages (actor pill buttons dim/light technique
cards; orange border marks universal chokepoints)
- Remove attack flow swimlane section from the shared layout
- Add new AiTM / Phishing Kit attack chain page covering Tycoon 2FA,
Evilginx, EvilProxy, Sneaky 2FA, and Device Code Flow across five
stages (Lure Delivery → Persistence & Objectives)
- Add AiTM card to attack-chains/index; add .ac-card-badge.aitm CSS
- Trim references on ransomware and infostealer pages to direct sources
- Cross-link all three chains to each other in Related Attack Chains
https://claude.ai/code/session_01DpMSTJkS25aCCEqBJBFBtC
The Streamlit app is hosted on Streamlit Community Cloud which has an
ephemeral filesystem — collected data was lost on every restart with no
way to push it back to the repo for the GitHub Pages site.
Adds GitHub API-based commit and PR functions so collected data can be
published from the cloud app. Also adds missing VT_API_KEY and GH_TOKEN
to the secrets pipeline, and creates a secrets.toml.example for cloud
configuration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: update_masq_infra.py produced a schema incompatible with
the Jekyll templates — missing meta.record_count, records[], campaigns[],
payload_summary, and infrastructure_summary. The website gates all
content on `site.data.masq_infra.meta.record_count > 0`, so every
section showed "No data yet".
Fixes across 11 files:
Schema & data (update_masq_infra.py, _data/masq_infra.json):
- Add meta.record_count alongside sample_size
- Build records[], payload_summary, infrastructure_summary sections
matching build_data.py's output schema
- Add campaigns[] placeholder for standalone pipeline
Collection scripts (collect_ioc_feeds.py, collect_infra_hunts.py,
fetch_payload_chains.py):
- Fix "agenttesl a" typo in _C2 set across all 3 files — AgentTesla
was never classified as C2
- Fix MalwareBazaar query_status check: "tag_info" → "ok"
- Add shared abuse.ch API key fallback for ThreatFox and URLhaus
History (append_history.py, _data/masq_infra_history.json):
- Read actual total_domains from record_count or sample_size
- Read lets_encrypt_pct from stats instead of hardcoded 0.0
- Read traffic_sources from data instead of hardcoded {}
- Fix week 2026-12 snapshot: 0 domains → 1569, 0% LE → 91.2%
Website (trends/masq-infra.md):
- Inject window.MASQ_HISTORY and load masq-infra-history.js
- Add chart container divs with correct IDs
- Add Trends to sidenav navigation
CI (.github/workflows/update-masq-infra.yml):
- Add missing MB_API_KEY to IOC feeds and infra hunts steps
- Add missing ANTHROPIC_API_KEY to cluster campaigns step
Streamlit app (collection_tab.py, helpers.py):
- Add VT_API_KEY to env builder and key map
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- collect_infra_hunts.py extract_domain(): log tldextract exceptions to stderr
instead of silently passing; bare `except Exception:` → `except Exception as exc:`
- fetch_payload_chains.py compute_favicon_hash(): log favicon fetch failures to
stderr instead of silently passing; same bare-except fix
- app/utils/helpers.py load_records(): log JSON parse errors to stderr before
returning []; previously a corrupt cache file was indistinguishable from a
missing one in the Streamlit UI
- cluster_campaigns.py build_campaign(): guard `r["id"]` list comprehension with
`if "id" in r` to prevent unhandled KeyError from aborting all campaign clustering
- cluster_campaigns.py assign_records_to_clusters(): use r.get("id") instead of
r["id"] in the assigned-ids membership check for the same reason
- append_history.py _extract_row(): fix schema mismatch with build_data.py v2.0.0
output; was reading stale top-level keys (generated_at, summary, stats,
lure_types, traffic_sources, asn_distribution, payload_families) that no longer
exist, causing every history entry to be written with all-zero values while the
script reported success; now reads from meta.last_updated, meta.record_count,
campaigns[], payload_summary.lure_payload_matrix, infrastructure_summary.top_asns,
and payload_summary.top_families
https://claude.ai/code/session_01BdaHsgueTdZc4su7SfM8AN
Streamlit Community Cloud installs from requirements.txt in the repo
root. The existing requirements-update.txt was not picked up, causing
plotly (and other pipeline deps) to be missing at runtime.
https://claude.ai/code/session_0191A2u4PjnLynghfj2iEVHX
Adds the full Streamlit data review and export UI:
- app/streamlit_app.py — main entry point with sidebar nav and custom CSS
- app/utils/helpers.py — shared utilities: load_secrets (st.secrets → os.environ
fallback), load_records/load_campaigns, confidence_badge, payload_class_color,
format_chain, run_script
- app/components/collection_tab.py — IOC feeds, infra hunts, chain reconstruction,
AI triage buttons with [SUMMARY] stderr parsing; cache file status row
- app/components/records_tab.py — filterable dataframe of triaged records with
detail expander and below-threshold warning metric
- app/components/chains_tab.py — chain flow diagram using st.columns, depth
distribution table, CDN usage metric
- app/components/payloads_tab.py — metric row, plotly class bar chart, top-15
families table, lure×payload pivot table
- app/components/campaigns_tab.py — campaign cards with confidence badges, class/
lure chips, narrative display, cross-tab navigation to samples tab
- app/components/infrastructure_tab.py — delivery domain table, favicon clusters
with Shodan links, ASN distribution chart (CDN providers excluded)
- app/components/samples_tab.py — full record table with urlscan links, export
masq_infra.json via build_data.py, raw records download
- .streamlit/config.toml — dark theme matching site palette
https://claude.ai/code/session_0191A2u4PjnLynghfj2iEVHX
Add claude_triage.py and replace cluster_campaigns.py + build_data.py
as part of the masq-infra pipeline rebuild:
- scripts/claude_triage.py (new): reads cache/enriched_records.json,
calls claude-sonnet-4-6 to classify records where payload_class is
unknown or payload_family is null. Hard cap of 50 API calls per run
with 1s rate limit. Gracefully falls back to copying input unchanged
when ANTHROPIC_API_KEY is missing. Writes cache/triaged_records.json.
- scripts/cluster_campaigns.py (replaced): hard-signal-only clustering
replaces the old union-find composite-key approach. Groups records by
shared_payload > shared_favicon > shared_ip (30-day window) >
shared_cert_pattern (cheap TLD + self-signed), each record assigned to
at most one cluster by priority. Confidence scored from hard-signal
base + corroborating signals. Writes cache/campaigns.json (≥70) and
cache/low_confidence_campaigns.json (<70). Optional --narratives flag
triggers AI-generated campaign summaries via claude-sonnet-4-6.
- scripts/build_data.py (replaced): reads cache/triaged_records.json +
cache/campaigns.json, filters to confidence ≥ 40, assembles meta /
payload_summary / infrastructure_summary / weekly_summary sections per
the schema in _data/masq_infra_schema.md, and writes
_data/masq_infra.json. Supports --dry-run flag.
https://claude.ai/code/session_0191A2u4PjnLynghfj2iEVHX
Add three pipeline scripts for the masq-infra rebuild:
- scripts/collect_ioc_feeds.py: queries MalwareBazaar (20 payload family
tags), ThreatFox (bulk IOC pull, confidence ≥ 75), and URLhaus (recent
500 URLs filtered to online/unknown status). Assembles unified
schema-conformant records, deduplicates by record id, writes
cache/ioc_records.json. Graceful degradation on missing API keys.
- scripts/collect_infra_hunts.py: proactive hunting via Shodan favicon
hash queries (6 brand hashes) and open-directory queries, plus URLScan
filename/title pivots across 11 brand lures. All candidate records
start with lower confidence scores than confirmed IOC feed records.
Writes cache/infra_records.json.
- scripts/fetch_payload_chains.py: reads both cache files, reconstructs
redirect chains from URLScan result JSON (classifies each hop as lure /
redirector / payload-delivery / cdn), fetches favicon hashes via
MurmurHash3, re-scores confidence using the full schema rules, and
writes cache/enriched_records.json. Hard cap of 200 URLScan lookups per
run to stay within free-tier limits.
https://claude.ai/code/session_0191A2u4PjnLynghfj2iEVHX
YAML: Removed variant-specific, single-incident, secondary, and annual
report entries from all 7 chokepoint Intel sections. Migrated variant-
specific sources to SourceURL on variation cards (Commit 3). Added
Tier: primary to entries that lacked the field. No Tier: supporting entries
remain in any chokepoint file.
Entry counts after trim:
clickfix: 18 → 9
renamed-rmm: 7 → 4
edr-bypass: 10 → 4 (Tier field added to all kept entries)
ransomware-svc: 8 → 3
browser-credential: 9 → 5 (Tier field added to all kept entries)
web-shells: 10 → 5 (Tier field added to all kept entries)
remote-execution: 9 → 7
Layout: Replaced tiered Intel rendering (primary card grid + supporting
collapsible toggle) with a uniform flat link list — title (link) + one
sentence description. Removed intel-card, intel-grid, intel-card-name,
intel-card-desc, intel-linked, intel-link-tag, intel-supporting-toggle CSS.
Removed intel-supporting-toggle JS event handler.
https://claude.ai/code/session_018xsxUHnwvGtKP6J2W69qa5
Layout: deleted the entire {% if cp.KnownBypasses %}...{% endif %} block
including section heading, table, and surrounding markup. Nothing replaces it.
Schema + template: marked KnownBypasses as deprecated with comments
explaining it is preserved in YAML data for future migration but no longer
rendered. Template entry commented out to discourage use in new files.
YAML data files are untouched — KnownBypasses data remains in all chokepoints.
https://claude.ai/code/session_018xsxUHnwvGtKP6J2W69qa5
YAML: Added SourceURL to top-level Variations entries across all 7
chokepoint files (24 total). One authoritative primary source per variant;
omitted field where no clear primary source exists. No SourceURL added to
nested structures (MasqueradeThemes in renamed-rmm-tools).
Layout: Added .variant-source-link CSS class (small monospace text, subtle
accent border, hover underline — matches chain-sigma-link pattern). Added
{% if v.SourceURL %} footer block to var-card template rendering
"Source →" link at the bottom of each variation card.
https://claude.ai/code/session_018xsxUHnwvGtKP6J2W69qa5
- Update CSS comment, HTML comment, heading text, aria-controls,
data-target, and collapsible body id from chain-body to
attack-chokepoints-body
- Stage count subtitle already used {{ cp.Chokepoints.size }}
dynamically; switch to Liquid filter form {{ cp.Chokepoints | size }}
for consistency with other collapsible subtitles in the layout
https://claude.ai/code/session_018xsxUHnwvGtKP6J2W69qa5
- Merge duplicate Storm-2561 Variation entries into single entry (2025-Q2)
- Merge two duplicate 2026-Q1 Storm-2561 EvolutionTimeline entries into one
- Trim LummaC2 Notes: remove sentence restating Status (disruption date)
- Trim RedLine Notes: remove sentence restating Status (disruption date)
- Replace verbose 2025-Q2 DetectionImpact with "No change to core detection pattern"
https://claude.ai/code/session_01RkmeTxBwe4djWBSc6rANyS
Remove all content after level: in every sigma-rules/**/*.yml file
outside browser-credential-theft (handled in prior commit): section
banners, investigation notes, KQL/SPL queries, and operational
narrative across clickfix, edr-bypass, ransomware-service,
remote-execution, renamed-rmm, and web-shells.
Also removes a mid-file implementation note block in
remote-execution/analyst.yml that preceded logsource:.
https://claude.ai/code/session_01RkmeTxBwe4djWBSc6rANyS
Remove all content after the level: field in research.yml, hunt.yml,
and analyst.yml: investigation notes, KQL queries, and operational
narrative. These belong in documentation, not in Sigma rule files.
Validates the strip pattern before applying it broadly.
https://claude.ai/code/session_01RkmeTxBwe4djWBSc6rANyS
- Remove defer from hljs script tags; wrap highlightAll() in DOMContentLoaded
to guarantee scripts are loaded before the call executes
- Replace xml/plaintext conditional log language assignment with flat language-yaml
https://claude.ai/code/session_01RkmeTxBwe4djWBSc6rANyS
Extracts fingerprint_campaigns() from aggregate() into main() in
update_masq_infra.py; writes result to cache/payload_clusters.json.
In build_data.py, loads payload_clusters.json independently, writes
merged["infra_clusters"] and merged["payload_clusters"] as distinct
top-level fields, and cleans up legacy campaigns/campaign_clusters keys.
build_campaigns() gains cluster_type parameter passed through to score_cluster().
https://claude.ai/code/session_01M2dV1Y8ujm65zXemApvfo4
Adds score_cluster(cluster, cluster_type) with separate signal sets for
infra clusters (lure_type, favicon_hash, asn, registration_week) and
payload clusters (payload_families, file_type, asn_cohort, date_range
width). Applies scoring in build_campaigns() and new build_payload_campaigns().
Each cluster output gains confidence, confidence_label, and signal_breakdown.
https://claude.ai/code/session_01M2dV1Y8ujm65zXemApvfo4
In _extract_domains() and build_domain_age_histogram(), replace bare
vt_creation_date lookup with: vt_creation_date → record["date"] →
record["first_seen"]. Handles privacy-protected domains (~40% of registrar
domains lack WHOIS) and pipeline records that use different field names.
Age histogram is documented as best-effort approximation.
https://claude.ai/code/session_01M2dV1Y8ujm65zXemApvfo4
Adds The DFIR Report, Elastic Security Labs, Red Canary Blog,
Mandiant (mandiant.com), and SentinelOne Labs to rss_feeds.
Huntress Blog was already present and skipped.
https://claude.ai/code/session_01M2dV1Y8ujm65zXemApvfo4
Replaces the generic "are there factual inaccuracies" closing question in
validate_accuracy_with_claude() with three targeted questions covering
prerequisite bypass, Variation Status staleness, and Sigma rule evasion.
https://claude.ai/code/session_01M2dV1Y8ujm65zXemApvfo4
Adds Description field and the Notes from the first existing Variation
to each chokepoint block in build_chokepoint_context(), giving Claude
detection scope rather than just names when evaluating articles.
https://claude.ai/code/session_01M2dV1Y8ujm65zXemApvfo4
Replaces RSS body-only text assembly with a full HTTP fetch of each
article URL, stripping HTML via html.parser and truncating to
MAX_ARTICLE_CHARS. Failed fetches fall back to the original RSS body
so the pipeline is never broken by unreachable URLs.
https://claude.ai/code/session_01M2dV1Y8ujm65zXemApvfo4
_data/chokepoints.* has never existed in this repo; the unconditional
`| sort:` call crashes the build with "Cannot sort a null object".
Wrap with {% if site.data.chokepoints %} so the page builds cleanly
whether or not the data file is present.
https://claude.ai/code/session_01CsVRSuTipWjCaQ2mqCGVsF
Wrap three IOK rule code blocks in a radio-input CSS-only tab interface
(no JavaScript) with labels Generic download lure / Crypto wallet /
Fast-deploy typosquat. Append matching .iok-tabs CSS to style.css.
Add cg-samples-table class and URLScan search column to both sample
tables. Add inline script that hides Payload host and Family columns
at page load when >80% of rows are empty, identified by header text.
https://claude.ai/code/session_01CsVRSuTipWjCaQ2mqCGVsF
Replace placeholder callouts in #lure-payload and #campaigns with
data-driven Liquid content sourced from masq_infra.lure_payload_matrix
and masq_infra.campaigns. Adds family tag pill CSS, campaign card
layout, and green ACTIVE badge for clusters seen within 7 days.
https://claude.ai/code/session_01CsVRSuTipWjCaQ2mqCGVsF
- _extract_domains(): inspect enriched record keys (lure_type → category →
tag fallback) and brand; propagate both into domain_info entries
- cluster_domains(): compute majority-vote lure_type, brand, and
lure_type_breakdown across cluster members; add all three to cluster dicts
- build_campaigns(): accept ha_lookup_results param; join HA family hits
keyed as "domain:{host}" into per-domain families; pass lure_type and
brand from cluster dict through to campaign output
- add build_lure_payload_matrix(): aggregates {lure_type, top_families,
domain_count} across all campaigns; written to merged["lure_payload_matrix"]
https://claude.ai/code/session_01CsVRSuTipWjCaQ2mqCGVsF
Apply three coding standards across all 6 enrichment pipeline scripts:
pathlib
- Replace all os.path.join/exists/dirname/makedirs with Path / operator,
path.exists(), path.mkdir(), path.read_text(), path.write_text()
httpx + async
- Replace requests.Session with httpx.AsyncClient throughout
- Drop the hand-rolled blocking RateLimiter class in enrich_infra.py;
replace with _VTThrottle (async lock + sliding window, same semantics)
- Parallelize: hostname enrichment (asyncio.gather + Semaphore),
favicon fetches (configurable FAVICON_CONCURRENCY), HA IOC lookups
(Semaphore of 6), LFS file downloads (asyncio.gather per file),
sandbox poll loops (concurrent per-job coroutines)
- All main() functions are now async; entry point is asyncio.run(main())
.env
- Add .env.example with all tunable constants (URLs, timeouts, rate limits,
concurrency, environment IDs); values load via python-dotenv with sane
defaults so scripts work without a local .env file
- Add .env to .gitignore
- Add httpx python-dotenv to workflow pip install line
https://claude.ai/code/session_01A9ipwynqdYKxToTJPMW8uf
Each script now appends its section to cache/pipeline_run.json at the
end of main(), recording API key presence, query counts, success/failure
tallies, and a status field:
- ingest_clickgrab: files_found, files_in_window, lfs_skipped, records_ingested
- enrich_infra: IPinfo queried/enriched/failed + VT requests_used/budget_exhausted
- ha_lookup: new_lookups, reports_found, total_cached
- cluster_campaigns: domains_processed, favicons_obtained, clusters_identified
- sandbox_submit: candidates, submitted_new, completed, failed, timed_out
- build_data: records_merged, clusters_merged, payload_families + full summary
https://claude.ai/code/session_01A9ipwynqdYKxToTJPMW8uf
- Combines enrich.yml steps into update-masq-infra.yml so all trends
pages update in a single Monday run with one PR
- Deletes enrich.yml (now redundant)
- Adds scripts/ha_lookup.py: queries HA /search/terms for existing
public reports on enriched domains/IPs (cached across runs)
- Updates build_data.py to merge HA lookup family tags into
payload_families alongside sandbox submission results
Step order: update_masq_infra → ingest_clickgrab → enrich_infra →
ha_lookup → sandbox_submit (opt-in) → cluster_campaigns → build_data
https://claude.ai/code/session_01A9ipwynqdYKxToTJPMW8uf
Rewrites sandbox_submit.py to use the Hybrid Analysis (Falcon Sandbox)
API v2 instead of tria.ge. Key changes:
- Auth: api-key header instead of Bearer token
- Submit: POST /submit/url with environment_id=160 (Windows 10 64-bit)
- Poll: GET /report/{job_id}/state watching for state == "SUCCESS"
- Report: GET /report/{job_id}/summary, extracts vx_family,
compromised_hosts, domains, sha256, threat_score, and verdict
- Cache files renamed: triage_submitted.json → ha_submitted.json,
triage_results.json → ha_results.json
- Env var: TRIAGE_TOKEN → HA_API_KEY
Updates enrich.yml to use secrets.HA_API_KEY and updates step/PR
body copy to reference Hybrid Analysis.
https://claude.ai/code/session_01A9ipwynqdYKxToTJPMW8uf
Result of dry-run build_data.py execution during pipeline validation.
Adds new top-level fields (asn_distribution, country_distribution,
domain_age_histogram, campaigns, summary, date_range, generated_at)
with zero-count values since ClickGrab LFS budget is currently
exhausted. Existing fields preserved (hosting_providers, lure_types,
urlhaus_tags, etc.).
https://claude.ai/code/session_01A9ipwynqdYKxToTJPMW8uf
Dry run revealed two bugs:
1. Filename format was YYYY-MM-DD but actual files use YYYYMMDD_HHMMSS
(e.g. clickgrab_report_20260302_032841.json)
2. All nightly reports are stored in Git LFS — raw.githubusercontent.com
returns pointer files, not JSON content
Fix: use GitHub Contents API to list nightly_reports/ and filter by
YYYYMMDD prefix in filename. Then fetch the LFS pointer via raw URL,
resolve the real download URL via GitHub LFS Batch API, and download
the actual content.
Gracefully handles LFS budget exceeded (logs warning, empty output,
downstream scripts run cleanly with zero records — confirmed by dry run).
Also pass GITHUB_TOKEN to the ingest step in enrich.yml so the workflow
uses authenticated GitHub API calls and gets LFS access priority.
https://claude.ai/code/session_01A9ipwynqdYKxToTJPMW8uf
Adds a 'Live Infrastructure Charts' section to trends/masq-infra.md
with 6 charts powered by Chart.js 4.4.3 (CDN):
1. ASN/Hosting Distribution — horizontal bar, top 10 ASNs
(CDN orange, bulletproof red, other purple)
2. Country of Origin — horizontal bar, country-level only
(IPinfo Lite free tier — no city precision)
3. Domain Age at Observation — histogram (0-1d → 90d+ buckets)
4. Payload Families — doughnut (Triage data); shows placeholder
when sandbox pipeline hasn't run
5. Campaign Timeline — swimlane (stacked bar per cluster)
6. TLS CA Distribution — pie; falls back to stats.tls_lets_encrypt_pct
from the existing update_masq_infra.py pipeline
Data is embedded at Jekyll build time via {{ site.data.masq_infra | jsonify }}
so charts work on GitHub Pages without serving _data/ files directly.
https://claude.ai/code/session_01A9ipwynqdYKxToTJPMW8uf
Weekly Monday 07:00 UTC pipeline that runs the full ClickGrab
enrichment sequence:
1. ingest_clickgrab.py — fetch last 7 nightly reports from GitHub
2. enrich_infra.py — IPinfo Lite + VT enrichment
3. sandbox_submit.py — Triage sandbox (gated on ENABLE_SANDBOX=true)
4. cluster_campaigns.py — favicon+ASN+reg-week clustering
5. build_data.py — merge into _data/masq_infra.json
Cache is persisted between runs via actions/cache so DNS lookups and
VT responses are not re-queried unnecessarily. Opens a PR on the
data/enrich-clickgrab-{DATE} branch for review before publishing.
https://claude.ai/code/session_01A9ipwynqdYKxToTJPMW8uf
Reads cache/enriched_infra.json, cache/campaign_clusters.json, and
cache/triage_results.json (optional) to compute:
- asn_distribution, country_distribution, domain_age_histogram
- campaigns (from cluster data + Triage family tags)
- summary counters (total_domains, unique_asns, etc.)
- payload_families (from Triage; falls back to existing value)
Merges into the existing _data/masq_infra.json, preserving all fields
written by update_masq_infra.py (hosting_providers, lure_types,
traffic_sources, urlhaus_tags, tls_cert_authorities, etc.) so both
pipelines can coexist in the same data file.
https://claude.ai/code/session_01A9ipwynqdYKxToTJPMW8uf
Groups domains into campaigns using union-find over a composite
(favicon_hash, ASN, registration_week) signal graph. Two domains are
linked when they share ≥2 of 3 signals; clusters require ≥3 members.
Favicon hashing uses Shodan's exact MMH3 / RFC 2045 base64 algorithm
(reused from update_masq_infra.py). A blocklist of common default
favicon hashes prevents false cluster merges. Falls back to 2-key
(favicon + ASN) clustering when VT creation date is absent.
Cluster IDs are human-readable: CLUSTER-{YYYY-WW}-{ASN}.
https://claude.ai/code/session_01A9ipwynqdYKxToTJPMW8uf
Submits staging URLs from the ClickGrab pipeline to Triage (tria.ge)
for behavioral analysis. Pre-submission filter requires:
- VT malicious vote count > 0
- Downloaded file with actionable extension (.ps1/.hta/.js/.bat/.exe/.msi)
- Not a CDN root without a deep file path
Tracks submitted URLs in cache/triage_submitted.json to avoid
resubmission on subsequent runs. Polls events stream every 30s with
15-minute timeout, then extracts family tags, network IOCs, and dropped
file hashes from the behavioral report.
Gated in CI by ENABLE_SANDBOX=true repository variable to prevent
accidental public submissions.
https://claude.ai/code/session_01A9ipwynqdYKxToTJPMW8uf
Resolves hostnames from ClickGrab URLs to IPs, enriches with:
- IPinfo Lite: ASN, org name, country, continent (free tier only)
- VirusTotal: reputation stats and domain creation date
Implements a sliding-window RateLimiter for VT's 4 req/min, 500/day
free tier — gracefully exits VT lookups on budget exhaustion without
crashing the pipeline. DNS and VT responses are persisted to
cache/dns_cache.json and cache/vt_cache.json across weekly runs.
https://claude.ai/code/session_01A9ipwynqdYKxToTJPMW8uf
Fetches the last 7 days of ClickGrab nightly reports from the public
MHaggis/ClickGrab repo, extracts per-URL records (url, tags,
redirect_chain, downloaded_files, script_snippets, risk_score),
deduplicates by (url, date), and writes cache/clickgrab_raw.json.
Mirrors the report schema handling from analyze_clickgrab.py to
support both old (list-of-dicts with Sites) and new (dict with sites)
report formats.
https://claude.ai/code/session_01A9ipwynqdYKxToTJPMW8uf
Adds a utility script to identify and delete remote branches that have
already been merged into main. Supports --dry-run mode for safe preview
before actually deleting anything.
Identified 30 merged branches ready for deletion:
- 17 claude/* work branches
- 2 chore/weekly-ttp-update-* branches
- 11 feature/fix/data/docs/misc branches
4 unmerged branches remain for review:
- claude/clickfix-malware-research-C8YsK (7 days old)
- claude/verify-clickfix-osint-pohiZ (8 days old)
- updated-chokepoints (9 days old)
- claude/review-slide-deck-content-NBanr (2 weeks old)
https://claude.ai/code/session_013k8d24eYFrsJhz1Lvr37iN
Blank lines inside a YAML literal block scalar (`run: |`) terminate the
block early. Moved PR body into a printf variable to avoid the blank line
in the YAML itself.
https://claude.ai/code/session_018u1RitQLKpYyxFBTRbFKnM
abuse.ch issues one API key that covers both MalwareBazaar and URLHaus.
When URLHAUS_API_KEY is not set, use MB_API_KEY as the Auth-Key header
so URLHaus requests authenticate correctly without requiring a separate secret.
https://claude.ai/code/session_018u1RitQLKpYyxFBTRbFKnM
Two improvements:
1. Validin DNS failure produces 100 identical warnings (one per domain)
because api.validin.com does not resolve from GitHub Actions runners.
Now catches requests.exceptions.ConnectionError, checks for
NameResolutionError/Failed to resolve in the message, and breaks the
domain loop immediately with a single warning. Non-DNS errors (timeouts,
HTTP errors) still iterate all domains as before.
2. Workflow committed _data/masq_infra.json directly to the default branch
with no review step. Replaced with a PR flow matching weekly-ttp-update:
- Creates a dated branch data/masq-infra-YYYY-MM-DD
- Commits and force-pushes (idempotent on re-runs same day)
- Opens a PR against main via gh pr create
- Skips PR creation if one is already open for that branch
- Skips everything if _data/masq_infra.json has no changes
Adds pull-requests: write permission to the workflow.
https://claude.ai/code/session_018u1RitQLKpYyxFBTRbFKnM
Two bugs fixed:
1. TypeError in classify_lure when download_url or page_title is None.
rec.get("download_url", "") returns None when the key exists with value
None — the default only fires on a missing key. Fixed by coercing all
three inputs inside classify_lure with `v or ""`, making the function
null-safe regardless of call site.
2. URLHaus 401 Unauthorized. abuse.ch added mandatory Auth-Key header
authentication to all /v1/ endpoints in late 2024. Added optional
URLHAUS_API_KEY env var (free key at abuse.ch); passes Auth-Key header
when set, warns and continues without it when unset. Updated workflow
to expose the new secret.
Verified with: null-input classify_lure assertions, collect_urlhaus
signature check, and aggregate-with-None-fields assertions.
https://claude.ai/code/session_018u1RitQLKpYyxFBTRbFKnM
URLScan's Elasticsearch backend requires all sort values for search_after
cursor pagination (typically [timestamp_ms, result_id]). Passing only
sort_val[0] produced a malformed single-value cursor, causing 400 Bad
Request on page 2+ of high-volume brand queries (discord, telegram, etc.).
Fix: pass sort_val (full list) instead of sort_val[0]. requests encodes
a list as repeated params: search_after=v1&search_after=v2, which is the
format Elasticsearch expects for composite sort cursors.
Also extend the 400 handler in _urlscan_request_with_backoff to stop
pagination gracefully for the current brand (return None) rather than
raising, so a single bad cursor doesn't crash the whole pipeline.
Validated with a requests.Request encoding test confirming both sort
values appear as separate search_after params in the prepared URL.
https://claude.ai/code/session_018u1RitQLKpYyxFBTRbFKnM
Three bugs fixed in collect_urlscan():
1. Root cause of 0-results exit: the client-side verdicts.overall.malicious
filter was dropping nearly all results. URLScan only sets this flag when
its own scanner or a community submission explicitly marks a domain — most
brand-impersonation sites are scanned by researchers before weaponization
and receive no malicious verdict. Removed the filter; verdict is now stored
on the record as urlscan_malicious for informational use only. Quality
signal comes from MalwareBazaar/VirusTotal hash enrichment and URLHaus
cross-reference, not URLScan verdicts.
2. IndexError on empty results page: added `if not results: break` guard
before results[-1] access (edge case when has_more is true but results=[]).
3. IndexError on empty sort field: replaced last.get("sort", [""])[0] with
safe extraction using `sort_val = results[-1].get("sort") or []` plus
an explicit break if sort_val is empty.
All three bugs validated with syntax check and pure-function unit tests.
https://claude.ai/code/session_018u1RitQLKpYyxFBTRbFKnM
The verdicts.malicious filter requires an elevated API tier; free keys
receive a 403. Replace with client-side filtering on verdicts.overall.malicious
from the response payload, which is available on all tiers.
Also handle 403 responses gracefully (log + skip) instead of raising,
so a single restricted query doesn't crash the entire pipeline.
https://claude.ai/code/session_018u1RitQLKpYyxFBTRbFKnM
- Detection recommendation cards now have expandable "Example payloads"
sections showing real/representative command strings per category
(IWR/IEX, IRM/IEX, WebClient, Curl, Base64 encoded+decoded, self-delete,
CDN staging URLs, hidden-window)
- Staging infrastructure table rows are now click-to-expand with detail
panels showing hosting type badge, status, ASN/geo, domain registration
date, registrar, and a SecurityTrails DNS history link per entry
- analyze_clickgrab.py extended with:
- payload_examples collector: harvests up to 3 deduplicated examples per
detection category from PowerShellCommands / EncodedPowerShell /
Base64Strings / PowerShellDownloads fields on each pipeline run
- enrich_domain(): calls ip-api.com (ASN/geo) and RDAP (registration
date/registrar) per staging domain; results written to YAML
- HOSTING_ANALYST lookup dict for manually-tagged hosting_type/status
- _data/clickgrab_trends.yml pre-populated with representative payload
examples and analyst-tagged enrichment fields (ASN/geo populated by
pipeline on next run; DNS history links to SecurityTrails free tier)
- assets/css/style.css: .cg-payload-example, .cg-payload-meta,
.cg-payload-label, .cg-infra-detail-*, .cg-badge-hosting-*,
.cg-badge-status-* classes
https://claude.ai/code/session_0183gMN46x4Gaq6JxTc3SvrG
Removed the two placeholder trend pages (2025-q1.md, chokepoint-shifts.md)
that didn't fit the site's analysis-first direction.
Added trends/masq-infra.md — a full analysis of software impersonation
infrastructure covering:
- Domain naming patterns (typosquatting, combosquatting, homoglyphs)
- Hosting provider abuse (Cloudflare Pages, GitHub Pages, Firebase)
- Favicon abuse from both attacker and defender perspectives, including
Murmur3 hash pivoting on Shodan/Censys for infrastructure clustering
- Detection chokepoint chain (T1036.005 PE metadata mismatch, execution
from download paths, MotW, cert transparency monitoring)
Updated trends/index.md to replace two old cards with the new page.
All styling uses CSS variables for full dark/light theme compatibility.
https://claude.ai/code/session_01Nza6uzSyU61aAX1txaoGve
Replaces all hardcoded dark-mode hex values (#c9d1d9, #8b949e, #30363d,
#161b22, #21262d, #58a6ff, #e3b341, #f0883e, #da3633, #388bfd, #3fb950)
with the site's CSS variable system (var(--text), var(--text-muted),
var(--border), var(--bg-card), var(--bg-input), var(--link), var(--high),
var(--accent), var(--critical), var(--low), var(--medium)).
Consistent with chokepoint and attack-chain pages, and now compatible
with the site's dark/light theme toggle.
https://claude.ai/code/session_01Nza6uzSyU61aAX1txaoGve
Both pages were rendering as unstyled raw markdown. Added the same
.cg-page layout wrapper used by clickgrab.md, but using CSS variables
(var(--text), var(--text-muted), var(--border), var(--link), var(--bg-card))
instead of hardcoded hex values — consistent with the rest of the site
(chokepoints, attack-chain pages) and compatible with the dark/light theme toggle.
https://claude.ai/code/session_01Nza6uzSyU61aAX1txaoGve
All three trend subpages were unreachable:
- chokepoint-shifts.md and 2025-q1.md had no YAML frontmatter, so
Jekyll treated them as static files (copied as .md, not rendered to
HTML) — navigating to /trends/chokepoint-shifts/ produced a 404.
- clickgrab.md had frontmatter but no explicit permalink, so Jekyll
output it at trends/clickgrab.html while the index linked to
/trends/clickgrab/ (trailing slash), also a 404.
Fix: add layout, title, description, and explicit permalink to each
subpage so Jekyll renders them as proper HTML pages at the expected URLs.
https://claude.ai/code/session_01Nza6uzSyU61aAX1txaoGve
Adds a vertical node-graph flow diagram to the ransomware and infostealer
attack chain pages showing which MITRE techniques are shared across the
groups/families already tracked in the actor convergence matrix. Colored
dots on each technique node indicate which actors use it — techniques where
all dots are filled are universal chokepoints and the highest-ROI targets
for detection engineering.
- _data/ransomware_ttp_overlap.yml: 9 phases, ~35 techniques mapped to
BlackBasta, LockBit 3.0, Akira, Alphv/BlackCat, and Play
- _data/infostealer_ttp_overlap.yml: 6 phases, ~16 techniques mapped to
RedLine, LummaC2, Vidar, StealC, and Raccoon
- _includes/ttp-overlap-diagram.html: legend + SVG container + JSON data
injection consumed by ttp-overlap.js
- assets/js/ttp-overlap.js: vanilla JS SVG renderer with hover tooltips;
nodes with full group coverage highlighted in accent orange
- _layouts/attack-chain.html: new TTP overlap section (show_ttp_overlap
front-matter flag), ttp-overlap.js script tag, and supporting CSS
- attack-chains/ransomware.md, infostealers.md: opt-in front-matter flags
https://claude.ai/code/session_01VVQNo9RGmnS6CTLVZxFuJW
The {% if page.chokepoints %} block wrapping the Mermaid script was
replaced but its closing {% endif %} after the <style> block was left
behind, causing 'Unknown tag endif' at build time.
https://claude.ai/code/session_01HSkU42mGpi5VjgjXd3HcZi
Redesigns the attack chain visualization on ransomware and infostealer
pages from a basic Mermaid LR flowchart to a three-swimlane SVG diagram
that maps attacker actions, MITRE ATT&CK techniques, and detection
posture at each stage.
Key changes:
- _includes/attack-flow.html: new Liquid-driven inline SVG swimlane
(3 lanes: Attacker / ATT&CK / Detection). Fully driven by page YAML;
no CDN requests. Each stage is keyboard-focusable with ARIA labels.
- assets/js/attack-flow.js: ~1.5 KB vanilla JS for hover/focus/touch
tooltips showing ATT&CK IDs, technique names, and detection signals.
- assets/css/style.css: ~280 lines of .af-* rules covering dark/light
themes, color-blind-safe edge patterns, reduced-motion, and the
tooltip component. WCAG 2.2 AA compliant.
- _layouts/attack-chain.html: replaces Mermaid block + CDN script with
{% include attack-flow.html %} and deferred attack-flow.js load.
- attack-chains/ransomware.md + infostealers.md: enriched YAML with
mitre_tactic, mitre_techniques[], detection_status, attacker_action,
and systems fields for all stages.
Detection-status color coding: red=exploited, yellow=detected,
green=blocked (also encoded as stroke patterns for color-blind users).
Total added payload: ~25 KB inline SVG + 1.5 KB JS (Mermaid CDN removed).
https://claude.ai/code/session_01HSkU42mGpi5VjgjXd3HcZi
- Replace stage pills with chokepoint stage cards that show the invariant
prerequisite, top detection signals, and hyperlinked chokepoints per stage
- Keep Mermaid flowchart as visual attack chain illustration (future enhancement)
- Remove duplicate prose sections: per-stage breakdowns, common actor family
profiles, and layered detection strategy bullets (all now in stage cards or matrix)
- Extend stage frontmatter with detection_signals and chokepoint_links arrays
- Add actor status badges to convergence matrix (Active / Disrupted / Defunct /
Inactive) reflecting current threat landscape accuracy:
Ransomware: BlackBasta=Inactive, LockBit=Disrupted, Alphv=Defunct
Infostealers: RedLine=Disrupted (Op Magnus Oct 2024), Raccoon=Disrupted (Oct 2023)
- Fix broken chokepoint links from raw .yml paths to /chokepoints/[slug]/ URLs
https://claude.ai/code/session_013ezw6S3Ba8uSDMLYyTE6yp
When the workflow is re-triggered (manually or after a failure) on the same
calendar day, the branch chore/weekly-ttp-update-YYYY-MM-DD already exists on
the remote. The plain `git push` is rejected with "fetch first".
Use `--force` since these branches are exclusively managed by the automation —
no human commits ever land on chore/weekly-ttp-update-* branches. Force-pushing
keeps any existing open PR updated with the latest run's data.
https://claude.ai/code/session_014vs9nGxHZPgFYkbcZrRpjK
feedparser depends on sgmllib3k, which requires sgmllib — a module removed
from Python 3 stdlib. sgmllib3k fails to build on the Actions runner,
causing ModuleNotFoundError and aborting the workflow before any intel
is collected.
Replace fetch_rss_articles with a stdlib-only implementation using
xml.etree.ElementTree + email.utils.parsedate_to_datetime. Handles both
RSS 2.0 and Atom feeds. Remove feedparser from requirements-update.txt.
https://claude.ai/code/session_014vs9nGxHZPgFYkbcZrRpjK
Three bugs causing the weekly TTP update workflow to fail:
1. `thinking={"type": "adaptive"}` is not a valid Anthropic API parameter —
"adaptive" is not a recognised type (valid: "enabled"/"disabled"). This
caused a 400 error on every Claude API call. Removed the thinking param
from both analyze_article_with_claude and validate_accuracy_with_claude.
2. `git add scripts/.seen_articles.json` fails with exit code 1 on the first
run before those files are committed. Split into two git add calls and
added --ignore-errors for the optional cache files.
3. `--label "automated,ttp-update"` is interpreted as a single label name
rather than two labels, and fails if neither label exists on the repo.
Removed the --label flag entirely.
https://claude.ai/code/session_014vs9nGxHZPgFYkbcZrRpjK
Adds an interactive flowchart section between the stage pills and the
Actor Convergence Matrix on all attack chain pages. The diagram renders
from existing YAML front-matter (no data changes) — stage nodes connect
left-to-right with dotted arrows dropping to orange chokepoint nodes,
making the invariant prerequisites immediately visible at a glance.
- _layouts/attack-chain.html: new "Attack Flow Visualization" section
using Liquid-templated Mermaid syntax; Mermaid v11 loaded via jsDelivr
CDN with base theme and transparent background to match site styling
- assets/css/style.css: .ac-flowviz-wrap styles for responsive overflow
and SVG transparency
https://claude.ai/code/session_01TnmRypmV2fragVoDG3Q77G
Adds a fully automated weekly pipeline that:
- Collects threat intel from 10 RSS feeds + CISA KEV each Sunday
- Uses Claude (claude-opus-4-6 with adaptive thinking) to extract new attack
variants and map them to existing chokepoints
- Applies additive-only YAML patches (new Variations + EvolutionTimeline entries)
to chokepoint files when confidence >= 70%
- Validates existing data accuracy every run:
• MITRE ATT&CK ID deprecation/revocation check (via STIX)
• Broken Intel reference link detection (HTTP 404 check)
• Stale entry flagging (entries >90 days since LastUpdated)
• Claude factual review per chokepoint against this week's articles
- Opens a GitHub PR with a structured Markdown report for human review
before any changes are merged — never auto-merges
New files:
scripts/update_ttps.py — main pipeline script
scripts/sources.yml — intel source configuration
.github/workflows/weekly-ttp-update.yml — Actions cron (Sundays 00:00 UTC)
requirements-update.txt — Python deps (anthropic, feedparser, requests)
Setup: add ANTHROPIC_API_KEY as a GitHub Actions repository secret.
Local test: python scripts/update_ttps.py --dry-run
https://claude.ai/code/session_014vs9nGxHZPgFYkbcZrRpjK
Replaces GitHub-only links with fully rendered Jekyll pages for the
ransomware and infostealer attack chains. The centrepiece of each page
is an Actor Convergence Matrix — a table showing every tracked threat
actor as a row and each attack-chain stage as a column, with the
invariant chokepoint condition highlighted in a footer row.
Changes:
- _config.yml: remove attack-chains/ from exclude list so Jekyll
renders the markdown files into site pages
- _includes/nav.html: point "Attack Chains" nav link to the site page
(/attack-chains/) instead of the GitHub directory; add active-state
class for current-page highlighting
- _layouts/attack-chain.html: new layout that renders breadcrumb,
"Chokepoint Convergence Principle" callout, numbered stage-flow
diagram, Actor Convergence Matrix (with invariant chokepoint footer
row), and the existing markdown prose — all theme-aware via CSS vars
- assets/css/style.css: append .ac-* component classes (insight
callout, stage pills, matrix table, index cards, index hero)
- attack-chains/index.md: new landing page listing both chains with
styled cards linking to each rendered page
- attack-chains/ransomware.md: add Jekyll front matter with 5-actor ×
5-stage YAML data (BlackBasta, LockBit 3.0, Akira, Alphv/BlackCat,
Play) and invariant chokepoint conditions; add Play ransomware to the
families section (n-day exploit entry vector)
- attack-chains/infostealers.md: add Jekyll front matter with 5-actor ×
5-stage YAML data (RedLine, LummaC2, Vidar, StealC, Raccoon) and
invariant chokepoint conditions; expand LummaC2 entry with ClickFix /
LOLBin execution detail
https://claude.ai/code/session_01Y2t5fMHmVzoi2ocPs4oAxM
Two-part fix for EarlyDetections (ETW clipboard + IOK lure) not showing
on the web page:
scripts/aggregate.py:
- Add enrich_early_detections() helper that reads SigmaRule / IokRule
file paths referenced in EarlyDetections entries and embeds the raw
text as _rule_content in each entry dict
- Call it from load_chokepoints() alongside existing _sigma_* enrichment
_layouts/chokepoint.html:
- Add "Early Detection Layers" section between Detection Strategy and
Evolution Timeline; loops over cp.EarlyDetections and renders each
entry with Description, Log Sources, FP Rate, Use Case, Detection Logic,
and the embedded rule (Sigma or IOK) in a syntax-highlighted code block
with View on GitHub / Download / Copy buttons
https://claude.ai/code/session_017p1YPPCPfK5PvtKdgvcreb
New detection data sources targeting earlier kill chain stages than existing
Sysmon-based Research/Hunt/Analyst rules:
EarlyDetections section (new top-level key in clickfix-techniques.yml):
- Pre-Execution (ETW): browser clipboard write via Microsoft-Windows-Win32k
SetClipboardData; fires before user pastes; covers all variants because
clipboard seeding is an inescapable prerequisite
- Pre-Interaction (IOK): lure page fingerprint via phish.report IOK format;
clipboard_api AND execution_hint co-occurrence is the stable invariant
across all ClickFix variants regardless of visual design or threat actor
New files:
- sigma-rules/clickfix/etw-clipboard.yml: Sigma rule for ETW clipboard write
detection (Microsoft-Windows-Win32k / OLE.Clipboard providers via SilkETW)
- iok-rules/clickfix/clickfix-lure.yml: IOK rule for lure page detection;
detects navigator.clipboard.writeText + Run dialog / terminal instructions
Intel additions: Matt Graeber ETW gist, SilkETW, phish.report IOK docs + GitHub
https://claude.ai/code/session_017p1YPPCPfK5PvtKdgvcreb
Creates six Sigma rules covering the two highest-priority detection chokepoints:
EDR Bypass Techniques (sigma-rules/edr-bypass/):
- research.yml: All non-Microsoft/unsigned kernel driver loads (Sysmon EID 6)
for environment baselining; baseline before BYOVD hunt tuning
- hunt.yml: sc.exe/net.exe/taskkill.exe targeting named security agent
services/processes; includes SIEM correlation query for driver load pivot
- analyst.yml: High-fidelity named security process termination + EDRSilencer
WFP filter add detection; full response checklist + Sentinel KQL
Web Shell Persistence (sigma-rules/web-shells/):
- research.yml: All child processes from web server parents (w3wp.exe, httpd,
nginx, java, php-cgi) for baseline inventory
- hunt.yml: Web server spawning shell interpreters or recon utilities; covers
both active execution and script file drop in web-accessible directories
- analyst.yml: Web server parent + suspicious cmd/PowerShell command line
(recon, encoded commands, downloaders); covers China Chopper, Godzilla,
Behinder, LEMURLOOT, GLASSTOKEN, ProxyShell patterns
Paths match SigmaRule references in chokepoint YAML definitions.
All rules follow detection.maturity.research/hunt/analyst tag convention.
https://claude.ai/code/session_01Dhh3LC2SPpDcEB1sjiDybv
Applied the same methodology used for clickfix-techniques.yml to evaluate
and improve the OsintSources blocks in all five remaining chokepoint files.
renamed-rmm-tools.yml (moderate):
- Expanded URLScan filename query with support*.exe and verify*.exe pretexts
- Updated Shodan note to also cover SimpleHelp (CISA AA25-163A, June 2025)
- Replaced Censys AnyDesk cert query with SimpleHelp (AnyDesk certs were
revoked after the Feb 2024 breach, making that query largely historical)
- Added VT Intelligence PE metadata query for renamed RMM binaries in the wild
- Added LOLRMM.io — the community catalog of RMM tool file/network indicators
remote-execution-tools.yml (significant):
- Removed port:445 country:US — returns millions of results with zero
signal for hunting attack infrastructure; was an exposure audit query,
not a threat hunting query
- Repositioned WinRM query with clearer notes about its scope (exposure
audit, not attacker infra hunting)
- Added Shodan JARM fingerprint query for Cobalt Strike team servers —
the correct approach for hunting C2 infra paired with Impacket/NetExec
- Added hunt.io for real-time C2 infrastructure mapping
ransomware-service-manipulation.yml (minor):
- Fixed ANY.RUN URL from general trends page to the actual public
submissions feed with ransomware filter
- Added Ransomware.live for real-time ransomware group activity tracking
- VT Intelligence and GitHub queries were solid; kept as-is
web-shells.yml (minor):
- Expanded Shodan title query with FilesMan and Antak Webshell
- Expanded URLScan filename query with webshell.php and cmd.aspx
- Added Censys eval(base64_decode) query for live obfuscated PHP shells
- VT Intelligence tag:webshell query was the strongest in any file; kept
edr-bypass-techniques.yml (minor):
- Added LOLDrivers (loldrivers.io) — the canonical community catalog of
vulnerable and malicious drivers used in BYOVD attacks; was absent
entirely despite being the most important resource for this chokepoint
- All three existing queries were solid; kept as-is
https://claude.ai/code/session_01CeEB6yuJimygkCBi7ubSh3
The previous OsintSources block had three problems:
1. URL keyword query (page.url:*clickfix* / *filefix*) was removed entirely.
These are researcher-coined names — real threat actor infrastructure
never uses them in URLs. The query only surfaces honeypots and
researcher submissions, not actual malicious sites.
2. Bare clipboard API query (navigator.clipboard) had high FP rate because
the API is common on legitimate sites. Replaced with three targeted queries
that combine clipboard API presence with ClickFix-specific UI signals:
deceptive page titles (verify/captcha/update), fake CAPTCHA text
("I am not a robot"), and keyboard execution instructions (Win+R, Ctrl+V).
3. VirusTotal Pastebin staging query is increasingly stale. Modern ClickFix
campaigns (2025-2026) use DNS-based staging via nslookup (Microsoft
disclosure Feb 2026) and encoded PowerShell droppers. Both are now
covered. Legacy Pastebin note retained in the updated query's Notes field.
Added hunt.io as a third platform — it actively fingerprints ClickFix
infrastructure and is a proven community resource for this technique.
https://claude.ai/code/session_01CeEB6yuJimygkCBi7ubSh3
Adds Tailwind Play CDN alongside the existing CSS-variable colour system so
layout/typography/spacing are driven by utility classes while dark/light
theming continues via custom properties — keeping the dark default theme and
the toggle shipped in the previous commit.
Changes:
- _layouts/default.html: add Tailwind CDN script, Tailwind base on <body>,
update footer to use Tailwind layout classes
- assets/css/style.css: strip all layout/spacing/typography rules that are
now handled by Tailwind; retain colour variables, [data-theme="light"]
overrides, JS-driven state classes (.filter-chip.active, .tab-btn.active,
.tab-panel), [data-priority] card top-border selectors, pseudo-elements,
and hover states referencing CSS vars; add .badge + detail-page colour rules
for class names used in the newly updated chokepoint.html
- _includes/nav.html: replace custom layout classes with Tailwind flex/spacing
utilities; keep .site-nav, .nav-logo, .nav-github for colour
- index.html: hero, search, filter chips, results meta, grid, no-results all
use Tailwind layout utilities; functional IDs and data attributes unchanged
- _includes/chokepoint-card.html: Tailwind layout on card/link/flex containers;
colour classes (priority-badge, card-title, etc.) remain for CSS vars
- _layouts/chokepoint.html: full Tailwind layout on article/header/sections;
fix orphaned class mismatches (.data-table, .badge, .sigma-tabs etc.) that
had no matching CSS rules; keep .tab-btn/.tab-panel/.copy-btn for JS
https://claude.ai/code/session_01WYiTax4PjLA9RC8JmHjLFG