mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-28 04:09:40 +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,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Anthropic Agent Skills Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
name: analyzing-tls-certificate-transparency-logs
|
||||
description: >
|
||||
Queries Certificate Transparency logs via crt.sh and pycrtsh to detect phishing
|
||||
domains, unauthorized certificate issuance, and shadow IT. Monitors newly issued
|
||||
certificates for typosquatting and brand impersonation using Levenshtein distance.
|
||||
Use for proactive phishing domain detection and certificate monitoring.
|
||||
---
|
||||
|
||||
# Analyzing TLS Certificate Transparency Logs
|
||||
|
||||
## Instructions
|
||||
|
||||
Query crt.sh Certificate Transparency database to find certificates issued for
|
||||
domains similar to your organization's brand, detecting phishing infrastructure.
|
||||
|
||||
```python
|
||||
from pycrtsh import Crtsh
|
||||
|
||||
c = Crtsh()
|
||||
# Search for certificates matching a domain
|
||||
certs = c.search("example.com")
|
||||
for cert in certs:
|
||||
print(cert["id"], cert["name_value"])
|
||||
|
||||
# Get full certificate details
|
||||
details = c.get(certs[0]["id"], type="id")
|
||||
```
|
||||
|
||||
Key analysis steps:
|
||||
1. Query crt.sh for all certificates matching your domain pattern
|
||||
2. Identify certificates with typosquatting variations (Levenshtein distance)
|
||||
3. Flag certificates from unexpected CAs
|
||||
4. Monitor for wildcard certificates on suspicious subdomains
|
||||
5. Cross-reference with known phishing infrastructure
|
||||
|
||||
## Examples
|
||||
|
||||
```python
|
||||
from pycrtsh import Crtsh
|
||||
c = Crtsh()
|
||||
certs = c.search("%.example.com")
|
||||
for cert in certs:
|
||||
print(f"Issuer: {cert.get('issuer_name')}, Domain: {cert.get('name_value')}")
|
||||
```
|
||||
@@ -0,0 +1,59 @@
|
||||
# API Reference: Analyzing TLS Certificate Transparency Logs
|
||||
|
||||
## pycrtsh
|
||||
|
||||
```python
|
||||
from pycrtsh import Crtsh
|
||||
c = Crtsh()
|
||||
|
||||
# Search certificates by domain
|
||||
certs = c.search("example.com") # exact match
|
||||
certs = c.search("%.example.com") # wildcard subdomains
|
||||
|
||||
# Get certificate details by ID
|
||||
details = c.get(cert_id, type="id")
|
||||
details = c.get(sha1_hash, type="sha1")
|
||||
details = c.get(sha256_hash, type="sha256")
|
||||
```
|
||||
|
||||
## crt.sh REST API (Direct)
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
# JSON output
|
||||
resp = requests.get("https://crt.sh/?q=%.example.com&output=json")
|
||||
records = resp.json()
|
||||
# Fields: id, issuer_ca_id, issuer_name, common_name,
|
||||
# name_value, not_before, not_after, serial_number
|
||||
```
|
||||
|
||||
## certstream (Real-Time CT Monitoring)
|
||||
|
||||
```python
|
||||
import certstream
|
||||
|
||||
def callback(message, context):
|
||||
if message["message_type"] == "certificate_update":
|
||||
all_domains = message["data"]["leaf_cert"]["all_domains"]
|
||||
print(all_domains)
|
||||
|
||||
certstream.listen_for_events(callback, url="wss://certstream.calidog.io/")
|
||||
```
|
||||
|
||||
## Key Certificate Fields
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `common_name` | Primary domain on certificate |
|
||||
| `name_value` | SAN (Subject Alternative Names) |
|
||||
| `issuer_name` | Certificate Authority |
|
||||
| `not_before` | Issuance date |
|
||||
| `not_after` | Expiration date |
|
||||
|
||||
### References
|
||||
|
||||
- pycrtsh: https://pypi.org/project/pycrtsh/
|
||||
- crt.sh: https://crt.sh/
|
||||
- certstream: https://certstream.calidog.io/
|
||||
- CT RFC 6962: https://datatracker.ietf.org/doc/html/rfc6962
|
||||
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for analyzing Certificate Transparency logs for phishing detection."""
|
||||
|
||||
import os
|
||||
import json
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
|
||||
import requests
|
||||
from pycrtsh import Crtsh
|
||||
|
||||
|
||||
def search_certificates(domain, include_expired=False):
|
||||
"""Search crt.sh for certificates matching a domain."""
|
||||
c = Crtsh()
|
||||
certs = c.search(domain)
|
||||
if not include_expired:
|
||||
now = datetime.utcnow()
|
||||
certs = [cert for cert in certs if cert.get("not_after")
|
||||
and datetime.strptime(str(cert["not_after"]), "%Y-%m-%dT%H:%M:%S") > now]
|
||||
return certs
|
||||
|
||||
|
||||
def get_certificate_details(cert_id):
|
||||
"""Get full certificate details from crt.sh by ID."""
|
||||
c = Crtsh()
|
||||
return c.get(cert_id, type="id")
|
||||
|
||||
|
||||
def search_crtsh_api(domain):
|
||||
"""Query crt.sh REST API directly for certificate records."""
|
||||
url = f"https://crt.sh/?q={domain}&output=json"
|
||||
resp = requests.get(url, timeout=30)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
|
||||
def levenshtein_distance(s1, s2):
|
||||
"""Compute Levenshtein distance between two strings."""
|
||||
if len(s1) < len(s2):
|
||||
return levenshtein_distance(s2, s1)
|
||||
if len(s2) == 0:
|
||||
return len(s1)
|
||||
prev_row = range(len(s2) + 1)
|
||||
for i, c1 in enumerate(s1):
|
||||
curr_row = [i + 1]
|
||||
for j, c2 in enumerate(s2):
|
||||
insertions = prev_row[j + 1] + 1
|
||||
deletions = curr_row[j] + 1
|
||||
substitutions = prev_row[j] + (c1 != c2)
|
||||
curr_row.append(min(insertions, deletions, substitutions))
|
||||
prev_row = curr_row
|
||||
return prev_row[-1]
|
||||
|
||||
|
||||
def detect_typosquatting(target_domain, ct_records, max_distance=3):
|
||||
"""Detect typosquatting domains using Levenshtein distance."""
|
||||
base = target_domain.split(".")[0]
|
||||
suspicious = []
|
||||
seen = set()
|
||||
for record in ct_records:
|
||||
domain = record.get("common_name", "") or record.get("name_value", "")
|
||||
if not domain or domain in seen:
|
||||
continue
|
||||
seen.add(domain)
|
||||
candidate_base = domain.split(".")[0].lstrip("*").lstrip(".")
|
||||
if candidate_base == base:
|
||||
continue
|
||||
dist = levenshtein_distance(base, candidate_base)
|
||||
if 0 < dist <= max_distance:
|
||||
suspicious.append({
|
||||
"domain": domain,
|
||||
"distance": dist,
|
||||
"issuer": record.get("issuer_name", ""),
|
||||
"not_before": record.get("not_before", ""),
|
||||
"not_after": record.get("not_after", ""),
|
||||
})
|
||||
return sorted(suspicious, key=lambda x: x["distance"])
|
||||
|
||||
|
||||
def detect_unauthorized_cas(ct_records, allowed_cas):
|
||||
"""Find certificates issued by unauthorized Certificate Authorities."""
|
||||
unauthorized = []
|
||||
for record in ct_records:
|
||||
issuer = record.get("issuer_name", "")
|
||||
if issuer and not any(ca.lower() in issuer.lower() for ca in allowed_cas):
|
||||
unauthorized.append({
|
||||
"domain": record.get("common_name", ""),
|
||||
"issuer": issuer,
|
||||
"not_before": record.get("not_before", ""),
|
||||
"cert_id": record.get("id"),
|
||||
})
|
||||
return unauthorized
|
||||
|
||||
|
||||
def monitor_new_certificates(domain, hours_back=24):
|
||||
"""Find certificates issued in the last N hours."""
|
||||
records = search_crtsh_api(f"%.{domain}")
|
||||
cutoff = datetime.utcnow().timestamp() - (hours_back * 3600)
|
||||
recent = []
|
||||
for r in records:
|
||||
not_before = r.get("not_before", "")
|
||||
if not_before:
|
||||
try:
|
||||
cert_time = datetime.strptime(not_before, "%Y-%m-%dT%H:%M:%S")
|
||||
if cert_time.timestamp() > cutoff:
|
||||
recent.append({
|
||||
"domain": r.get("common_name", ""),
|
||||
"issuer": r.get("issuer_name", ""),
|
||||
"not_before": not_before,
|
||||
"name_value": r.get("name_value", ""),
|
||||
})
|
||||
except ValueError:
|
||||
continue
|
||||
return recent
|
||||
|
||||
|
||||
def find_wildcard_certificates(ct_records):
|
||||
"""Identify wildcard certificates that could cover many subdomains."""
|
||||
wildcards = []
|
||||
for r in ct_records:
|
||||
name = r.get("common_name", "") or r.get("name_value", "")
|
||||
if name.startswith("*."):
|
||||
wildcards.append({
|
||||
"domain": name,
|
||||
"issuer": r.get("issuer_name", ""),
|
||||
"not_before": r.get("not_before", ""),
|
||||
"not_after": r.get("not_after", ""),
|
||||
})
|
||||
return wildcards
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Certificate Transparency Analysis Agent")
|
||||
parser.add_argument("--domain", required=True, help="Target domain to monitor")
|
||||
parser.add_argument("--allowed-cas", nargs="*", default=["Let's Encrypt", "DigiCert",
|
||||
"Sectigo", "Amazon", "Google Trust Services"])
|
||||
parser.add_argument("--output", default="ct_report.json")
|
||||
parser.add_argument("--action", choices=[
|
||||
"search", "typosquat", "unauthorized_ca", "monitor", "full_scan"
|
||||
], default="full_scan")
|
||||
args = parser.parse_args()
|
||||
|
||||
report = {"domain": args.domain, "generated_at": datetime.utcnow().isoformat(),
|
||||
"findings": {}}
|
||||
|
||||
ct_records = search_crtsh_api(f"%.{args.domain}")
|
||||
report["findings"]["total_certificates"] = len(ct_records)
|
||||
print(f"[+] Found {len(ct_records)} certificates for {args.domain}")
|
||||
|
||||
if args.action in ("typosquat", "full_scan"):
|
||||
typos = detect_typosquatting(args.domain, ct_records)
|
||||
report["findings"]["typosquatting"] = typos
|
||||
print(f"[+] Typosquatting domains: {len(typos)}")
|
||||
|
||||
if args.action in ("unauthorized_ca", "full_scan"):
|
||||
unauth = detect_unauthorized_cas(ct_records, args.allowed_cas)
|
||||
report["findings"]["unauthorized_cas"] = unauth[:50]
|
||||
print(f"[+] Unauthorized CA certs: {len(unauth)}")
|
||||
|
||||
if args.action in ("monitor", "full_scan"):
|
||||
recent = monitor_new_certificates(args.domain)
|
||||
report["findings"]["recent_24h"] = recent
|
||||
print(f"[+] Certificates issued in last 24h: {len(recent)}")
|
||||
|
||||
wildcards = find_wildcard_certificates(ct_records)
|
||||
report["findings"]["wildcard_certs"] = wildcards
|
||||
print(f"[+] Wildcard certificates: {len(wildcards)}")
|
||||
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2, default=str)
|
||||
print(f"[+] Report saved to {args.output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user