mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-15 23:44: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
177 lines
6.8 KiB
Python
177 lines
6.8 KiB
Python
#!/usr/bin/env python3
|
|
"""OSINT automation agent using SpiderFoot REST API for target profiling and reconnaissance."""
|
|
|
|
import os
|
|
import json
|
|
import time
|
|
import argparse
|
|
from datetime import datetime
|
|
|
|
import requests
|
|
|
|
|
|
def get_sf_session(base_url):
|
|
"""Create a requests session for SpiderFoot API."""
|
|
session = requests.Session()
|
|
session.headers.update({"Accept": "application/json"})
|
|
session.base_url = base_url.rstrip("/")
|
|
return session
|
|
|
|
|
|
def list_modules(session):
|
|
"""List available SpiderFoot modules."""
|
|
resp = session.get(f"{session.base_url}/api/modules", timeout=30)
|
|
resp.raise_for_status()
|
|
modules = resp.json()
|
|
return [{"name": m.get("name", ""), "descr": m.get("descr", ""),
|
|
"group": m.get("group", ""), "provides": m.get("provides", [])}
|
|
for m in modules]
|
|
|
|
|
|
def list_scan_types(session):
|
|
"""List available scan types (use cases)."""
|
|
resp = session.get(f"{session.base_url}/api/scantypes", timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
|
|
def start_scan(session, target, scan_name, use_case="all"):
|
|
"""Start a new SpiderFoot scan via REST API."""
|
|
data = {
|
|
"scanname": scan_name,
|
|
"scantarget": target,
|
|
"usecase": use_case,
|
|
}
|
|
resp = session.post(f"{session.base_url}/api/startscan", data=data, timeout=30)
|
|
resp.raise_for_status()
|
|
result = resp.json()
|
|
scan_id = result.get("scanid", result.get("id", ""))
|
|
print(f"[+] Scan started: {scan_id} (target: {target}, use_case: {use_case})")
|
|
return scan_id
|
|
|
|
|
|
def get_scan_status(session, scan_id):
|
|
"""Check scan status."""
|
|
resp = session.get(f"{session.base_url}/api/scanstatus/{scan_id}", timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
|
|
def wait_for_scan(session, scan_id, poll_interval=10, timeout=600):
|
|
"""Poll scan status until completion or timeout."""
|
|
elapsed = 0
|
|
while elapsed < timeout:
|
|
status = get_scan_status(session, scan_id)
|
|
state = status.get("status", "")
|
|
if state in ("FINISHED", "ABORTED", "ERROR-FAILED"):
|
|
print(f"[+] Scan {scan_id} completed with status: {state}")
|
|
return state
|
|
print(f"[*] Scan status: {state} (elapsed: {elapsed}s)")
|
|
time.sleep(poll_interval)
|
|
elapsed += poll_interval
|
|
print(f"[!] Scan timed out after {timeout}s")
|
|
return "TIMEOUT"
|
|
|
|
|
|
def get_scan_results(session, scan_id):
|
|
"""Retrieve all results from a completed scan."""
|
|
resp = session.get(f"{session.base_url}/api/scanresults/{scan_id}", timeout=30)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
|
|
|
|
def categorize_results(results):
|
|
"""Categorize scan results by data element type."""
|
|
categories = {
|
|
"domains": [], "ips": [], "emails": [], "credentials": [],
|
|
"dns_records": [], "urls": [], "hostnames": [], "other": [],
|
|
}
|
|
type_map = {
|
|
"INTERNET_NAME": "domains", "IP_ADDRESS": "ips", "EMAILADDR": "emails",
|
|
"LEAKSITE_CONTENT": "credentials", "DNS_TEXT": "dns_records",
|
|
"LINKED_URL_INTERNAL": "urls", "LINKED_URL_EXTERNAL": "urls",
|
|
"AFFILIATE_INTERNET_NAME": "hostnames", "CO_HOSTED_SITE": "hostnames",
|
|
}
|
|
for result in results:
|
|
data_type = result.get("type", "")
|
|
category = type_map.get(data_type, "other")
|
|
entry = {
|
|
"data": result.get("data", ""),
|
|
"type": data_type,
|
|
"module": result.get("module", ""),
|
|
"source": result.get("source", ""),
|
|
}
|
|
categories[category].append(entry)
|
|
return categories
|
|
|
|
|
|
def generate_target_profile(target, categories):
|
|
"""Generate structured OSINT profile from categorized results."""
|
|
return {
|
|
"target": target,
|
|
"profile_time": datetime.utcnow().isoformat() + "Z",
|
|
"summary": {
|
|
"domains_found": len(categories["domains"]),
|
|
"ips_found": len(categories["ips"]),
|
|
"emails_found": len(categories["emails"]),
|
|
"credentials_found": len(categories["credentials"]),
|
|
"dns_records": len(categories["dns_records"]),
|
|
"urls_found": len(categories["urls"]),
|
|
"hostnames_found": len(categories["hostnames"]),
|
|
"other_findings": len(categories["other"]),
|
|
},
|
|
"domains": [d["data"] for d in categories["domains"][:50]],
|
|
"ips": [d["data"] for d in categories["ips"][:50]],
|
|
"emails": [d["data"] for d in categories["emails"][:50]],
|
|
"leaked_credentials": len(categories["credentials"]),
|
|
"dns_records": [d["data"] for d in categories["dns_records"][:20]],
|
|
"data_sources": list({r["module"] for cat in categories.values() for r in cat}),
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="SpiderFoot OSINT Agent")
|
|
parser.add_argument("--target", required=True, help="Scan target (domain, IP, email, name)")
|
|
parser.add_argument("--server", default=os.environ.get("SPIDERFOOT_URL", "http://127.0.0.1:5001"),
|
|
help="SpiderFoot server URL")
|
|
parser.add_argument("--use-case", choices=["all", "footprint", "investigate", "passive"],
|
|
default="footprint", help="Scan use case")
|
|
parser.add_argument("--scan-name", default="", help="Scan name (default: auto-generated)")
|
|
parser.add_argument("--timeout", type=int, default=600, help="Scan timeout in seconds")
|
|
parser.add_argument("--poll-interval", type=int, default=10, help="Status poll interval in seconds")
|
|
parser.add_argument("--output", default="osint_report.json", help="Output report path")
|
|
parser.add_argument("--list-modules", action="store_true", help="List available modules and exit")
|
|
args = parser.parse_args()
|
|
|
|
session = get_sf_session(args.server)
|
|
|
|
if args.list_modules:
|
|
modules = list_modules(session)
|
|
for m in modules:
|
|
print(f" {m['name']}: {m['descr']}")
|
|
print(f"\n[+] Total modules: {len(modules)}")
|
|
return
|
|
|
|
scan_name = args.scan_name or f"osint-{args.target}-{datetime.utcnow().strftime('%Y%m%d%H%M%S')}"
|
|
scan_id = start_scan(session, args.target, scan_name, args.use_case)
|
|
final_status = wait_for_scan(session, scan_id, args.poll_interval, args.timeout)
|
|
|
|
results = get_scan_results(session, scan_id)
|
|
print(f"[+] Retrieved {len(results)} results from scan")
|
|
|
|
categories = categorize_results(results)
|
|
profile = generate_target_profile(args.target, categories)
|
|
profile["scan_id"] = scan_id
|
|
profile["scan_status"] = final_status
|
|
profile["use_case"] = args.use_case
|
|
|
|
with open(args.output, "w") as f:
|
|
json.dump(profile, f, indent=2)
|
|
print(f"[+] Domains: {profile['summary']['domains_found']}, IPs: {profile['summary']['ips_found']}, "
|
|
f"Emails: {profile['summary']['emails_found']}")
|
|
print(f"[+] Report saved to {args.output}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|