mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-14 11:51:16 +03:00
27c6414ca5
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
192 lines
7.4 KiB
Python
192 lines
7.4 KiB
Python
#!/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()
|