Agent skill

Threat Intel Campaign

by SCStelz in SCStelz/security-investigator

Turn a published threat-intelligence article into a tested threat-hunting campaign.

MITAuto-check passedSecurity

Install Threat Intel Campaign

skills CLI
$ npx skills add SCStelz/security-investigator --skill threat-intel-campaign -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install SCStelz/security-investigator threat-intel-campaign --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.github/skills/threat-intel-campaign .claude/skills/threat-intel-campaign && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
threat-intel-campaign
GitHub stars
249
Token cost
~6.9k tokens
SKILL.md length
2,749 words
Files
1
Skills in repo
22
Repo updated
First seen
Licence
MIT

At a glance

Turn a published threat-intelligence article into a tested threat-hunting campaign.

  • Works in 7 steps: Setup → Fetch & parse the feed (feed mode only) → Dedup against existing campaigns → …
  • Keywords: threat intel campaign
  • SKILL.md covers Purpose, 📑 TABLE OF CONTENTS, ⚠️ CRITICAL WORKFLOW RULES -… and Prerequisites, plus 10 more sections
  • Calls python, git and gh; reaches microsoft.com and w3.org; needs GITHUB_TOKEN

What it does

Threat Intel Campaign is an agent skill from SCStelz/security-investigator. Turn a published threat-intelligence article into a tested threat-hunting campaign. Reads a platform-agnostic RSS/Atom feed (feedurl is a parameter — nothing vendor-specific is hardcoded), triages articles from a recent window, applies a huntability relevance gate to decide whether an article warrants a campaign, then writes/tests/tunes KQL hunts and publishes them as a campaign file under queries/threat-intelligence/YYYY-MM/. Also supports a single-article mode (pass an article URL directly). Side-effect-free…

Its SKILL.md is about 6.9k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Security, covering OSINT, Security operations and Commit messages. It works with Microsoft Sentinel. The repository describes itself as: Automated security investigation tool using Microsoft MCP Servers, GitHub Copilot, Python Modules and custom copilot-instructions. The licence is MIT.

When your agent uses it

  • Keywords: threat intel campaign
  • Ingest threat intelligence
  • Write hunts from this article
  • Threat intelligence blog

Example prompts

  • “threat intel campaign”
  • “ingest threat intelligence”
  • “TI feed”
  • “/threat-intel-campaign”

Requirements

  • Python 3
  • A credential in GITHUB_TOKEN

Workflow steps

7 steps, taken from the step headings in SKILL.md.

  1. Setup
  2. Fetch & parse the feed (feed mode only)
  3. Dedup against existing campaigns
  4. Relevance gate
  5. Write / test / tune (per qualifying article)
  6. Publish the campaign file
  7. Regenerate artifacts + emit results

What it can do on your machine

Read from SKILL.md and the folder at commit 51e1385. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • python
    • git
    • gh
    • pip

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Hosts in commands or code, which the agent is likely to contact:

    • microsoft.com
    • w3.org

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names these keys or tokens, usually read from environment variables:

    • GITHUB_TOKEN

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Threat Intel Campaign loads about 6.9k tokens when it runs. Until then it costs about 217 tokens; SKILL.md has 2,749 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~217
When it runs · the whole SKILL.md, loaded when a task matches
~6.9k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from SCStelz/security-investigator at commit 51e1385, republished under its MIT licence (© SCStelz). 2,749 words, ~6,856 tokens.

