mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-26 03:34:37 +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,178 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user