mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-31 16:27:43 +03:00
Complete folder anatomy for all 649 cybersecurity skills + update LICENSE to Mahipal
- Add scripts/agent.py and references/api-reference.md to all remaining skills - Update all 648 LICENSE files: copyright now reads 'Mahipal' - Add implementing-security-monitoring-with-datadog (new skill with full anatomy) - All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Anthropic Agent Skills Contributors
|
||||
Copyright (c) 2025 Mahipal
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
# S7comm Protocol Security Analysis - API Reference
|
||||
|
||||
## pyshark Library
|
||||
|
||||
Python wrapper for TShark (Wireshark CLI) for packet analysis.
|
||||
|
||||
### Loading S7comm Traffic
|
||||
```python
|
||||
import pyshark
|
||||
cap = pyshark.FileCapture("traffic.pcap", display_filter="s7comm")
|
||||
for pkt in cap:
|
||||
s7_layer = pkt.s7comm
|
||||
print(s7_layer.rosctr, s7_layer.param_func)
|
||||
cap.close()
|
||||
```
|
||||
|
||||
### Key S7comm Layer Fields
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `s7comm.rosctr` | PDU type: 1=Job, 2=Ack, 3=Ack-Data, 7=Userdata |
|
||||
| `s7comm.param_func` | Function code (hex) |
|
||||
| `s7comm.error_class` | Error class (0 = no error) |
|
||||
| `s7comm.error_code` | Specific error code |
|
||||
| `s7comm.param_data` | Parameter data payload |
|
||||
|
||||
## S7comm Function Codes
|
||||
|
||||
| Code | Name | Risk Level |
|
||||
|------|------|------------|
|
||||
| 0x04 | Read Var | Low - read process data |
|
||||
| 0x05 | Write Var | High - modify PLC memory |
|
||||
| 0x28 | Setup Communication | Low - session init |
|
||||
| 0x29 | PLC Run | Critical - start PLC execution |
|
||||
| 0x1a | PLC Stop | Critical - halt PLC execution |
|
||||
| 0xf0 | Userdata | Medium - diagnostics/programming |
|
||||
|
||||
## S7comm Protocol Overview
|
||||
|
||||
S7comm runs over ISO-on-TCP (RFC 1006) on port 102. The protocol stack:
|
||||
1. TCP connection on port 102
|
||||
2. TPKT header (RFC 1006)
|
||||
3. COTP connection-oriented transport (ISO 8073)
|
||||
4. S7comm PDU
|
||||
|
||||
### Security Concerns
|
||||
- No built-in authentication in S7comm (pre-S7comm-Plus)
|
||||
- No encryption of traffic
|
||||
- Write operations can modify PLC logic and process values
|
||||
- Stop/Run commands can halt industrial processes
|
||||
|
||||
## Detection Patterns
|
||||
|
||||
### Unauthorized Access
|
||||
Multiple unique source IPs connecting to a single PLC (> 3 sources) indicates potential unauthorized access.
|
||||
|
||||
### Brute Force
|
||||
Repeated error responses (error_class != 0) from a PLC to a single source exceeding threshold count.
|
||||
|
||||
### Dangerous Operations
|
||||
Any write_var, run, or stop function codes should be flagged and correlated with authorized change windows.
|
||||
|
||||
## Output Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"report": "s7comm_protocol_security_analysis",
|
||||
"total_s7_packets": 1500,
|
||||
"total_findings": 8,
|
||||
"severity_summary": {"critical": 2, "high": 5, "medium": 1},
|
||||
"traffic_patterns": {"function_distribution": {"read_var": 1200, "write_var": 50}},
|
||||
"findings": [{"type": "dangerous_operation_stop", "severity": "critical"}]
|
||||
}
|
||||
```
|
||||
|
||||
## CLI Usage
|
||||
|
||||
```bash
|
||||
python agent.py --pcap capture.pcap --brute-threshold 10 --output report.json
|
||||
```
|
||||
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env python3
|
||||
"""S7comm Protocol Security Analysis agent — analyzes Siemens S7 protocol
|
||||
traffic from PCAP files using pyshark to detect unauthorized PLC access,
|
||||
password brute-force, and dangerous write operations."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from collections import Counter, defaultdict
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import pyshark
|
||||
except ImportError:
|
||||
print("Install pyshark: pip install pyshark", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
S7_PORT = 102
|
||||
S7_FUNCTION_CODES = {
|
||||
"0x04": "read_var",
|
||||
"0x05": "write_var",
|
||||
"0x00": "cpu_services",
|
||||
"0x28": "setup_communication",
|
||||
"0x29": "run",
|
||||
"0x1a": "stop",
|
||||
"0xf0": "userdata",
|
||||
}
|
||||
|
||||
DANGEROUS_FUNCTIONS = {"write_var", "run", "stop"}
|
||||
|
||||
|
||||
def load_pcap(pcap_path: str, display_filter: str = "s7comm") -> list:
|
||||
"""Load S7comm packets from PCAP file."""
|
||||
cap = pyshark.FileCapture(pcap_path, display_filter=display_filter)
|
||||
packets = []
|
||||
for pkt in cap:
|
||||
try:
|
||||
s7_layer = pkt.s7comm
|
||||
packets.append({
|
||||
"timestamp": str(pkt.sniff_time),
|
||||
"src_ip": str(pkt.ip.src),
|
||||
"dst_ip": str(pkt.ip.dst),
|
||||
"src_port": str(pkt.tcp.srcport),
|
||||
"dst_port": str(pkt.tcp.dstport),
|
||||
"rosctr": getattr(s7_layer, "rosctr", "unknown"),
|
||||
"function": getattr(s7_layer, "param_func", "unknown"),
|
||||
"error_class": getattr(s7_layer, "error_class", "0"),
|
||||
"error_code": getattr(s7_layer, "error_code", "0"),
|
||||
})
|
||||
except AttributeError:
|
||||
continue
|
||||
cap.close()
|
||||
return packets
|
||||
|
||||
|
||||
def detect_unauthorized_access(packets: list) -> list[dict]:
|
||||
"""Detect potential unauthorized access to PLCs."""
|
||||
findings = []
|
||||
plc_connections = defaultdict(set)
|
||||
for pkt in packets:
|
||||
plc_connections[pkt["dst_ip"]].add(pkt["src_ip"])
|
||||
|
||||
for plc_ip, sources in plc_connections.items():
|
||||
if len(sources) > 3:
|
||||
findings.append({
|
||||
"type": "multiple_sources_to_plc",
|
||||
"severity": "high",
|
||||
"plc_ip": plc_ip,
|
||||
"source_count": len(sources),
|
||||
"sources": sorted(sources),
|
||||
"detail": f"PLC {plc_ip} accessed by {len(sources)} unique sources",
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def detect_dangerous_operations(packets: list) -> list[dict]:
|
||||
"""Flag write, run, and stop operations on PLCs."""
|
||||
findings = []
|
||||
for pkt in packets:
|
||||
func_code = pkt.get("function", "unknown")
|
||||
func_name = S7_FUNCTION_CODES.get(func_code, func_code)
|
||||
if func_name in DANGEROUS_FUNCTIONS:
|
||||
findings.append({
|
||||
"type": f"dangerous_operation_{func_name}",
|
||||
"severity": "critical" if func_name in ("stop", "run") else "high",
|
||||
"src_ip": pkt["src_ip"],
|
||||
"dst_ip": pkt["dst_ip"],
|
||||
"timestamp": pkt["timestamp"],
|
||||
"function": func_name,
|
||||
"detail": f"{func_name} operation from {pkt['src_ip']} to PLC {pkt['dst_ip']}",
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def detect_brute_force(packets: list, threshold: int = 10) -> list[dict]:
|
||||
"""Detect authentication brute-force via repeated setup_communication with errors."""
|
||||
findings = []
|
||||
error_counts = Counter()
|
||||
for pkt in packets:
|
||||
if pkt.get("error_class", "0") != "0" or pkt.get("error_code", "0") != "0":
|
||||
key = (pkt["src_ip"], pkt["dst_ip"])
|
||||
error_counts[key] += 1
|
||||
|
||||
for (src, dst), count in error_counts.items():
|
||||
if count >= threshold:
|
||||
findings.append({
|
||||
"type": "potential_brute_force",
|
||||
"severity": "critical",
|
||||
"src_ip": src,
|
||||
"dst_ip": dst,
|
||||
"error_count": count,
|
||||
"detail": f"{count} error responses from PLC {dst} to {src} — possible brute-force",
|
||||
})
|
||||
return findings
|
||||
|
||||
|
||||
def analyze_traffic_patterns(packets: list) -> dict:
|
||||
"""Compute traffic statistics."""
|
||||
func_counts = Counter()
|
||||
src_counts = Counter()
|
||||
dst_counts = Counter()
|
||||
for pkt in packets:
|
||||
func_code = pkt.get("function", "unknown")
|
||||
func_name = S7_FUNCTION_CODES.get(func_code, func_code)
|
||||
func_counts[func_name] += 1
|
||||
src_counts[pkt["src_ip"]] += 1
|
||||
dst_counts[pkt["dst_ip"]] += 1
|
||||
|
||||
return {
|
||||
"total_packets": len(packets),
|
||||
"function_distribution": dict(func_counts.most_common(20)),
|
||||
"top_sources": dict(src_counts.most_common(10)),
|
||||
"top_destinations": dict(dst_counts.most_common(10)),
|
||||
}
|
||||
|
||||
|
||||
def generate_report(pcap_path: str, brute_threshold: int) -> dict:
|
||||
"""Run all analyses and produce consolidated JSON report."""
|
||||
packets = load_pcap(pcap_path)
|
||||
findings = []
|
||||
findings.extend(detect_unauthorized_access(packets))
|
||||
findings.extend(detect_dangerous_operations(packets))
|
||||
findings.extend(detect_brute_force(packets, brute_threshold))
|
||||
traffic = analyze_traffic_patterns(packets)
|
||||
|
||||
severity_counts = Counter(f["severity"] for f in findings)
|
||||
return {
|
||||
"report": "s7comm_protocol_security_analysis",
|
||||
"generated_at": datetime.utcnow().isoformat() + "Z",
|
||||
"pcap_file": pcap_path,
|
||||
"total_s7_packets": len(packets),
|
||||
"total_findings": len(findings),
|
||||
"severity_summary": dict(severity_counts),
|
||||
"traffic_patterns": traffic,
|
||||
"findings": findings,
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="S7comm Protocol Security Analysis Agent")
|
||||
parser.add_argument("--pcap", required=True, help="Path to PCAP file with S7comm traffic")
|
||||
parser.add_argument("--brute-threshold", type=int, default=10, help="Error count threshold for brute-force (default: 10)")
|
||||
parser.add_argument("--output", help="Output JSON file path")
|
||||
args = parser.parse_args()
|
||||
|
||||
report = generate_report(args.pcap, args.brute_threshold)
|
||||
output = json.dumps(report, indent=2)
|
||||
if args.output:
|
||||
Path(args.output).write_text(output, encoding="utf-8")
|
||||
print(f"Report written to {args.output}")
|
||||
else:
|
||||
print(output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user