Download SKILL.mdSave it as .claude/skills/threat-intel-campaign/SKILL.md (or your agent's skills folder).
name
threat-intel-campaign
description
Turn a published threat-intelligence article into a tested threat-hunting campaign. Reads a platform-agnostic RSS/Atom feed (feed_url is a parameter — nothing vendor-specific is hardcoded), triages articles from a recent window, applies a huntability relevance gate to decide whether an article warrants a campaign, then writes/tests/tunes KQL hunts and publishes them as a campaign file under queries/threat-intelligence/YYYY-MM/. Also supports a single-article mode (pass an article URL directly). Side-effect-free: it writes campaign files and regenerates the manifest/TOCs but performs NO git commits or PRs — branch/PR orchestration belongs to the calling automation. Trigger keywords: "threat intel campaign", "ingest threat intelligence", "TI feed", "write hunts from this article", "threat intelligence blog", "build a hunting campaign".

Threat Intelligence Campaign Authoring — Instructions

Purpose

This skill converts published threat-intelligence reporting into tested, tuned, publish-ready threat-hunting campaigns that land in queries/threat-intelligence/YYYY-MM/. It exists to be driven either:

  • Interactively — a human gives one article URL ("read this article and write/test/tune hunts"), or
  • Unattended — a scheduled automation passes a feed URL and the skill triages everything published in a recent window.

It does the authoring (parse → triage → relevance gate → write → test → tune → publish files → regenerate manifest/TOCs). It deliberately does NOT create branches, commits, or pull requests. That orchestration — and the per-article PR isolation — belongs to the calling workflow. This keeps the skill reusable and free of git side effects when a human runs it.

What this skill produces:

OutputDescription
Campaign file(s)queries/threat-intelligence/YYYY-MM/<slug>.md in the standard campaign format
Regenerated artifacts.github/manifests/discovery-manifest.yaml + per-file Quick Reference TOCs
Structured resultA JSON array (one entry per article) the calling automation consumes to drive per-article PRs
Human summaryA readable per-article decision log
In-chat hunt findings summaryA per-article report of what the test runs actually surfaced — real hits, false positives to tune, and follow-up actions. Emitted to chat/run output only; never written to a tracked file. This is where concrete findings live, keeping the committed campaign file PII-free.

📑 TABLE OF CONTENTS

  1. Critical Workflow Rules
  2. Prerequisites
  3. Inputs / Parameters
  4. Invocation Modes
  5. Execution Workflow — Phase 0–6
  6. The Relevance Gate (Huntability Rubric)
  7. Writing / Testing / Tuning Queries
  8. Campaign File Format
  9. Structured Output Contract
  10. In-Chat Hunt Findings Summary
  11. Known Pitfalls
  12. Quality Checklist

⚠️ CRITICAL WORKFLOW RULES - READ FIRST ⚠️

  1. No git side effects. This skill NEVER runs git commit, git push, gh pr create, or any branch operation. It writes files and regenerates the manifest/TOCs only. Publishing (branch + commit + PR per article) is the calling automation's job. If a human is running this interactively, leave the files in the working tree for them to review.

  2. feed_url is a parameter — nothing vendor-specific is hardcoded. The skill handles any RSS 2.0 or Atom feed. The Microsoft Threat Intelligence feed is just one value a caller may pass; do not assume it.

  3. Advanced Hunting (≤30d) is the primary test/tune engine. Write and validate every query against RunAdvancedHuntingQuery within a 30-day window. Fall back to the Sentinel Data Lake (query_lake, >30d) only when you need additional supporting evidence that AH's 30-day cap cannot provide (e.g., confirming a rare IOC's longer-term absence/presence). Follow the Tool Selection Rule and timestamp-adaptation guidance in copilot-instructions.md.

  4. ⛔ Evidence-based "tested" claims only. A query may be described as tested in the campaign file only if it was actually executed. If a query could not be run (table absent, AH safety filter, telemetry gap), say so explicitly and mark cd_ready: false with honest adaptation_notes. Never imply validation that did not happen. Follow the Evidence-Based Analysis rule in copilot-instructions.md.

  5. ⛔ Committed output is PII-free — but published IOCs are NOT PII. Test/tune runs against the live tenant, but campaign files are version-controlled. NEVER paste real tenant entities (your UPNs, hostnames, IPs, workspace/tenant GUIDs, app names) into a campaign file. The article's published IOCs (hashes, domains, URLs, certs, filenames) are the opposite — they are public, shareable, and MUST be included verbatim in the IOC Reference table and the IOC-sweep queries (this is how the committed companion files do it). Do not placeholder or omit a published IOC. Concrete tenant findings from test runs belong in the In-Chat Hunt Findings Summary, not the file. Perform a PII sanity-check before finalizing each file.

  6. ⛔ Every IOC must trace to the article. Never invent one. Copy IOCs from the article's "Indicators of compromise" table (and any inline-cited indicators) exactly. Before finalizing, re-open the article's IOC section and confirm each hash/domain/URL/filename in your file appears there character-for-character. Hallucinated or mis-transcribed IOCs are a critical evidence-integrity failure — they produce false detections and erode trust. If an indicator only appears in narrative prose (not the IOC table), label it as such.

  7. Reference, don't reinvent. Use the kql-query-authoring skill's discipline for query construction (schema validation via kql-search MCP, table pitfalls, TimeGenerated vs Timestamp), and the detection-authoring skill's CD Metadata Contract for the <!-- cd-metadata --> block on every query. Read those SKILL.md files when authoring.

  8. Workspace selection. Follow the SENTINEL WORKSPACE SELECTION rule in copilot-instructions.md. In unattended runs the caller will specify the workspace; if exactly one exists, auto-select and state it.

  9. Read config.json for workspace ID, tenant, and Azure MCP parameters before querying.

  10. Quiet runs are a success, not a failure. If nothing in the window qualifies, that is a valid, expected outcome. Emit an empty/"skipped"-only result set and stop — do not lower the bar to manufacture a campaign.


