Files
Anthropic-Cybersecurity-Skills/skills/exploiting-ipv6-vulnerabilities/scripts/agent.py
T
mukul975 27c6414ca5 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
2026-03-10 21:02:12 +01:00

179 lines
6.6 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
import socket
import struct
from datetime import datetime
from typing import List
try:
from scapy.all import (
sniff, sendp, 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, _ = __import__("scapy.all", fromlist=["sr"]).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()