external/anthropic-cybersecurity-skills/skills/performing-ot-vulnerability-scanning-safely/SKILL.md
Perform vulnerability scanning in OT/ICS environments safely using passive monitoring, native protocol queries, and carefully controlled active scanning with Tenable OT Security to identify vulnerabilities without disrupting industrial processes or crashing legacy controllers.
npx skillsauth add seikaikyo/dash-skills performing-ot-vulnerability-scanning-safelyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Do not use for aggressive active scanning of production PLCs (can crash legacy controllers), for IT vulnerability scanning using standard Nessus profiles on OT networks, or for penetration testing of live OT systems (see performing-ics-penetration-testing).
Passive monitoring identifies vulnerabilities without sending any packets to OT devices.
#!/usr/bin/env python3
"""OT Safe Vulnerability Scanner Orchestrator.
Coordinates passive monitoring, native protocol queries, and carefully
controlled active scanning for OT vulnerability assessment without
disrupting industrial operations.
"""
import json
import csv
import sys
from datetime import datetime
from typing import Dict, List, Optional
try:
import requests
except ImportError:
print("Install requests: pip install requests")
sys.exit(1)
class OTVulnerabilityScanner:
"""Safe OT vulnerability scanning orchestrator."""
SCAN_SAFETY_LEVELS = {
"passive": {
"description": "Observe network traffic only, zero risk to devices",
"risk_level": "NONE",
"methods": ["traffic_fingerprinting", "protocol_analysis", "version_detection"],
"requires_window": False,
},
"native_query": {
"description": "Query devices using native industrial protocols",
"risk_level": "MINIMAL",
"methods": ["modbus_device_id", "s7_szl_read", "cip_identity", "bacnet_whois"],
"requires_window": True,
},
"controlled_active": {
"description": "Standard vulnerability checks with OT-safe profiles",
"risk_level": "LOW-MODERATE",
"methods": ["credentialed_scan", "banner_grab", "service_detection"],
"requires_window": True,
},
}
def __init__(self, tenable_url: str, api_key: str, verify_ssl: bool = True):
self.tenable_url = tenable_url.rstrip("/")
self.session = requests.Session()
self.session.headers.update({
"X-ApiKeys": f"accessKey={api_key}",
"Content-Type": "application/json",
})
self.session.verify = verify_ssl
self.findings = []
def check_safety_prerequisites(self, scan_level: str, target_subnet: str) -> dict:
"""Verify safety prerequisites before scanning."""
checks = {
"scan_level": scan_level,
"target": target_subnet,
"safety_level": self.SCAN_SAFETY_LEVELS[scan_level],
"checks_passed": [],
"checks_failed": [],
"approved": False,
}
prerequisites = [
{
"name": "Lab validation complete",
"description": "Scan profile tested against each device type in lab environment",
"required_for": ["native_query", "controlled_active"],
},
{
"name": "Vendor warranty verified",
"description": "Scanning will not void vendor support agreements",
"required_for": ["native_query", "controlled_active"],
},
{
"name": "Change management approved",
"description": "Change ticket approved for scanning activity",
"required_for": ["native_query", "controlled_active"],
},
{
"name": "Maintenance window confirmed",
"description": "Operations team confirms acceptable scanning window",
"required_for": ["controlled_active"],
},
{
"name": "Rollback plan documented",
"description": "Procedure to stop scan and recover if device becomes unresponsive",
"required_for": ["controlled_active"],
},
{
"name": "SIS excluded from scope",
"description": "Safety Instrumented Systems are never actively scanned",
"required_for": ["passive", "native_query", "controlled_active"],
},
]
for prereq in prerequisites:
if scan_level in prereq["required_for"]:
checks["checks_passed"].append(prereq["name"])
return checks
def run_passive_assessment(self, site_id: str):
"""Run passive vulnerability assessment using traffic analysis."""
print(f"[*] Running passive vulnerability assessment for site {site_id}")
print(f"[*] Safety Level: NONE - no packets sent to OT devices")
try:
resp = self.session.get(
f"{self.tenable_url}/api/v1/assets",
params={"site_id": site_id}
)
resp.raise_for_status()
assets = resp.json().get("assets", [])
for asset in assets:
asset_id = asset.get("id")
vuln_resp = self.session.get(
f"{self.tenable_url}/api/v1/assets/{asset_id}/vulnerabilities"
)
if vuln_resp.status_code == 200:
vulns = vuln_resp.json().get("vulnerabilities", [])
for vuln in vulns:
self.findings.append({
"asset": asset.get("name", "Unknown"),
"ip": asset.get("ip_address", ""),
"type": asset.get("type", ""),
"vendor": asset.get("vendor", ""),
"cve": vuln.get("cve_id", ""),
"severity": vuln.get("severity", ""),
"cvss": vuln.get("cvss_score", 0),
"description": vuln.get("description", ""),
"detection_method": "passive",
"remediation": vuln.get("remediation", ""),
})
print(f"[+] Passive assessment complete: {len(self.findings)} vulnerabilities found")
except requests.RequestException as e:
print(f"[!] API error: {e}")
def generate_prioritized_report(self, output_file: str):
"""Generate risk-prioritized vulnerability report for OT environment."""
self.findings.sort(key=lambda x: x.get("cvss", 0), reverse=True)
print(f"\n{'='*70}")
print("OT VULNERABILITY ASSESSMENT REPORT")
print(f"{'='*70}")
print(f"Date: {datetime.now().isoformat()}")
print(f"Total Findings: {len(self.findings)}")
severity_counts = {}
for f in self.findings:
sev = f.get("severity", "Unknown")
severity_counts[sev] = severity_counts.get(sev, 0) + 1
print(f"\nSeverity Distribution:")
for sev in ["Critical", "High", "Medium", "Low"]:
print(f" {sev}: {severity_counts.get(sev, 0)}")
# Risk-based prioritization considering OT context
print(f"\n--- RISK-PRIORITIZED FINDINGS ---")
print(f"(Prioritized by CVSS score and OT impact)")
for i, finding in enumerate(self.findings[:20], 1):
print(f"\n {i}. [{finding['severity']}] {finding['cve']}")
print(f" Asset: {finding['asset']} ({finding['ip']})")
print(f" Vendor: {finding['vendor']} | Type: {finding['type']}")
print(f" CVSS: {finding['cvss']}")
print(f" Detection: {finding['detection_method']}")
print(f" Description: {finding['description'][:100]}")
if finding.get("remediation"):
print(f" Remediation: {finding['remediation'][:100]}")
# Export to CSV
if output_file:
with open(output_file, "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=self.findings[0].keys())
writer.writeheader()
writer.writerows(self.findings)
print(f"\n[+] Report exported to {output_file}")
if __name__ == "__main__":
scanner = OTVulnerabilityScanner(
tenable_url="https://tenable-ot.plant.local",
api_key="your-api-key-here",
verify_ssl=True,
)
# Always start with passive assessment
safety_check = scanner.check_safety_prerequisites("passive", "10.10.0.0/16")
print(f"Safety prerequisites: {json.dumps(safety_check, indent=2)}")
scanner.run_passive_assessment(site_id="plant-01")
scanner.generate_prioritized_report("ot_vulnerabilities.csv")
| Term | Definition | |------|------------| | Passive Vulnerability Detection | Identifying vulnerabilities by analyzing mirrored traffic without sending any packets to OT devices | | Native Protocol Query | Using industrial protocols (Modbus FC43, S7 SZL Read, CIP Get Attribute) to safely extract device information | | OT-Safe Scan Profile | Vulnerability scanner configuration designed and lab-tested to avoid crashing industrial controllers | | Compensating Control | Alternative security measure protecting an unpatched OT asset (firewall DPI, network isolation) | | CVSS in OT Context | Standard CVSS scores adjusted for OT impact considering safety, availability, and physical consequences | | Tenable OT Security | Purpose-built OT vulnerability management platform using passive and native protocol-based detection |
OT VULNERABILITY ASSESSMENT REPORT
=====================================
Date: YYYY-MM-DD
Scope: [network segments]
Method: [Passive/Native Query/Controlled Active]
VULNERABILITY SUMMARY:
Critical: [count]
High: [count]
Medium: [count]
Low: [count]
TOP RISK FINDINGS:
1. [CVE] - [CVSS] - [Asset] - [Description]
UNPATACHABLE ASSETS REQUIRING COMPENSATING CONTROLS:
[Asset] - [Reason] - [Recommended Control]
PATCH PRIORITIZATION:
Immediate: [list]
Next Window: [list]
Acceptable Risk: [list with justification]
tools
Conduct comprehensive GDPR compliance assessments by evaluating data processing activities against EU Regulation 2016/679, including Article 30 records of processing, lawful basis validation, data subject rights implementation, Data Protection Impact Assessments (DPIAs) under Article 35, breach notification procedures, international transfer safeguards (SCCs, adequacy decisions), and technical/organizational measures under Article 32. Use when processing personal data of EU residents, preparing for supervisory authority audits, implementing privacy-by-design for new systems, scoping compliance gaps for M&A due diligence, assessing third-party processors, or responding to data subject access requests at scale. Incorporates 2026 guidance from ICO, EDPB, and post-Data (Use and Access) Act 2025 UK-GDPR considerations. Do not use for implementing specific Article 32 controls — use implementing-gdpr-data-protection-controls; or for DSAR automation — use implementing-gdpr-data-subject-access-request.
tools
Parse Windows forensic artifacts—$MFT/$J (MFTECmd), Prefetch (PECmd), registry hives (RECmd), shellbags, and Amcache—into normalized CSV/JSON with Eric Zimmerman's EZ Tools, then load results into Timeline Explorer for analysis. Use during DFIR/incident-response investigations, after triage collection (e.g. with KAPE), to establish program execution, file/folder access, and persistence evidence from acquired forensic images.
development
Build automated multi-turn adversarial attacks against conversational LLM targets using Microsoft PyRIT's RedTeamingOrchestrator, CrescendoOrchestrator (gradual escalation), and TreeOfAttacksWithPruningOrchestrator (adaptive branching), with scorer feedback loops and persisted conversation memory. Use when single-shot LLM scanning is insufficient and you need multi-turn, scorer-driven AI red-team campaigns against a chatbot or agent.
testing
Stand up MISP, enable and cache curated threat feeds (CIRCL, abuse.ch, Feodo Tracker), apply warninglists to suppress false positives, query indicators with PyMISP, and export attributes as auto-generated Suricata/Sigma/Wazuh detection rules. Use when maturing a MISP instance to actively drive detection, curating threat feeds with quality controls, or automating IOC-to-detection pipelines for the SIEM/IDS.