Prerequisites

DependencyUsed for
kql-search MCP (GITHUB_TOKEN set)Schema validation, table discovery, community query examples
Sentinel Triage MCP (RunAdvancedHuntingQuery)Primary query testing/tuning (≤30d)
Sentinel Data Lake MCP (query_lake)Supporting evidence only (>30d)
Microsoft Learn MCPGrounding TTP/technique/error-code explanations
Python 3 (stdlib xml.etree, urllib)RSS/Atom parsing — no external dependency required
web_fetch / web_search toolsFetching article bodies and the feed
.github/manifests/build_manifest.py, scripts/generate_tocs.pyPost-processing

Feed parsing uses Python stdlib (xml.etree.ElementTree) so it works unattended without pip install. feedparser may be used if already installed, but never assume it.


Inputs / Parameters

ParameterDefaultDescription
feed_url(required in feed mode)Any RSS 2.0 / Atom feed URL
article_url(none)A single article to process directly (single-article mode)
lookback_hours24How far back to consider feed entries (by published date)
max_campaigns3Cap on campaigns produced per run (bounds tenant query load + review burden)
workspace_idfrom config.jsonSentinel workspace for testing
min_queries / max_queries4 / 9Soft bounds on queries per campaign

Invocation Modes

A. Single-article mode — caller passes article_url (the classic "read this article and write hunts" prompt). → Skip Phase 1 (feed) and the time filter. Still run Phase 2 (dedup) and Phase 3 (relevance gate) unless the human explicitly says "build it regardless". Then Phases 4–6 for that one article.

B. Feed mode — caller passes feed_url (+ optional lookback_hours). → Full Phase 0–6 across all qualifying entries in the window, capped at max_campaigns.


Execution Workflow

Phase 0 — Setup
  1. Read config.json (workspace ID, tenant, subscription, Azure MCP params).
  2. Resolve workspace per the selection rule. State which workspace is in use.
  3. Confirm kql-search + Triage MCP are available (needed for testing).
Phase 1 — Fetch & parse the feed (feed mode only)

Fetch feed_url and parse entries with Python stdlib so it works for both RSS and Atom:

python
import sys, urllib.request, datetime as dt
import xml.etree.ElementTree as ET
from email.utils import parsedate_to_datetime

feed_url = sys.argv[1]
lookback_hours = int(sys.argv[2]) if len(sys.argv) > 2 else 24
cutoff = dt.datetime.now(dt.timezone.utc) - dt.timedelta(hours=lookback_hours)

raw = urllib.request.urlopen(urllib.request.Request(feed_url, headers={"User-Agent": "ti-campaign/1.0"}), timeout=30).read()
root = ET.fromstring(raw)
ATOM = "{http://www.w3.org/2005/Atom}"

def text(el, *tags):
    for t in tags:
        for tag in (t, ATOM + t):
            f = el.find(tag)
            if f is not None and f.text:
                return f.text.strip()
    return None

def parse_date(s):
    if not s: return None
    try: return parsedate_to_datetime(s)              # RSS pubDate
    except Exception:
        try: return dt.datetime.fromisoformat(s.replace("Z", "+00:00"))  # Atom ISO
        except Exception: return None

entries = list(root.iter("item")) or list(root.iter(ATOM + "entry"))  # RSS first, else Atom
for e in entries:
    title = text(e, "title")
    link = text(e, "link")
    if not link:
        a = e.find(ATOM + "link")
        link = a.get("href") if a is not None else None
    pub = parse_date(text(e, "pubDate", "published", "updated"))
    if pub and pub >= cutoff:
        print(f"{pub.isoformat()}\t{title}\t{link}")

Run it with the powershell tool (python script.py <feed_url> <lookback_hours>). Collect (published, title, link) for entries inside the window. If the feed only exposes summaries, you'll fetch full bodies in Phase 3.

Phase 2 — Dedup against existing campaigns

