mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-26 14:10:59 +03:00
Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
Complete skill folder anatomy across all cybersecurity skills: - scripts/agent.py: 80-150 line Python agents using real libraries (impacket, boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.) - references/api-reference.md: real API documentation with method signatures - LICENSE: MIT license for all skill folders
This commit is contained in:
@@ -0,0 +1,191 @@
|
||||
#!/usr/bin/env python3
|
||||
# For authorized penetration testing and lab environments only
|
||||
"""External Reconnaissance Agent - Maps organization attack surface using passive OSINT."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
|
||||
import requests
|
||||
import shodan
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def enumerate_subdomains_crtsh(domain):
|
||||
"""Discover subdomains via certificate transparency logs."""
|
||||
url = f"https://crt.sh/?q=%.{domain}&output=json"
|
||||
resp = requests.get(url, timeout=60)
|
||||
subdomains = set()
|
||||
if resp.status_code == 200:
|
||||
for entry in resp.json():
|
||||
for name in entry.get("name_value", "").split("\n"):
|
||||
name = name.strip().lower()
|
||||
if name and "*" not in name and domain in name:
|
||||
subdomains.add(name)
|
||||
logger.info("crt.sh: %d subdomains for %s", len(subdomains), domain)
|
||||
return sorted(subdomains)
|
||||
|
||||
|
||||
def enumerate_dns_records(domain):
|
||||
"""Query public DNS records for a domain using DNS-over-HTTPS."""
|
||||
record_types = ["A", "AAAA", "MX", "NS", "TXT", "CNAME", "SOA"]
|
||||
records = {}
|
||||
for rtype in record_types:
|
||||
url = f"https://dns.google/resolve?name={domain}&type={rtype}"
|
||||
resp = requests.get(url, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
answers = data.get("Answer", [])
|
||||
if answers:
|
||||
records[rtype] = [a.get("data", "") for a in answers]
|
||||
logger.info("DNS records collected for %s: %s", domain, list(records.keys()))
|
||||
return records
|
||||
|
||||
|
||||
def shodan_org_search(api_key, org_name, max_results=50):
|
||||
"""Search Shodan for hosts belonging to an organization."""
|
||||
api = shodan.Shodan(api_key)
|
||||
results = api.search(f'org:"{org_name}"', limit=max_results)
|
||||
hosts = []
|
||||
for match in results["matches"]:
|
||||
hosts.append({
|
||||
"ip": match["ip_str"],
|
||||
"port": match["port"],
|
||||
"product": match.get("product", ""),
|
||||
"version": match.get("version", ""),
|
||||
"os": match.get("os", ""),
|
||||
"hostnames": match.get("hostnames", []),
|
||||
})
|
||||
logger.info("Shodan: %d hosts for org '%s'", len(hosts), org_name)
|
||||
return hosts
|
||||
|
||||
|
||||
def check_email_security(domain):
|
||||
"""Check SPF, DKIM, and DMARC records for email security posture."""
|
||||
email_security = {}
|
||||
for prefix, rtype in [("", "TXT"), ("_dmarc.", "TXT")]:
|
||||
url = f"https://dns.google/resolve?name={prefix}{domain}&type={rtype}"
|
||||
resp = requests.get(url, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
answers = resp.json().get("Answer", [])
|
||||
for a in answers:
|
||||
data = a.get("data", "")
|
||||
if "v=spf1" in data:
|
||||
email_security["spf"] = data
|
||||
elif "v=DMARC1" in data:
|
||||
email_security["dmarc"] = data
|
||||
email_security["spf_present"] = "spf" in email_security
|
||||
email_security["dmarc_present"] = "dmarc" in email_security
|
||||
logger.info("Email security for %s: SPF=%s DMARC=%s",
|
||||
domain, email_security["spf_present"], email_security["dmarc_present"])
|
||||
return email_security
|
||||
|
||||
|
||||
def search_breach_data(email_domain):
|
||||
"""Search Have I Been Pwned for breached accounts (requires API key)."""
|
||||
url = f"https://haveibeenpwned.com/api/v3/breachedaccount/{email_domain}"
|
||||
headers = {"hibp-api-key": "PLACEHOLDER", "user-agent": "OSINT-Agent"}
|
||||
try:
|
||||
resp = requests.get(url, headers=headers, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
return resp.json()
|
||||
except requests.RequestException:
|
||||
pass
|
||||
return []
|
||||
|
||||
|
||||
def check_web_technologies(domain):
|
||||
"""Identify web technologies via HTTP response headers."""
|
||||
technologies = {}
|
||||
try:
|
||||
resp = requests.get(f"https://{domain}", timeout=10, allow_redirects=True, verify=False)
|
||||
headers = resp.headers
|
||||
tech_headers = {
|
||||
"Server": headers.get("Server", ""),
|
||||
"X-Powered-By": headers.get("X-Powered-By", ""),
|
||||
"X-AspNet-Version": headers.get("X-AspNet-Version", ""),
|
||||
"X-Generator": headers.get("X-Generator", ""),
|
||||
}
|
||||
technologies = {k: v for k, v in tech_headers.items() if v}
|
||||
technologies["status_code"] = resp.status_code
|
||||
technologies["final_url"] = resp.url
|
||||
except requests.RequestException as e:
|
||||
technologies["error"] = str(e)
|
||||
return technologies
|
||||
|
||||
|
||||
def search_github_leaks(domain, github_token=None):
|
||||
"""Search GitHub for leaked credentials related to the target domain."""
|
||||
headers = {"Accept": "application/vnd.github.v3+json"}
|
||||
if github_token:
|
||||
headers["Authorization"] = f"token {github_token}"
|
||||
queries = [f'"{domain}" password', f'"{domain}" api_key', f'"{domain}" secret']
|
||||
all_results = []
|
||||
for query in queries:
|
||||
resp = requests.get(
|
||||
"https://api.github.com/search/code",
|
||||
headers=headers, params={"q": query, "per_page": 10}, timeout=15
|
||||
)
|
||||
if resp.status_code == 200:
|
||||
items = resp.json().get("items", [])
|
||||
for item in items:
|
||||
all_results.append({
|
||||
"repo": item["repository"]["full_name"],
|
||||
"path": item["path"],
|
||||
"url": item["html_url"],
|
||||
"query": query,
|
||||
})
|
||||
logger.info("GitHub: %d potential leaks for %s", len(all_results), domain)
|
||||
return all_results
|
||||
|
||||
|
||||
def generate_recon_report(domain, subdomains, dns, shodan_hosts, email_sec, technologies, github_leaks):
|
||||
"""Generate external reconnaissance report."""
|
||||
report = {
|
||||
"target": domain,
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"subdomains": {"count": len(subdomains), "list": subdomains},
|
||||
"dns_records": dns,
|
||||
"shodan_hosts": {"count": len(shodan_hosts), "hosts": shodan_hosts},
|
||||
"email_security": email_sec,
|
||||
"web_technologies": technologies,
|
||||
"github_leaks": github_leaks,
|
||||
}
|
||||
print(f"RECON REPORT - {domain}")
|
||||
print(f"Subdomains: {len(subdomains)}, Shodan hosts: {len(shodan_hosts)}, GitHub leaks: {len(github_leaks)}")
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="External Reconnaissance OSINT Agent")
|
||||
parser.add_argument("--domain", required=True, help="Target domain")
|
||||
parser.add_argument("--org", help="Organization name for Shodan search")
|
||||
parser.add_argument("--shodan-key", help="Shodan API key")
|
||||
parser.add_argument("--github-token", help="GitHub token for code search")
|
||||
parser.add_argument("--output", default="recon_report.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
subdomains = enumerate_subdomains_crtsh(args.domain)
|
||||
dns = enumerate_dns_records(args.domain)
|
||||
email_sec = check_email_security(args.domain)
|
||||
technologies = check_web_technologies(args.domain)
|
||||
|
||||
shodan_hosts = []
|
||||
if args.shodan_key and args.org:
|
||||
shodan_hosts = shodan_org_search(args.shodan_key, args.org)
|
||||
|
||||
github_leaks = search_github_leaks(args.domain, args.github_token) if args.github_token else []
|
||||
|
||||
report = generate_recon_report(
|
||||
args.domain, subdomains, dns, shodan_hosts, email_sec, technologies, github_leaks
|
||||
)
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
logger.info("Recon report saved to %s", args.output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user