Detect anomalies in DNP3 (Distributed Network Protocol 3) communications used in SCADA systems by monitoring for unauthorized control commands, firmware update attempts, protocol violations, and deviations from baseline traffic patterns using deep packet inspection and machine learning approaches.
Works with
AI-first code editor with Composer
Before installing skills in Cursor, ensure your development environment meets these requirements:
node --versiondetecting-dnp3-protocol-anomaliesExecute the skills CLI command in your project's root directory to begin installation:
Fetches detecting-dnp3-protocol-anomalies from mukul975/Anthropic-Cybersecurity-Skills and configures it for Cursor.
The CLI shows a list of agents. Use arrow keys and space to select Cursor:
Confirm successful installation by checking the skill directory location:
Restart Cursor to activate detecting-dnp3-protocol-anomalies. Access via /detecting-dnp3-protocol-anomalies in your agent's command palette.
We perform automated surface-level scans (Gen AI Scanner, Socket, Snyk) during installation. These checks detect common vulnerabilities but do not guarantee complete security. Always review skill source code and verify the publisher's reputation before production use.
Skills execute code in your environment. Always review source, verify the publisher, and test in isolation before production.
Submit your Claude Code skill and start earning
Automate repetitive workflows and reduce manual effort
Example
Generate reports, summarize documents, draft communications
Save 3-5 hours per week on routine tasks
Learn new skills, understand complex topics, get expert guidance
Example
Explain concepts, provide examples, suggest learning resources
Accelerate learning and skill development by 2x
Enhance output quality through reviews, suggestions, and refinements
Example
Review drafts, suggest improvements, catch errors
Improve work quality by 30-40% with less effort
0
total installs
0
this week
8.6K
GitHub stars
0
upvotes
Run in your terminal
0
installs
0
this week
8.6K
stars
| name | detecting-dnp3-protocol-anomalies |
| description | 'Detect anomalies in DNP3 (Distributed Network Protocol 3) communications used in SCADA systems by monitoring for unauthorized control commands, firmware update attempts, protocol violations, and deviations from baseline traffic patterns using deep packet inspection and machine learning approaches. ' |
| domain | cybersecurity |
| subdomain | ot-ics-security |
| tags | - ot-security - ics - dnp3 - scada - anomaly-detection - protocol-analysis - energy-sector - ids |
| version | '1.0' |
| author | mahipal |
| license | Apache-2.0 |
| atlas_techniques | - AML.T0043 - AML.T0018 |
| nist_ai_rmf | - MEASURE-2.7 - MEASURE-2.5 - MAP-5.1 |
| nist_csf | - PR.IR-01 - DE.CM-01 - ID.AM-05 - GV.OC-02 |
Do not use for non-DNP3 protocol monitoring (see detecting-modbus-command-injection-attacks for Modbus), for DNP3 Secure Authentication configuration (separate implementation), or for protocol-agnostic network anomaly detection.
#!/usr/bin/env python3
"""DNP3 Protocol Anomaly Detector.
Monitors DNP3 communications for unauthorized control commands,
protocol violations, and deviations from established baselines.
Supports both TCP and serial DNP3 deployments.
"""
import struct
import sys
import json
from collections import defaultdict
from datetime import datetime
from typing import Dict, List, Optional, Set
try:
from scapy.all import rdpcap, IP, TCP
except ImportError:
print("Install scapy: pip install scapy")
sys.exit(1)
# DNP3 Function Codes
DNP3_FUNCTIONS = {
0x00: "Confirm", 0x01: "Read", 0x02: "Write",
0x03: "Select", 0x04: "Operate", 0x05: "Direct Operate",
0x06: "Direct Operate No Ack", 0x07: "Immediate Freeze",
0x08: "Immediate Freeze No Ack", 0x09: "Freeze and Clear",
0x0A: "Freeze and Clear No Ack", 0x0B: "Freeze at Time",
0x0C: "Freeze at Time No Ack", 0x0D: "Cold Restart",
0x0E: "Warm Restart", 0x0F: "Initialize Data",
0x10: "Initialize Application", 0x11: "Start Application",
0x12: "Stop Application", 0x13: "Save Configuration",
0x14: "Enable Unsolicited", 0x15: "Disable Unsolicited",
0x16: "Assign Class", 0x17: "Delay Measurement",
0x18: "Record Current Time", 0x19: "Open File",
0x1A: "Close File", 0x1B: "Delete File",
0x1C: "Get File Info", 0x1D: "Authenticate File",
0x1E: "Abort File", 0x81: "Response", 0x82: "Unsolicited Response",
}
# High-risk function codes that should trigger alerts
DNP3_CRITICAL_FUNCTIONS = {
0x02, # Write
0x03, 0x04, 0x05, 0x06, # Select/Operate/Direct Operate
0x0D, # Cold Restart
0x0E, # Warm Restart
0x0F, # Initialize Data
0x10, # Initialize Application
0x12, # Stop Application
0x19, 0x1A, 0x1B, # File operations (firmware update)
}
class DNP3AnomalyDetector:
"""Detects anomalies in DNP3 protocol communications."""
def __init__(self, baseline_file: Optional[str] = None):
self.alerts = []
self.sessions = defaultdict(lambda: {
"packet_count": 0,
"function_codes": defaultdict(int),
"control_commands": 0,
"file_operations": 0,
"restarts": 0,
})
self.packet_count = 0
self.dnp3_count = 0
self.authorized_masters: Set[str] = set()
self.authorized_pairs: Dict[str, Set[str]] = defaultdict(set)
self.baseline_functions: Dict[str, Set[int]] = defaultdict(set)
if baseline_file:
self.load_baseline(baseline_file)
def load_baseline(self, filepath: str):
"""Load DNP3 communication baseline."""
with open(filepath, "r") as f:
baseline = json.load(f)
for entry in baseline.get("authorized_communications", []):
master = entry["master_ip"]
outstation = entry["outstation_ip"]
self.authorized_masters.add(master)
self.authorized_pairs[master].add(outstation)
self.baseline_functions[f"{master}->{outstation}"] = set(
entry.get("expected_function_codes", [0x00, 0x01])
)
def parse_dnp3_header(self, payload: bytes) -> Optional[dict]:
"""Parse DNP3 data link layer and transport/application headers."""
if len(payload) < 10:
return None
# DNP3 Data Link Layer: start(2) + length(1) + control(1) + dest(2) + source(2) + crc(2)
start_bytes = struct.unpack(">H", payload[0:2])[0]
if start_bytes != 0x0564:
return None
length = payload[2]
control = payload[3]
dest_addr = struct.unpack("<H", payload[4:6])[0]
source_addr = struct.unpack("<H", payload[6:8])[0]
direction = "Master->Outstation" if (control & 0x80) else "Outstation->Master"
result = {
"length": length,
"control": control,
"direction": direction,
"dest_addr": dest_addr,
"source_addr": source_addr,
"is_master": bool(control & 0x80),
}
# Parse transport and application layer (after CRC bytes)
if len(payload) >= 12:
transport_header = payload[10]
if len(payload) >= 13:
app_control = payload[11]
func_code = payload[12]
result["function_code"] = func_code
result["function_name"] = DNP3_FUNCTIONS.get(
func_code, f"Unknown (0x{func_code:02x})"
)
return result
def analyze_packet(self, pkt):
"""Analyze a packet for DNP3 anomalies."""
self.packet_count += 1
if not pkt.haslayer(IP) or not pkt.haslayer(TCP):
return
tcp = pkt[TCP]
if tcp.dport != 20000 and tcp.sport != 20000:
return
payload = bytes(tcp.payload)
if not payload:
return
dnp3 = self.parse_dnp3_header(payload)
if not dnp3:
return
self.dnp3_count += 1
src_ip = pkt[IP].src
dst_ip = pkt[IP].dst
session_key = f"{src_ip}->{dst_ip}"
session = self.sessions[session_key]
session["packet_count"] += 1
func_code = dnp3.get("function_code")
if func_code is not None:
session["function_codes"][func_code] += 1
# Detection 1: Unauthorized DNP3 master
if dnp3.get("is_master") and self.authorized_masters:
if src_ip not in self.authorized_masters:
self.alerts.append({
"severity": "CRITICAL",
"type": "UNAUTHORIZED_DNP3_MASTER",
"src": src_ip, "dst": dst_ip,
"function": dnp3.get("function_name"),
"description": f"Unauthorized DNP3 master {src_ip} communicating with outstation {dst_ip}",
"mitre": "T0869 - Standard Application Layer Protocol",
})
# Detection 2: Cold/Warm restart command
if func_code in (0x0D, 0x0E):
session["restarts"] += 1
restart_type = "Cold" if func_code == 0x0D else "Warm"
self.alerts.append({
"severity": "CRITICAL",
"type": "DNP3_RESTART_COMMAND",
"src": src_ip, "dst": dst_ip,
"function": f"{restart_type} Restart",
"description": f"{restart_type} restart command sent to outstation {dst_ip} (addr {dnp3['dest_addr']})",
"mitre": "T0816 - Device Restart/Shutdown",
})
# Detection 3: File operations (potential firmware update)
if func_code in (0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E):
session["file_operations"] += 1
self.alerts.append({
"severity": "CRITICAL",
"type": "DNP3_FILE_OPERATION",
"src": src_ip, "dst": dst_ip,
"function": dnp3.get("function_name"),
"description": f"File operation on outstation {dst_ip} - potential firmware update or PIPEDREAM indicator",
"mitre": "T0839 - Module Firmware",
})
# Detection 4: Control commands (Select/Operate)
if func_code in (0x03, 0x04, 0x05, 0x06):
session["control_commands"] += 1
if session_key in self.baseline_functions:
if func_code not in self.baseline_functions[session_key]:
self.alerts.append({
"severity": "HIGH",
"type": "UNEXPECTED_CONTROL_COMMAND",
"src": src_ip, "dst": dst_ip,
"function": dnp3.get("function_name"),
"description": f"Control command {dnp3.get('function_name')} not in baseline for {session_key}",
"mitre": "T0855 - Unauthorized Command Message",
})
# Detection 5: Anomalous function code for this pair
if session_key in self.baseline_functions:
if func_code not in self.baseline_functions[session_key]:
if func_code not in (0x00, 0x81, 0x82): # Exclude common response codes
self.alerts.append({
"severity": "MEDIUM",
"type": "ANOMALOUS_FUNCTION_CODE",
"src": src_ip, "dst": dst_ip,
"function": dnp3.get("function_name"),
"description": f"Function code 0x{func_code:02x} not in baseline",
"mitre": "T0855 - Unauthorized Command Message",
})
def generate_report(self):
"""Generate DNP3 anomaly detection report."""
print(f"\n{'='*70}")
print("DNP3 PROTOCOL ANOMALY DETECTION REPORT")
print(f"{'='*70}")
print(f"Analysis Time: {datetime.now().isoformat()}")
print(f"Total Packets: {self.packet_count}")
print(f"DNP3 Packets: {self.dnp3_count}")
print(f"Alerts: {len(self.alerts)}")
print(f"\n--- DNP3 SESSION SUMMARY ---")
for key, session in self.sessions.items():
print(f"\n {key}")
print(f" Packets: {session['packet_count']}")
funcs = [DNP3_FUNCTIONS.get(f, f"0x{f:02x}") for f in session["function_codes"]]
print(f" Functions: {', '.join(funcs)}")
print(f" Control Commands: {session['control_commands']}")
print(f" File Operations: {session['file_operations']}")
print(f" Restart Commands: {session['restarts']}")
if self.alerts:
print(f"\n--- ALERTS ---")
for alert in self.alerts:
print(f"\n [{alert['severity']}] {alert['type']}")
print(f" {alert['src']} -> {alert['dst']}")
print(f" Function: {alert['function']}")
print(f" Detail: {alert['description']}")
print(f" MITRE ICS: {alert.get('mitre', 'N/A')}")
if __name__ == "__main__":
detector = DNP3AnomalyDetector(
baseline_file=sys.argv[2] if len(sys.argv) > 2 else None
)
if len(sys.argv) >= 2:
print(f"[*] Analyzing: {sys.argv[1]}")
packets = rdpcap(sys.argv[1])
for pkt in packets:
detector.analyze_packet(pkt)
detector.generate_report()
else:
print("Usage: python dnp3_detector.py <capture.pcap> [baseline.json]")
| Term | Definition |
|---|---|
| DNP3 | Distributed Network Protocol version 3, the predominant SCADA protocol in the energy sector for communication between masters and outstations |
| Outstation | DNP3 slave device (typically an RTU or IED) that responds to master station polls and commands |
| Select-Before-Operate | DNP3 safety mechanism requiring a Select command before an Operate, preventing accidental control actions |
| Cold Restart (FC 0x0D) | DNP3 command that fully restarts an outstation, resetting all configuration -- a high-risk denial-of-service operation |
| DNP3 Secure Authentication | Optional DNP3 extension (SA v5) adding HMAC-based authentication to prevent command spoofing |
| PIPEDREAM | ICS attack framework with DNP3 capabilities for manipulating outstations and performing firmware updates |
DNP3 ANOMALY DETECTION REPORT
================================
Analysis Period: [start] to [end]
Monitoring Point: [substation/segment]
TRAFFIC SUMMARY:
DNP3 Packets: [count]
Unique Master-Outstation Pairs: [count]
Control Commands: [count]
File Operations: [count]
ALERTS:
[CRITICAL] Unauthorized DNP3 master [IP]
[CRITICAL] Cold restart command to outstation [addr]
[HIGH] Unexpected control command from [IP]
RECOMMENDATIONS:
1. Deploy DNP3 Secure Authentication (SA v5)
2. Block unauthorized sources at firewall
3. Enable DNP3 DPI on industrial firewall
Prerequisites
Time Estimate
15-45 minutes depending on use case complexity
Steps
Common Pitfalls
✓ Do
✗ Don't
💡 Pro Tips
✓ Use when
Use when skill capabilities match your task, clear ROI on time saved, and you can validate outputs. Best for repetitive tasks, learning, and quality improvement.
✗ Avoid when
Avoid when task requires deep expertise you can't validate, involves sensitive decisions, or when learning process is more valuable than speed of completion.
mukul975/Anthropic-Cybersecurity-Skills
mukul975/Anthropic-Cybersecurity-Skills
mukul975/Anthropic-Cybersecurity-Skills
mukul975/Anthropic-Cybersecurity-Skills
mukul975/Anthropic-Cybersecurity-Skills
mukul975/Anthropic-Cybersecurity-Skills
Useful defaults in detecting-dnp3-protocol-anomalies — fewer surprises than typical one-off scripts, and it plays nicely with `npx skills` flows.
Keeps context tight: detecting-dnp3-protocol-anomalies is the kind of skill you can hand to a new teammate without a long onboarding doc.
detecting-dnp3-protocol-anomalies has been reliable in day-to-day use. Documentation quality is above average for community skills.
detecting-dnp3-protocol-anomalies reduced setup friction for our internal harness; good balance of opinion and flexibility.
I recommend detecting-dnp3-protocol-anomalies for anyone iterating fast on agent tooling; clear intent and a small, reviewable surface area.
We added detecting-dnp3-protocol-anomalies from the explainx registry; install was straightforward and the SKILL.md answered most questions upfront.
Registry listing for detecting-dnp3-protocol-anomalies matched our evaluation — installs cleanly and behaves as described in the markdown.
detecting-dnp3-protocol-anomalies fits our agent workflows well — practical, well scoped, and easy to wire into existing repos.
detecting-dnp3-protocol-anomalies is among the better-maintained entries we tried; worth keeping pinned for repeat workflows.
Solid pick for teams standardizing on skills: detecting-dnp3-protocol-anomalies is focused, and the summary matches what you get after install.
showing 1-10 of 67