For each candidate URL, check whether it's already been turned into a campaign:

  • grep for the article URL (and a normalized form without trailing slash / query string) across queries/threat-intelligence/**.
  • Also grep the proposed slug. If a match exists → mark decision: "skipped", reason: "already published", and drop it from the work list.
Phase 3 — Relevance gate

For each remaining candidate, fetch the full article body (web_fetch) and apply the Huntability Rubric. Produce a decision (campaign / skipped) with a one-line reason. Rank campaign candidates by huntability confidence and keep the top max_campaigns.

Phase 4 — Write / test / tune (per qualifying article)

See Writing / Testing / Tuning Queries. Output: a set of validated queries, each with an honest cd-metadata block and tuning notes.

Phase 5 — Publish the campaign file

Write queries/threat-intelligence/YYYY-MM/<slug>.md in the exact Campaign File Format. YYYY-MM = the article's publication month. <slug> = short, kebab/underscore, descriptive (e.g., soho_router_dns_hijacking). Do NOT hand-write the Quick Reference TOC — generate_tocs.py creates it.

Phase 6 — Regenerate artifacts + emit results
  1. python .github/manifests/build_manifest.py (regenerate + validate; fix any error-level warnings on your new file).
  2. python scripts/generate_tocs.py (insert the Quick Reference TOC).
  3. Emit the Structured Output Contract JSON + a human summary.
  4. Emit the In-Chat Hunt Findings Summary — the per-article report of what the test runs actually found (hits, false positives to tune, follow-up actions). Chat/run output only; never write it to a tracked file.
  5. Stop. No git.

The Relevance Gate — Huntability Rubric

This is the judgment step: does this article warrant a hunting campaign? Decide with explicit gates, not vibes. Cite the evidence from the article for each gate.

Hard gates (BOTH must PASS to build)
GatePASS criteriaFAIL examples
G1 — Huntable behaviorArticle describes specific, observable attacker behaviors mappable to ≥1 ATT&CK technique (process exec, persistence mechanism, C2 pattern, auth abuse, mailbox manipulation, registry/file artifacts, etc.)"Threat actor targeted sector X" with no technique detail; pure attribution/geopolitics
G2 — Telemetry coverage≥1 behavior or IOC maps to a table we ingest (Device*, Email*, Identity*, Signin*/EntraId*, Cloud*, Audit*, OfficeActivity, network/DNS)Behaviors only observable in telemetry we don't collect (e.g., physical, OT-only with no connector, third-party logs not onboarded)
Confidence signals (raise/lower priority among passing candidates)
SignalEffect
Concrete IOCs (hashes, domains, IPs, filenames, command lines, registry keys, user-agents)↑↑ strong — enables direct-match hunts
Multiple distinct mappable TTPs (richer attack chain)↑
Named ATT&CK technique IDs in the article↑
Novel TTP not already covered by an existing campaign/query↑
Overlaps heavily with an existing campaign↓ (consider extending the existing file instead of a new one)
Auto-skip categories (do not build)
  • Product/feature announcements, GA/preview notices, roadmap posts
  • Analyst-recognition / "named a Leader" / awards
  • Strategy, opinion, policy, or business-update posts
  • Event/webinar recaps and partner marketing
  • Pure data-breach news with no attacker TTPs/IOCs
Decision rule

BUILD if G1 PASS and G2 PASS and (concrete IOCs present OR ≥2 distinct mappable TTPs). Otherwise SKIP with a specific reason (which gate failed / which auto-skip category).

Record the rubric outcome in the structured result reason field (e.g., "BUILD: 4 IOCs + 6 mappable TTPs (endpoint, identity)" or "SKIP: product announcement, G1 fail").


Show full SKILL.md (1,150 more words)Show less

Writing / Testing / Tuning Queries

