mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 06:04:56 +03:00
c47eed6a64
- Fix 25 shell=True subprocess calls with list-based commands - Fix 49 verify=False in defensive skills (env-var override) - Add timeout to 231 HTTP/subprocess/socket calls - Fix 6 SQL injection patterns with whitelist validation - Replace 8 __import__() with standard imports - Remove 701 unused imports across 442 files - Add authorized-testing disclaimers to all offensive skills - Complete 11 incomplete skill directories - Expand 10 stub SKILL.md files with full content - Fix 2 YAML parse errors in frontmatter - Fix 5 pre-existing syntax errors - Convert 22 hardcoded paths/ports to environment variables - Back up 21 redundant skill pairs to .bak - Fix 2 global declaration errors - 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE) - 0 compile errors across all 724 agent.py files
171 lines
6.2 KiB
Python
171 lines
6.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Threat Intelligence Feed Integration Agent - Ingests STIX/TAXII and open-source TI feeds."""
|
|
|
|
import json
|
|
import logging
|
|
import os
|
|
import argparse
|
|
import hashlib
|
|
from datetime import datetime, timedelta
|
|
|
|
import requests
|
|
from taxii2client.v21 import Collection
|
|
from stix2 import Indicator, Bundle, parse
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def ingest_taxii_feed(taxii_url, collection_url, username, password, hours_back=24):
|
|
"""Ingest indicators from a TAXII 2.1 server collection."""
|
|
added_after = (datetime.utcnow() - timedelta(hours=hours_back)).strftime(
|
|
"%Y-%m-%dT%H:%M:%S.000Z"
|
|
)
|
|
collection = Collection(collection_url, user=username, password=password)
|
|
response = collection.get_objects(added_after=added_after, type=["indicator"])
|
|
indicators = []
|
|
for obj in response.get("objects", []):
|
|
indicator = parse(obj)
|
|
indicators.append({
|
|
"id": str(indicator.id),
|
|
"pattern": indicator.pattern,
|
|
"valid_from": str(indicator.valid_from),
|
|
"source": "taxii",
|
|
})
|
|
logger.info("Ingested %d indicators from TAXII feed", len(indicators))
|
|
return indicators
|
|
|
|
|
|
def ingest_urlhaus_feed():
|
|
"""Ingest recent malicious URLs from Abuse.ch URLhaus."""
|
|
url = "https://urlhaus-api.abuse.ch/v1/urls/recent/limit/100/"
|
|
resp = requests.post(url, timeout=30)
|
|
data = resp.json()
|
|
indicators = []
|
|
for entry in data.get("urls", []):
|
|
indicators.append({
|
|
"type": "url",
|
|
"value": entry["url"],
|
|
"threat": entry.get("threat", "unknown"),
|
|
"status": entry.get("url_status", "unknown"),
|
|
"tags": entry.get("tags", []),
|
|
"source": "urlhaus",
|
|
})
|
|
logger.info("Ingested %d URLs from URLhaus", len(indicators))
|
|
return indicators
|
|
|
|
|
|
def ingest_feodotracker():
|
|
"""Ingest C2 IP blocklist from Abuse.ch Feodo Tracker."""
|
|
url = "https://feodotracker.abuse.ch/downloads/ipblocklist_recommended.json"
|
|
resp = requests.get(url, timeout=30)
|
|
indicators = []
|
|
for entry in resp.json():
|
|
indicators.append({
|
|
"type": "ipv4",
|
|
"value": entry["ip_address"],
|
|
"port": entry.get("port"),
|
|
"malware": entry.get("malware", "unknown"),
|
|
"first_seen": entry.get("first_seen"),
|
|
"source": "feodotracker",
|
|
})
|
|
logger.info("Ingested %d C2 IPs from Feodo Tracker", len(indicators))
|
|
return indicators
|
|
|
|
|
|
def normalize_to_stix(indicators):
|
|
"""Convert raw indicators to STIX 2.1 Indicator objects."""
|
|
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}']",
|
|
}
|
|
stix_objects = []
|
|
for ioc in indicators:
|
|
ioc_type = ioc.get("type", "url")
|
|
pattern_fn = pattern_map.get(ioc_type, pattern_map["url"])
|
|
stix_ind = 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"),
|
|
labels=[ioc.get("source", "unknown")],
|
|
)
|
|
stix_objects.append(stix_ind)
|
|
logger.info("Normalized %d indicators to STIX 2.1", len(stix_objects))
|
|
return stix_objects
|
|
|
|
|
|
def deduplicate(indicators):
|
|
"""Deduplicate indicators across feeds by type:value hash."""
|
|
seen = set()
|
|
unique = []
|
|
for ioc in indicators:
|
|
key = hashlib.sha256(f"{ioc.get('type', '')}:{ioc['value']}".encode()).hexdigest()
|
|
if key not in seen:
|
|
seen.add(key)
|
|
unique.append(ioc)
|
|
logger.info("Deduplicated: %d -> %d unique indicators", len(indicators), len(unique))
|
|
return unique
|
|
|
|
|
|
def export_stix_bundle(stix_objects, output_path):
|
|
"""Export STIX 2.1 bundle to JSON file."""
|
|
bundle = Bundle(objects=stix_objects)
|
|
with open(output_path, "w") as f:
|
|
f.write(bundle.serialize(pretty=True))
|
|
logger.info("STIX bundle exported to %s (%d indicators)", output_path, len(stix_objects))
|
|
|
|
|
|
def push_to_splunk_ti(splunk_url, session_key, indicators):
|
|
"""Push indicators to Splunk ES threat intelligence framework."""
|
|
headers = {"Authorization": f"Splunk {session_key}"}
|
|
pushed = 0
|
|
for ioc in indicators:
|
|
data = {"ip": ioc["value"], "description": f"{ioc.get('source')}: {ioc['value']}"}
|
|
resp = requests.post(
|
|
f"{splunk_url}/services/data/threat_intel/item/ip_intel",
|
|
headers=headers, data=data,
|
|
verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
|
|
timeout=30,
|
|
)
|
|
if resp.status_code == 201:
|
|
pushed += 1
|
|
logger.info("Pushed %d indicators to Splunk TI", pushed)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Threat Intelligence Feed Integration Agent")
|
|
parser.add_argument("--taxii-url", help="TAXII 2.1 server URL")
|
|
parser.add_argument("--taxii-collection", help="TAXII collection URL")
|
|
parser.add_argument("--taxii-user", help="TAXII username")
|
|
parser.add_argument("--taxii-pass", help="TAXII password")
|
|
parser.add_argument("--urlhaus", action="store_true", help="Ingest URLhaus feed")
|
|
parser.add_argument("--feodo", action="store_true", help="Ingest Feodo Tracker feed")
|
|
parser.add_argument("--output", default="ti_bundle.json", help="STIX bundle output")
|
|
args = parser.parse_args()
|
|
|
|
all_indicators = []
|
|
|
|
if args.taxii_url and args.taxii_collection:
|
|
taxii_iocs = ingest_taxii_feed(
|
|
args.taxii_url, args.taxii_collection, args.taxii_user, args.taxii_pass
|
|
)
|
|
all_indicators.extend(taxii_iocs)
|
|
|
|
if args.urlhaus:
|
|
all_indicators.extend(ingest_urlhaus_feed())
|
|
|
|
if args.feodo:
|
|
all_indicators.extend(ingest_feodotracker())
|
|
|
|
unique = deduplicate(all_indicators)
|
|
stix_objects = normalize_to_stix(unique)
|
|
export_stix_bundle(stix_objects, args.output)
|
|
logger.info("Feed integration complete: %d total indicators", len(unique))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|