Files
Anthropic-Cybersecurity-Skills/skills/exploiting-ipv6-vulnerabilities/scripts/agent.py
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- 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
2026-03-19 13:26:49 +01:00

177 lines
6.5 KiB
Python

#!/usr/bin/env python3
# For authorized testing in lab/CTF environments only
"""IPv6 vulnerability assessment agent using scapy for NDP/RA analysis."""
import argparse
import json
import logging
import sys
from datetime import datetime
from typing import List
try:
from scapy.all import (
sniff, sendp, sr, get_if_hwaddr, get_if_addr6, conf,
Ether, IPv6, ICMPv6ND_RA, ICMPv6ND_NA, ICMPv6ND_NS,
ICMPv6NDOptSrcLLAddr, ICMPv6NDOptPrefixInfo, ICMPv6NDOptRDNSS,
ICMPv6NDOptDstLLAddr,
)
from scapy.layers.inet6 import ICMPv6EchoRequest
except ImportError:
sys.exit("scapy is required: pip install scapy")
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
def discover_ipv6_hosts(interface: str, timeout: int = 5) -> List[dict]:
"""Discover IPv6 hosts by sending ICMPv6 Echo to all-nodes multicast."""
target = "ff02::1"
pkt = IPv6(dst=target) / ICMPv6EchoRequest()
logger.info("Sending ICMPv6 Echo to all-nodes multicast on %s", interface)
replies = []
try:
ans, _ = sr(
pkt, iface=interface, timeout=timeout, verbose=0, multi=True
)
for sent, recv in ans:
src = recv[IPv6].src
replies.append({"ipv6_address": src, "mac": recv.src if hasattr(recv, "src") else ""})
except Exception as exc:
logger.error("Discovery failed: %s", exc)
logger.info("Discovered %d IPv6 hosts", len(replies))
return replies
def capture_router_advertisements(interface: str, timeout: int = 10) -> List[dict]:
"""Capture and analyze Router Advertisement packets on the network."""
logger.info("Capturing Router Advertisements on %s for %ds", interface, timeout)
ras = []
def process_ra(pkt):
if pkt.haslayer(ICMPv6ND_RA):
ra_info = {
"src_ip": pkt[IPv6].src,
"src_mac": pkt.src if hasattr(pkt, "src") else "",
"router_lifetime": pkt[ICMPv6ND_RA].routerlifetime,
"managed_flag": bool(pkt[ICMPv6ND_RA].M),
"other_flag": bool(pkt[ICMPv6ND_RA].O),
"prefixes": [],
"dns_servers": [],
}
if pkt.haslayer(ICMPv6NDOptPrefixInfo):
layer = pkt[ICMPv6NDOptPrefixInfo]
ra_info["prefixes"].append({
"prefix": layer.prefix,
"prefix_len": layer.prefixlen,
"valid_lifetime": layer.validlifetime,
})
if pkt.haslayer(ICMPv6NDOptRDNSS):
ra_info["dns_servers"] = pkt[ICMPv6NDOptRDNSS].dns
ras.append(ra_info)
logger.info("RA from %s (lifetime=%d)", ra_info["src_ip"], ra_info["router_lifetime"])
sniff(iface=interface, filter="icmp6", prn=process_ra, timeout=timeout, store=0)
return ras
def detect_rogue_ra(ras: List[dict], known_routers: List[str]) -> List[dict]:
"""Identify rogue Router Advertisements from unknown sources."""
rogues = []
for ra in ras:
if ra["src_ip"] not in known_routers:
rogues.append({
"alert": "ROGUE_ROUTER_ADVERTISEMENT",
"src_ip": ra["src_ip"],
"src_mac": ra["src_mac"],
"router_lifetime": ra["router_lifetime"],
"prefixes": ra["prefixes"],
})
logger.warning("ROGUE RA detected from %s", ra["src_ip"])
return rogues
def check_ipv6_firewall() -> dict:
"""Check if ip6tables rules are configured (Linux only)."""
import subprocess
result = {"ip6tables_present": False, "rules_count": 0, "rules": []}
try:
output = subprocess.run(
["ip6tables", "-L", "-n", "--line-numbers"],
capture_output=True, text=True, timeout=5,
)
lines = [l.strip() for l in output.stdout.strip().split("\n") if l.strip()]
result["ip6tables_present"] = True
result["rules_count"] = len([l for l in lines if l and not l.startswith("Chain") and not l.startswith("num")])
result["rules"] = lines[:20]
except (FileNotFoundError, subprocess.TimeoutExpired):
logger.warning("ip6tables not available")
return result
def check_tunnel_protocols(interface: str, timeout: int = 5) -> dict:
"""Check for IPv6 tunneling protocols (Teredo, 6to4, ISATAP)."""
tunnels = {"teredo": False, "six_to_four": False, "isatap": False}
def detect_tunnel(pkt):
if pkt.haslayer("UDP") and (pkt["UDP"].sport == 3544 or pkt["UDP"].dport == 3544):
tunnels["teredo"] = True
if pkt.haslayer("IP") and pkt["IP"].proto == 41:
tunnels["six_to_four"] = True
try:
sniff(iface=interface, prn=detect_tunnel, timeout=timeout, store=0)
except Exception as exc:
logger.warning("Tunnel detection failed: %s", exc)
return tunnels
def generate_assessment(interface: str, known_routers: List[str]) -> dict:
"""Run complete IPv6 security assessment."""
hosts = discover_ipv6_hosts(interface, timeout=5)
ras = capture_router_advertisements(interface, timeout=10)
rogues = detect_rogue_ra(ras, known_routers)
firewall = check_ipv6_firewall()
tunnels = check_tunnel_protocols(interface, timeout=5)
findings = []
if rogues:
findings.append(f"CRITICAL: {len(rogues)} rogue Router Advertisements detected")
if firewall["rules_count"] == 0:
findings.append("HIGH: No ip6tables rules configured")
if tunnels["teredo"]:
findings.append("MEDIUM: Teredo tunnel traffic detected")
return {
"assessment_date": datetime.utcnow().isoformat(),
"interface": interface,
"ipv6_hosts_discovered": len(hosts),
"hosts": hosts,
"router_advertisements": ras,
"rogue_ras": rogues,
"firewall_status": firewall,
"tunnel_protocols": tunnels,
"risk_findings": findings,
}
def main():
parser = argparse.ArgumentParser(description="IPv6 Vulnerability Assessment Agent")
parser.add_argument("--interface", default="eth0", help="Network interface")
parser.add_argument("--known-routers", nargs="*", default=[], help="Known legitimate router IPv6 addresses")
parser.add_argument("--output", default="ipv6_assessment.json")
args = parser.parse_args()
report = generate_assessment(args.interface, args.known_routers)
with open(args.output, "w") as f:
json.dump(report, f, indent=2, default=str)
logger.info("Report saved to %s", args.output)
print(json.dumps(report, indent=2, default=str))
if __name__ == "__main__":
main()