For each qualifying article:

  1. Extract the TTPs and IOCs. Map each TTP to ATT&CK technique IDs (use Microsoft Learn MCP to confirm technique semantics). Build the IOC table (hashes, domains, IPs, filenames, etc.).

  2. Pick detection surfaces. Map TTPs → tables. Prefer XDR-native tables for AH testing. Check the discovery manifest + grep queries/** first — if an existing query file already covers a TTP, reuse/adapt its pattern and cite it as a companion rather than duplicating.

  3. Author each query following the kql-query-authoring discipline:

    • Validate the table/columns via kql-search MCP (get_table_schema) before writing.
    • Respect the table pitfalls in copilot-instructions.md (e.g., TimeGenerated vs Timestamp, dynamic-field parse_json, IpAddress casing).
    • Datetime filter first; project a useful, PII-light column set; order by/summarize to bound output.
  4. Test in Advanced Hunting (≤30d). Run every query via RunAdvancedHuntingQuery. Apply the Step-5 zero-result sanity check from copilot-instructions.md — a 0-row result must be verified correct (e.g., a direct-IOC sweep returning 0 in a clean environment is the desired outcome; a 0 from a broken filter is not). As you test, record the findings for each query — row count, whether hits look like true/false positives, and any notable entities — so you can build the In-Chat Hunt Findings Summary in Phase 6. These raw findings stay in chat; they do NOT go into the campaign file.

  5. Tune. If a query is noisy, add targeted exclusions (trusted publishers, known service accounts, expected automation) and document them in Tuning Notes — generically, never with live tenant identifiers. Re-run after tuning.

  6. Supporting evidence via Data Lake (>30d) — only if needed. If 30 days is insufficient to characterize prevalence/absence of a rare IOC, run a scoped query_lake (adapt Timestamp→TimeGenerated for Sentinel/LA tables). Use this for evidence, not as the primary engine.

  7. CD metadata. Attach a <!-- cd-metadata --> block to every query per the detection-authoring CD Metadata Contract. Set cd_ready: true only for high-fidelity, low-noise queries that actually validated cleanly; otherwise cd_ready: false with adaptation_notes explaining what's needed.

  8. IOC freshness note. IOC-match queries (hashes/domains) rot. Note that operators rotate IOCs and recommend periodic refresh from current MS TI / VirusTotal / a TI indicator table.


Campaign File Format

Match the existing files in queries/threat-intelligence/YYYY-MM/ exactly. Structure:

markdown
# <Threat / Actor / Campaign> — Threat Hunts

**Created:** YYYY-MM-DD  
**Platform:** Microsoft Defender XDR | Microsoft Sentinel | Both  
**Tables:** <exact KQL table names, comma-separated>  
**Keywords:** <attack techniques, actor names, tooling, artifacts, field names>  
**MITRE:** <technique/tactic IDs, comma-separated>  
**Domains:** <threat-pulse domain tags: incidents|identity|spn|endpoint|email|admin|cloud|exposure>  
**Timeframe:** Last N days (configurable)  
**Source:** [<Article title> (<date>)](<article_url>)

---

## Threat Overview
<2–4 sentence synopsis grounded in the article. Include actor attribution if stated.>

### TTP Summary
| Capability | TTP |
|---|---|
| ... | ... |

### ⚠️ Hunt Pitfalls
| Pitfall | Mitigation |
|---|---|
| ... | ... |

---

## IOC Reference
<Table of published IOCs (hashes/domains/IPs/filenames). Note they rot; recommend refresh.>

---

## Query 1: <Title>

**Purpose:** <what it detects, and what a clean result looks like>  
**Severity:** <Low|Medium|High>  
**MITRE:** <technique IDs>  
<!-- cd-metadata
cd_ready: true|false
cd_table: <PrimaryTable>
cd_frequency: NRT|Hourly|...
cd_severity: <Low|Medium|High>
cd_mitre: ["T...."]
cd_entities: ["device","file","account",...]
cd_adaptation_notes: "<honest notes>"
-->
` ` `kql
<tested query>
` ` `
**Expected results:** <what to expect; 0-row interpretation if a direct IOC sweep>

---

## Query 2: ...
...

---

## General Tuning Notes
1. IOC refresh ...
2. Telemetry gaps ...
3. CD-readiness summary ...

---

## References
- Microsoft Threat Intelligence — [<title>](<url>)
- MITRE ATT&CK — [<technique/actor>](<attack url>)
- Companion files: [`queries/<domain>/<file>.md`](...)

Header field requirements (enforced by build_manifest.py): Tables, Keywords, MITRE, and Domains are mandatory. Domains values must come from the valid set (incidents, identity, spn, endpoint, email, admin, cloud, exposure). A missing Domains is an error-level manifest warning.

Do NOT pre-write a ## Quick Reference — Query Index section — generate_tocs.py inserts it. Pre-creating it breaks the strip-and-reinsert logic.


Structured Output Contract

At the end of every run, emit a JSON array (one object per article considered) so the calling automation can isolate per-article PRs. Print it in a fenced ```json block:

json
[
  {
    "article_title": "SOHO router compromise leads to DNS hijacking...",
    "article_url": "https://www.microsoft.com/en-us/security/blog/2026/04/07/...",
    "published": "2026-04-07T00:00:00Z",
    "decision": "campaign",
    "reason": "BUILD: 6 mappable TTPs + IOCs (endpoint, identity)",
    "file_path": "queries/threat-intelligence/2026-04/dns_hijacking_soho_compromise.md",
    "queries_written": 9,
    "queries_tested": 9,
    "queries_cd_ready": 4,
    "domains": ["endpoint", "identity"]
  },
  {
    "article_title": "Microsoft named a Leader in ...",
    "article_url": "https://www.microsoft.com/en-us/security/blog/2026/04/05/...",
    "published": "2026-04-05T00:00:00Z",
    "decision": "skipped",
    "reason": "SKIP: analyst-recognition post, G1 fail",
    "file_path": null
  }
]

decision ∈ campaign | skipped. For skipped, file_path is null. Follow the JSON block with a short human-readable summary (counts, what was built, what was skipped and why).


In-Chat Hunt Findings Summary

After the structured JSON, emit a per-article findings summary that reports what the test runs actually surfaced in the tenant. This is the counterpart to the PII-free campaign file: the file is the reusable, sanitized hunt definition; this summary is the investigation result of running those hunts right now.

Where it goes: chat / run output only. Never write it to a tracked file (not the campaign file, not any queries/** or docs/** file). For unattended runs, the calling automation decides where to route it (e.g., PR description, notification, ticket) per its own data-handling policy — the skill just emits it.

PII posture: Unlike the committed campaign file, this summary may include the concrete entities an analyst needs to act (device names, UPNs, IPs, file hashes, sender addresses, message IDs) — it is investigation output to an operator who already has tenant access, the same as any other investigation skill's chat output. Do not redact what's needed for triage; do not persist it to the repo.

Skip when nothing actionable: If every query returned a verified-clean 0 (e.g., all IOC sweeps clean in a tenant where the IOCs predate the AH window), say so in one line per query rather than padding. The value is in the hits and the FPs, not in restating "0 rows" decoratively.

Format
markdown
## 🔎 Hunt Findings — <Article Title> (<run date>)
**Workspace:** <name>  **Lookback:** <window>  **Queries run:** <n>

| # | Query | Rows | Assessment | Action |
|---|-------|------|------------|--------|
| 1 | AI-brand display-name spoof | 0 | ✅ Clean (tuned; no spoofed-domain phish in window) | None |
| 4 | Fake-AI installer download | 3 | 🟠 2 likely FP (sanctioned vendor), 1 to review | Verify host on DEVICE-X; see below |
| 7 | Endpoint IOC sweep | 0 | ✅ Clean — IOCs predate 30d AH window | Re-run in Data Lake (90d) for retrospective |

### 🔴 True / suspected positives
- **Q4 — DEVICE-X / user@contoso.com:** downloaded `seedance_setup_x64.exe` from `hxxp://…` at <time>. Not a sanctioned vendor host. **Follow-up:** isolate/triage device, pivot Q5 (execution) + Q7 (C2).

### 🟠 False positives to tune
- **Q4 — 2 rows:** `<vendor>` installer from `downloads.<vendor>.com` — legitimate. **Tuning:** add `downloads.<vendor>.com` to the trusted-host list (reflected generically in the file's Tuning Notes, not as a literal tenant value).

### ⚠️ Follow-up actions
- [ ] Re-run Q3/Q7 IOC sweeps in Sentinel Data Lake (>30d) for retrospective coverage.
- [ ] Confirm DEVICE-X download disposition with endpoint team.
- [ ] If positives confirmed, consider promoting Q1/Q4/Q5 to custom detections (see detection-authoring skill).

Closing the loop with the campaign file: when a finding reveals a tuning need (e.g., a legitimate host triggering FPs), capture the generic fix in the campaign file's Tuning Notes / adaptation_notes (e.g., "exclude sanctioned vendor download hosts") — never the literal tenant value. The findings summary names the specific host; the file describes the class of exclusion.


Known Pitfalls

PitfallMitigation
Feed exposes only summaries, not full TTPsAlways web_fetch the full article body before the relevance gate and query authoring
Atom vs RSS schema differences (<entry>/<published> vs <item>/<pubDate>)The Phase-1 parser handles both; never hardcode one shape
Treating a marketing/recognition post as huntableApply the auto-skip categories; G1 must find real behavior
Claiming a query is "tested" when it errored or hit the AH safety filterOnly mark tested if it ran and returned a sane result; otherwise cd_ready: false + notes
Pasting live tenant entities (from test runs) into the committed fileCampaign files are PII-free; test data informs tuning notes only
Placeholdering or omitting an article's published IOCsPublished IOCs are public, not PII — transcribe them verbatim into the IOC Reference table AND the IOC-sweep queries. Never ship <HASH1> placeholders or "see table" stand-ins.
Inventing / mis-transcribing an IOCEvery hash/domain/URL/filename must appear character-for-character in the article's IOC table. Re-verify against the source before finalizing; a hallucinated IOC is a critical evidence-integrity failure.
Using Data Lake as the primary engineAH ≤30d is primary; Data Lake >30d for supporting evidence only
Hand-writing the Quick Reference TOCLet generate_tocs.py generate it
Forgetting to regenerate the manifestAlways run build_manifest.py after writing files; resolve error-level warnings
Duplicating an existing campaign/queryGrep first; extend or cite companions instead of duplicating
Performing git operationsNever — publishing is the automation's responsibility
Putting real findings/entities in the committed file, OR omitting them from chatTwo separate outputs: campaign file = PII-free reusable hunt; In-Chat Hunt Findings Summary = real hits/FPs/follow-ups (chat only). Don't merge them.

Quality Checklist

Before emitting results, confirm:

  • Every candidate has an explicit decision + evidence-based reason
  • Each campaign file matches the standard format (header fields complete, Domains valid)
  • Every query has a cd-metadata block with an honest cd_ready value
  • Every query was tested in Advanced Hunting (or its non-execution is documented)
  • Zero-result queries were sanity-checked (desired vs broken)
  • No live tenant PII anywhere in the committed file
  • Every published IOC from the article is present verbatim (no placeholders/omissions) and every IOC in the file traces back to the article's IOC table (no invented/mis-transcribed indicators)
  • build_manifest.py runs clean (no error-level warnings on the new file)
  • generate_tocs.py has inserted the Quick Reference TOC
  • Structured JSON result emitted + human summary
  • In-Chat Hunt Findings Summary emitted (hits, FPs to tune, follow-ups) — chat/run output only, not a tracked file
  • No git operations performed

© SCStelz, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .github/skills/threat-intel-campaign of SCStelz/security-investigator.

Open the folder on GitHubat commit 51e1385

Compare with similar skills

Threat Intel Campaign next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Threat Intel Campaign compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Threat Intel Campaign this skillSCStelz/security-investigator249—~6.9kAutomated safety check: PassMIT
Building Detection Rules With Sigmamukul975/Anthropic-Cybersecurity-Skills34k—~2.7kAutomated safety check: PassApache-2.0
Unified Secops Platformvinayaklatthe/microsoft-security-skills175—~2.1kAutomated safety check: PassMIT
Enrich Iocdandye/ai-runbooks127—~702Automated safety check: PassApache-2.0
Detecting Azure Service Principal Abusemukul975/Anthropic-Cybersecurity-Skills34k—~2.1kAutomated safety check: PassApache-2.0
Implementing Stix Taxii Feed Integrationmukul975/Anthropic-Cybersecurity-Skills34k—~2.7kAutomated safety check: PassApache-2.0

Similar skills

  • Building Detection Rules With Sigma

    mukul975/Anthropic-Cybersecurity-Skills

    Builds vendor-agnostic detection rules using the Sigma rule format for threat detection across SIEM platforms including Splunk, Elastic, and Microsoft Sentinel.

    34k GitHub stars~2.7k tokensUpdated 1 mo ago
    SecurityAuto-check passed
  • Unified Secops Platform

    vinayaklatthe/microsoft-security-skills

    Guidance for the Microsoft unified security operations platform that brings Microsoft Sentinel, Microsoft Defender XDR, Security Copilot, Threat Intelligence, and Microsoft Security Exposure…

    175 GitHub stars~2.1k tokensUpdated 3 mo ago
    SecurityAuto-check passed
  • Enrich Ioc

    dandye/ai-runbooks

    Enrich an IOC (IP, domain, hash, URL) with threat intelligence.

    127 GitHub stars~702 tokensUpdated 1 mo ago
    SecurityAuto-check passed
  • Detecting Azure Service Principal Abuse

    mukul975/Anthropic-Cybersecurity-Skills

    Detect Azure service principal abuse in Microsoft Entra ID using KQL detection queries (Sentinel/Splunk) against Azure AD Audit and Sign-in Logs, covering added credentials, privileged role…

    34k GitHub stars~2.1k tokensUpdated 1 mo ago
    SecurityAuto-check passed
  • Implementing Stix Taxii Feed Integration

    mukul975/Anthropic-Cybersecurity-Skills

    Implements a STIX 2.1/TAXII 2.1 threat-intelligence feed consumer and producer in Python, covering TAXII server discovery, collection polling, parsing STIX bundles with the stix2 library, and…

    34k GitHub stars~2.7k tokensUpdated 1 mo ago
    SecurityAuto-check passed
  • Analyzing Azure Activity Logs For Threats

    mukul975/Anthropic-Cybersecurity-Skills

    Queries Azure Monitor activity logs and sign-in logs via azure-monitor-query to detect suspicious administrative operations, impossible travel, privilege escalation, and resource modifications.

    34k GitHub stars~609 tokensUpdated 1 mo ago
    SecurityAuto-check passed

More from SCStelz/security-investigator

All 22 skills in this repo
  • Ca Policy Investigation

    SCStelz/security-investigator

    A skill your agent uses when asked to investigate Conditional Access policy changes, sign-in failures related to CA policies (error codes 53000, 50074, 530032), or suspected policy…

    249 GitHub stars~3.8k tokensUpdated 2 days ago
    Auto-check passed
  • Context Memory Review

    SCStelz/security-investigator

    Weekly review of an investigation tenant-context memory file against the most recent SOC scan reports (e.g.

    249 GitHub stars~3.7k tokensUpdated 2 days ago
    Auto-check passed
  • Heatmap Visualization

    SCStelz/security-investigator

    A skill your agent uses when asked to create heatmaps, visualize patterns over time, show activity grids, or display aggregated data in a matrix format.

    249 GitHub stars~3.4k tokensUpdated 2 days ago
    Auto-check passed
  • AI Agent Activity

    SCStelz/security-investigator

    Report/investigate RUNTIME ACTIVITY of AI agents (Agent 365 / Copilot Studio / M365 Copilot / Work IQ) — agents used, tools/connectors, channels, tokens, prompt/reply content, and Prompt Shield…

    249 GitHub stars~17k tokensUpdated 2 days ago
    Auto-check passed
  • AI Agent Posture

    SCStelz/security-investigator

    Audit or report on AI agent security posture across Copilot Studio, Microsoft 365 Copilot, Microsoft Foundry, and third-party agents.

    249 GitHub stars~21k tokensUpdated 2 days ago
    Auto-check passed
  • App Registration Posture

    SCStelz/security-investigator

    Audit Entra ID app registration and service principal security posture.

    249 GitHub stars~21k tokensUpdated 2 days ago
    Auto-check passed

Categories

Questions about Threat Intel Campaign

What does Threat Intel Campaign do?

Turn a published threat-intelligence article into a tested threat-hunting campaign. Threat Intel Campaign is an agent skill from SCStelz/security-investigator. Turn a published threat-intelligence article into a tested threat-hunting campaign.

When should I use Threat Intel Campaign?

Threat Intel Campaign fits situations like: keywords: threat intel campaign; ingest threat intelligence; write hunts from this article; threat intelligence blog.

How do I install Threat Intel Campaign in Claude Code?

Run `npx skills add SCStelz/security-investigator --skill threat-intel-campaign -a claude-code`. Or copy the skill folder (.github/skills/threat-intel-campaign in SCStelz/security-investigator) into .claude/skills/threat-intel-campaign in your project. Claude Code loads it when a task matches its description.

How do I install Threat Intel Campaign in Codex?

Run `npx skills add SCStelz/security-investigator --skill threat-intel-campaign -a codex`. Or copy the skill folder (.github/skills/threat-intel-campaign in SCStelz/security-investigator) into .agents/skills/threat-intel-campaign in your project. Codex loads it when a task matches its description.

Can I use Threat Intel Campaign in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add SCStelz/security-investigator --skill threat-intel-campaign -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/threat-intel-campaign, .gemini/skills/threat-intel-campaign, .github/skills/threat-intel-campaign and .opencode/skills/threat-intel-campaign in your project.

What does Threat Intel Campaign need to run?

Going by SKILL.md and its folder, Threat Intel Campaign needs the command-line tools its instructions call (python, git, gh and pip) and credentials named GITHUB_TOKEN. Our summary lists: Python 3; A credential in GITHUB_TOKEN.

Does Threat Intel Campaign access the network?

SKILL.md names 2 domains. In commands or code: microsoft.com and w3.org; the agent is likely to contact these when it follows the instructions. This is read from the text; nothing was executed.

Is Threat Intel Campaign safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Threat Intel Campaign use?

Threat Intel Campaign is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Threat Intel Campaign use?

About 6.9k tokens (SKILL.md is roughly 27k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Threat Intel Campaign?

Skills that share tags, products or a category with Threat Intel Campaign: Building Detection Rules With Sigma (mukul975/Anthropic-Cybersecurity-Skills, 34k stars), Unified Secops Platform (vinayaklatthe/microsoft-security-skills, 175 stars), Enrich Ioc (dandye/ai-runbooks, 127 stars) and Detecting Azure Service Principal Abuse (mukul975/Anthropic-Cybersecurity-Skills, 34k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Threat Intel Campaign?

SCStelz (a GitHub user) maintains it in SCStelz/security-investigator, which has 249 GitHub stars. The repository holds 22 skills in this directory. The repository was last updated on October 8, 2026.

Source: SCStelz/security-investigator on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.