npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
When to Use
- During active incident response to identify and block adversary infrastructure
- Post-incident to document all observed adversary artifacts for future detection
- When sharing threat intelligence with ISACs, sector partners, or law enforcement
- When building detection rules in SIEM, EDR, or network security tools
- When enriching IOCs with threat intelligence context for risk scoring
Do not use for behavioral TTP analysis without accompanying technical indicators; use MITRE ATT&CK mapping for behavioral characterization.
Prerequisites
- Access to incident evidence sources: SIEM logs, EDR telemetry, memory dumps, disk images, network captures
- Threat intelligence platform (MISP, OpenCTI, ThreatConnect) for IOC management and sharing
- IOC enrichment tools: VirusTotal, OTX (AlienVault Open Threat Exchange), Shodan, DomainTools
- STIX 2.1 knowledge for structured IOC representation
- Sharing agreements with relevant ISACs (FS-ISAC, H-ISAC, IT-ISAC) or sector partners
Workflow
Step 1: Identify IOC Categories
Collect indicators across all categories from incident evidence:
Network Indicators:
- IP addresses (C2 servers, staging servers, exfiltration destinations)
- Domain names (C2 domains, phishing domains, DGA domains)
- URLs (malware download, C2 check-in, exfiltration endpoints)
- JA3/JA3S hashes (TLS client/server fingerprints)
- User-Agent strings (custom or unusual HTTP headers)
- DNS query patterns (tunneling signatures, DGA patterns)
Host Indicators:
- File hashes (MD5, SHA-1, SHA-256 of malware, tools, scripts)
- File paths (known malware installation directories)
- Registry keys (persistence mechanisms, configuration storage)
- Scheduled tasks and service names (persistence)
- Mutex/event names (malware instance synchronization)
- Named pipes (C2 communication channels, e.g., Cobalt Strike)
Email Indicators:
- Sender addresses and domains (spoofed or attacker-controlled)
- Subject lines and body content patterns
- Attachment names and hashes
- Embedded URLs
- Email header anomalies (SPF/DKIM/DMARC failures)
Step 2: Extract IOCs from Evidence Sources
Systematically extract indicators from each evidence source:
From SIEM/Log Analysis:
# Extract unique destination IPs from firewall logs
index=firewall action=blocked
| stats count by dest_ip
| where count > 100
# Extract domains from DNS query logs
index=dns query=*evil* OR query=*c2*
| stats count by queryFrom Memory Forensics:
# Extract network connections
vol -f memory.raw windows.netscan | grep ESTABLISHED
# Extract strings from suspicious process memory
vol -f memory.raw windows.memmap --pid 3847 --dump
strings -n 8 pid.3847.dmp | grep -E "(http|https)://"From Malware Analysis:
Sandbox Report IOC Extraction:
- Dropped files: 3 (hashes extracted)
- DNS queries: update.evil[.]com, cdn.malware[.]net
- HTTP connections: POST to https://185.220.101[.]42/gate.php
- Registry modified: HKCU\Software\Microsoft\Windows\CurrentVersion\Run\svcupdate
- Mutex created: Global\MTX_0x1234ABCD
- Named pipe: \\.\pipe\MSSE-1234-serverStep 3: Enrich IOCs with Context
Add threat intelligence context to each indicator:
IOC Enrichment Report:
━━━━━━━━━━━━━━━━━━━━━
IP: 185.220.101.42
VirusTotal: 12/89 vendors flag as malicious
Shodan: Open ports: 443, 8443, 80
Geolocation: Netherlands, AS208476
First Seen: 2025-10-01
Threat Intel: Associated with Qakbot C2 infrastructure
Confidence: High
TLP: AMBER
Domain: update.evil[.]com
Registration: 2025-10-28 (recently registered)
Registrar: Namecheap
WHOIS Privacy: Yes
VirusTotal: 8/89 vendors flag as malicious
DNS History: Resolved to 185.220.101.42, 91.215.85.17
Confidence: High
TLP: AMBERStep 4: Score and Prioritize IOCs
Assign confidence and risk scores to each indicator:
| Score | Confidence Level | Criteria |
|---|---|---|
| 90-100 | Confirmed Malicious | Multiple TI sources confirm, observed in active attack |
| 70-89 | Highly Suspicious | Single TI source confirms, behavioral analysis supports |
| 50-69 | Suspicious | Limited TI data, contextually suspicious |
| 30-49 | Unconfirmed | No TI matches, but anomalous in environment |
| 0-29 | Likely Benign | False positive indicators or legitimate infrastructure |
Step 5: Distribute IOCs for Detection and Blocking
Push IOCs to defensive systems for immediate protection:
- Firewall/IPS: Block C2 IPs and domains
- DNS: Sinkhole malicious domains
- EDR: Add file hashes to blocklist, create custom IOC watchlists
- Email Gateway: Block sender domains, attachment hashes, malicious URLs
- SIEM: Create correlation searches for IOC matches
- Web Proxy: Block URLs and domains in web filtering policy
Step 6: Share IOCs with Partners
Package IOCs in STIX 2.1 format for sharing:
{
"type": "indicator",
"spec_version": "2.1",
"id": "indicator--a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"created": "2025-11-15T18:00:00Z",
"modified": "2025-11-15T18:00:00Z",
"name": "Qakbot C2 Server IP",
"indicator_types": ["malicious-activity"],
"pattern": "[ipv4-addr:value = '185.220.101.42']",
"pattern_type": "stix",
"valid_from": "2025-11-15T14:23:00Z",
"confidence": 95,
"labels": ["c2", "qakbot"],
"object_marking_refs": ["marking-definition--f88d31f6-486f-44da-b317-01333bde0b82"]
}Submit to MISP, ISAC portals, and TAXII servers per sharing agreements.
Key Concepts
| Term | Definition |
|---|---|
| IOC (Indicator of Compromise) | Technical artifact observed during a security incident that indicates adversary presence (hash, IP, domain, etc.) |
| TLP (Traffic Light Protocol) | Standard for classifying the sharing restrictions of threat intelligence: WHITE, GREEN, AMBER, AMBER+STRICT, RED |
| STIX (Structured Threat Information Expression) | Standard language for representing cyber threat intelligence in a structured, machine-readable format |
| TAXII (Trusted Automated Exchange of Intelligence Information) | Transport protocol for sharing STIX-formatted threat intelligence between organizations |
| Confidence Score | Numerical rating (0-100) indicating the analyst's certainty that an indicator is truly malicious |
| IOC Lifecycle | Process of creating, validating, distributing, and eventually retiring indicators as they lose relevance |
| Defanging | Practice of modifying malicious URLs and domains in reports to prevent accidental clicks (e.g., evil[.]com) |
Tools & Systems
- MISP: Open-source threat intelligence sharing platform for managing, storing, and distributing IOCs
- VirusTotal: Multi-engine malware scanning and threat intelligence platform for IOC enrichment
- OpenCTI: Open-source cyber threat intelligence platform supporting STIX 2.1 natively
- Yeti: Open-source platform for organizing observables, indicators, and TTPs
- CyberChef: GCHQ's data transformation tool useful for decoding, defanging, and formatting IOCs
Common Scenarios
Scenario: Post-Incident IOC Package for ISAC Sharing
Context: After responding to a Qakbot infection that led to Cobalt Strike deployment, the IR team must package all IOCs for sharing with the Financial Services ISAC (FS-ISAC).
Approach:
- Compile all network, host, and email indicators from the investigation
- Enrich each IOC with VirusTotal and MISP correlation data
- Assign confidence scores based on direct observation vs. secondary correlation
- Mark all IOCs with TLP:AMBER for partner sharing
- Export as STIX 2.1 bundle and submit to FS-ISAC TAXII feed
- Create a human-readable IOC summary report for email distribution
Pitfalls:
- Including internal IP addresses or hostnames in shared IOC packages (information leakage)
- Sharing IOCs at TLP:WHITE that should be restricted to TLP:AMBER
- Not defanging URLs and domains in human-readable reports
- Sharing IP addresses of legitimate CDNs or cloud providers as malicious IOCs
Output Format
INDICATOR OF COMPROMISE REPORT
================================
Incident: INC-2025-1547
Date: 2025-11-15
TLP: AMBER
Sharing: FS-ISAC, internal SOC
NETWORK INDICATORS
Type | Value | Confidence | Context
---------|--------------------------|------------|--------
IPv4 | 185.220.101[.]42 | 95 | Qakbot C2 server
IPv4 | 91.215.85[.]17 | 90 | Cobalt Strike C2
Domain | update.evil[.]com | 95 | Staging domain
URL | hxxps://185.220[.]101.42/gate.php | 95 | C2 check-in
JA3 | a0e9f5d64349fb13191bc7...| 80 | Qakbot TLS fingerprint
HOST INDICATORS
Type | Value | Confidence | Context
---------|--------------------------|------------|--------
SHA-256 | a1b2c3d4e5f6... | 100 | Qakbot dropper
SHA-256 | b2c3d4e5f6a7... | 100 | Cobalt Strike beacon
FilePath | C:\Users\*\AppData\Local\Temp\update.exe | 85 | Dropper location
RegKey | HKCU\...\Run\svcupdate | 90 | Persistence
Mutex | Global\MTX_0x1234ABCD | 95 | Qakbot instance lock
Task | WindowsUpdate | 90 | Scheduled task persistence
EMAIL INDICATORS
Type | Value | Confidence | Context
---------|--------------------------|------------|--------
Sender | billing@spoofed[.]com | 95 | Phishing sender
Subject | "Invoice-Nov2025" | 70 | Phishing subject line
Hash | c3d4e5f6a7b8... | 100 | Malicious .docm attachment
TOTAL: 14 indicators | HIGH confidence avg: 91References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 1
api-reference.md2.2 KB
API Reference: IOC Collection Agent
Overview
Extracts indicators of compromise from text/files using regex patterns, enriches them via VirusTotal and MalwareBazaar APIs, assigns confidence scores, and exports as a STIX 2.1 bundle.
Dependencies
| Package | Version | Purpose |
|---|---|---|
| requests | >=2.28 | API enrichment calls |
| stix2 | >=3.0 | STIX 2.1 bundle creation |
CLI Usage
python agent.py --input-file incident_report.txt --vt-key <key> --output iocs.json
python agent.py --input-text "C2 at 185.220.101.42 hash a1b2c3..." --output iocs.jsonKey Functions
extract_iocs_from_text(text)
Extracts IPv4 addresses, domains, SHA-256/MD5 hashes, and URLs using regex patterns.
extract_iocs_from_file(file_path)
Reads a file and delegates to extract_iocs_from_text.
enrich_ip_virustotal(ip_address, api_key)
Queries VirusTotal v3 API for IP reputation (malicious count, ASN, country).
enrich_hash_malwarebazaar(file_hash)
Queries MalwareBazaar for file hash metadata (family, type, tags).
enrich_domain_abuseipdb(domain, api_key)
Checks domain reputation via AbuseIPDB API (abuse confidence, report count).
score_ioc(ioc_type, enrichment_data)
Assigns confidence score (0-100) based on enrichment results and detection counts.
export_stix_bundle(iocs_with_scores, output_path)
Creates STIX 2.1 Indicator objects and exports as a Bundle JSON file.
External APIs Used
| API | Endpoint | Auth | Purpose |
|---|---|---|---|
| VirusTotal v3 | /api/v3/ip_addresses/{ip} |
API key header | IP reputation |
| MalwareBazaar | https://mb-api.abuse.ch/api/v1/ |
None | Hash lookup |
| AbuseIPDB | /api/v2/check |
API key header | Domain/IP reputation |
IOC Types Supported
| Type | Regex Pattern | STIX Pattern |
|---|---|---|
| IPv4 | \b(?:\d{1,3}\.){3}\d{1,3}\b |
[ipv4-addr:value = '...'] |
| Domain | \b(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}\b |
[domain-name:value = '...'] |
| SHA-256 | \b[a-fA-F0-9]{64}\b |
[file:hashes.'SHA-256' = '...'] |
| MD5 | \b[a-fA-F0-9]{32}\b |
[file:hashes.MD5 = '...'] |
| URL | https?://[^\s"'<>]+ |
[url:value = '...'] |
Scripts 1
agent.py7.3 KB
#!/usr/bin/env python3
"""IOC Collection Agent - Extracts, enriches, and exports indicators of compromise."""
import json
import re
import logging
import argparse
from datetime import datetime
import requests
from stix2 import Indicator, Bundle
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
IPV4_PATTERN = re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b")
DOMAIN_PATTERN = re.compile(r"\b(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}\b")
SHA256_PATTERN = re.compile(r"\b[a-fA-F0-9]{64}\b")
MD5_PATTERN = re.compile(r"\b[a-fA-F0-9]{32}\b")
URL_PATTERN = re.compile(r"https?://[^\s\"'<>]+")
def extract_iocs_from_text(text):
"""Extract IOCs (IPs, domains, hashes, URLs) from unstructured text."""
iocs = {
"ipv4": list(set(IPV4_PATTERN.findall(text))),
"domain": list(set(DOMAIN_PATTERN.findall(text))),
"sha256": list(set(SHA256_PATTERN.findall(text))),
"md5": list(set(MD5_PATTERN.findall(text))),
"url": list(set(URL_PATTERN.findall(text))),
}
total = sum(len(v) for v in iocs.values())
logger.info("Extracted %d IOCs from text input", total)
return iocs
def extract_iocs_from_file(file_path):
"""Extract IOCs from a file (text report, log file, etc.)."""
with open(file_path, "r", errors="ignore") as f:
content = f.read()
return extract_iocs_from_text(content)
def enrich_ip_virustotal(ip_address, api_key):
"""Enrich an IP address using VirusTotal API."""
url = f"https://www.virustotal.com/api/v3/ip_addresses/{ip_address}"
headers = {"x-apikey": api_key}
resp = requests.get(url, headers=headers, timeout=30)
if resp.status_code == 200:
data = resp.json()["data"]["attributes"]
result = {
"ip": ip_address,
"malicious": data.get("last_analysis_stats", {}).get("malicious", 0),
"country": data.get("country", "unknown"),
"asn": data.get("asn", 0),
"as_owner": data.get("as_owner", "unknown"),
}
logger.info("VT enrichment for %s: %d malicious detections", ip_address, result["malicious"])
return result
return {"ip": ip_address, "error": resp.status_code}
def enrich_hash_malwarebazaar(file_hash):
"""Enrich a file hash using MalwareBazaar API."""
url = "https://mb-api.abuse.ch/api/v1/"
resp = requests.post(url, data={"query": "get_info", "hash": file_hash}, timeout=30)
result = resp.json()
if result.get("query_status") == "ok":
sample = result["data"][0]
return {
"hash": file_hash,
"family": sample.get("signature", "unknown"),
"file_type": sample.get("file_type", "unknown"),
"tags": sample.get("tags", []),
"first_seen": sample.get("first_seen"),
}
return {"hash": file_hash, "status": "not_found"}
def enrich_domain_abuseipdb(domain, api_key):
"""Check domain reputation via AbuseIPDB."""
url = "https://api.abuseipdb.com/api/v2/check"
headers = {"Key": api_key, "Accept": "application/json"}
resp = requests.get(url, headers=headers, params={"ipAddress": domain}, timeout=30)
if resp.status_code == 200:
data = resp.json()["data"]
return {
"domain": domain,
"abuse_confidence": data.get("abuseConfidenceScore", 0),
"total_reports": data.get("totalReports", 0),
"country": data.get("countryCode", "unknown"),
}
return {"domain": domain, "error": resp.status_code}
def score_ioc(ioc_type, enrichment_data):
"""Assign confidence score (0-100) based on enrichment results."""
if ioc_type == "ipv4":
malicious_count = enrichment_data.get("malicious", 0)
if malicious_count >= 10:
return 95
elif malicious_count >= 5:
return 80
elif malicious_count >= 1:
return 60
return 30
elif ioc_type in ("sha256", "md5"):
if enrichment_data.get("family") and enrichment_data["family"] != "unknown":
return 95
return 50
return 50
def export_stix_bundle(iocs_with_scores, output_path):
"""Export IOCs as a STIX 2.1 bundle."""
pattern_map = {
"ipv4": lambda v: f"[ipv4-addr:value = '{v}']",
"domain": lambda v: f"[domain-name:value = '{v}']",
"url": lambda v: f"[url:value = '{v}']",
"sha256": lambda v: f"[file:hashes.'SHA-256' = '{v}']",
"md5": lambda v: f"[file:hashes.MD5 = '{v}']",
}
stix_indicators = []
for ioc in iocs_with_scores:
ioc_type = ioc["type"]
pattern_fn = pattern_map.get(ioc_type)
if pattern_fn:
indicator = Indicator(
name=f"{ioc_type}: {ioc['value']}",
pattern=pattern_fn(ioc["value"]),
pattern_type="stix",
valid_from=datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"),
confidence=ioc.get("confidence", 50),
labels=[ioc_type],
)
stix_indicators.append(indicator)
bundle = Bundle(objects=stix_indicators)
with open(output_path, "w") as f:
f.write(bundle.serialize(pretty=True))
logger.info("STIX bundle exported: %d indicators to %s", len(stix_indicators), output_path)
def generate_ioc_report(iocs_with_scores):
"""Print a formatted IOC report."""
lines = [
"INDICATOR OF COMPROMISE REPORT",
"=" * 40,
f"Date: {datetime.utcnow().strftime('%Y-%m-%d %H:%M UTC')}",
f"Total IOCs: {len(iocs_with_scores)}",
"",
]
for ioc_type in ["ipv4", "domain", "sha256", "md5", "url"]:
typed = [i for i in iocs_with_scores if i["type"] == ioc_type]
if typed:
lines.append(f"\n{ioc_type.upper()} ({len(typed)}):")
for ioc in typed[:10]:
lines.append(f" {ioc['value'][:60]:60s} Confidence: {ioc.get('confidence', 'N/A')}")
print("\n".join(lines))
def main():
parser = argparse.ArgumentParser(description="IOC Collection Agent")
parser.add_argument("--input-file", help="File to extract IOCs from")
parser.add_argument("--input-text", help="Text string to extract IOCs from")
parser.add_argument("--vt-key", help="VirusTotal API key for enrichment")
parser.add_argument("--output", default="ioc_bundle.json", help="STIX bundle output")
args = parser.parse_args()
raw_iocs = {}
if args.input_file:
raw_iocs = extract_iocs_from_file(args.input_file)
elif args.input_text:
raw_iocs = extract_iocs_from_text(args.input_text)
iocs_with_scores = []
for ioc_type, values in raw_iocs.items():
for value in values:
entry = {"type": ioc_type, "value": value, "confidence": 50}
if ioc_type == "ipv4" and args.vt_key:
enrichment = enrich_ip_virustotal(value, args.vt_key)
entry["confidence"] = score_ioc("ipv4", enrichment)
entry["enrichment"] = enrichment
elif ioc_type in ("sha256", "md5"):
enrichment = enrich_hash_malwarebazaar(value)
entry["confidence"] = score_ioc(ioc_type, enrichment)
entry["enrichment"] = enrichment
iocs_with_scores.append(entry)
generate_ioc_report(iocs_with_scores)
export_stix_bundle(iocs_with_scores, args.output)
if __name__ == "__main__":
main()