关于 detecting-attacks-on-historian-servers
面向监控运营技术历史数据库的安全技能,涵盖 OSIsoft PI 和 Ignition。它说明如何检查未授权访问、数据篡改、横向移动及针对历史数据库的漏洞利用。
主要功能
- 01监控历史数据库攻击迹象
- 02检查未授权客户端和查询
- 03调查数据篡改
- 04评估横向移动
- 05验证历史数据完整性
适用场景
- 01调查可疑的历史数据库连接
- 02OT 事件后检查数据完整性
- 03排查针对历史数据库的漏洞利用
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill detecting-attacks-on-historian-servers安装后在 Claude Code 中输入/detecting-attacks-on-historian-servers确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSEreferences/api-reference.mdscripts/agent.py详细介绍
检测针对 Historian 服务器的攻击
何时使用
- 在监控连接 IT 与 OT 网络的 historian 服务器以发现入侵迹象时
- 在检测过程 historian 数据库中的未授权查询或数据篡改时
- 在调查攻击者通过 historian 服务器在 IT 与 OT 区域之间进行横向移动时
- 在响应关于利用 historian 特有漏洞(CVE-2025-0921)的告警时
- 在疑似 OT 安全事件后验证 historian 数据完整性时
不要使用于通用数据库安全监控(参见数据库安全技能)、historian 部署与配置,或仅涉及 IT 的数据仓库安全。
前提条件
- Historian 服务器清单(OSIsoft PI、Ignition、GE Proficy、Wonderware InSQL)
- 对 historian 网段的网络监控(面向 IT 和面向 OT 的接口)
- 用于数据完整性验证的 historian API 访问权限
- 正常 historian 查询模式的基线(哪些应用查询哪些标签)
- 了解 historian 架构(数据源、接口、客户端连接)
工作流
第 1 步:监控 Historian 以发现攻击指标
#!/usr/bin/env python3
"""OT Historian Attack Detector.
Monitors historian servers for unauthorized access, data manipulation,
lateral movement indicators, and exploitation of historian-specific
vulnerabilities. Supports OSIsoft PI and Ignition platforms.
"""
import json
import sys
from collections import defaultdict
from datetime import datetime, timedelta
from typing import Dict, List, Optional
try:
import requests
except ImportError:
print("Install requests: pip install requests")
sys.exit(1)
class HistorianAttackDetector:
"""Detects attacks targeting OT historian servers."""
def __init__(self, historian_type: str, historian_url: str,
api_credentials: dict, verify_ssl: bool = False):
self.historian_type = historian_type
self.historian_url = historian_url.rstrip("/")
self.credentials = api_credentials
self.verify_ssl = verify_ssl
self.alerts = []
self.authorized_clients = set()
self.authorized_queries = {}
def set_baseline(self, authorized_clients: List[str],
authorized_query_patterns: Dict[str, List[str]]):
"""Set baseline of authorized historian clients and query patterns."""
self.authorized_clients = set(authorized_clients)
self.authorized_queries = authorized_query_patterns
def check_active_connections(self) -> List[dict]:
"""Check for unauthorized connections to historian."""
connections = []
if self.historian_type == "osisoft_pi":
try:
resp = requests.get(
f"{self.historian_url}/piwebapi/system/status",
auth=(self.credentials.get("username"), self.credentials.get("password")),
verify=self.verify_ssl,
timeout=10,
)
if resp.status_code == 200:
data = resp.json()
connections = data.get("ConnectedClients", [])
except requests.RequestException as e:
print(f"[!] PI Web API error: {e}")
elif self.historian_type == "ignition":
try:
resp = requests.get(
f"{self.historian_url}/data/status/connections",
headers={"Authorization": f"Bearer {self.credentials.get('token')}"},
verify=self.verify_ssl,
timeout=10,
)
if resp.status_code == 200:
connections = resp.json().get("connections", [])
except requests.RequestException as e:
print(f"[!] Ignition API error: {e}")
# Check for unauthorized clients
for conn in connections:
client_ip = conn.get("client_ip", conn.get("address", ""))
if self.authorized_clients and client_ip not in self.authorized_clients:
self.alerts.append({
"severity": "HIGH",
"type": "UNAUTHORIZED_HISTORIAN_CLIENT",
"timestamp": datetime.now().isoformat(),
"source_ip": client_ip,
"details": f"Unauthorized client {client_ip} connected to {self.historian_type} historian",
"mitre": "T0802 - Automated Collection",
})
return connections
def check_data_integrity(self, tags: List[str], hours_back: int = 24):
"""Check historian data for manipulation indicators."""
print(f"[*] Checking data integrity for {len(tags)} tags over last {hours_back}h")
integrity_issues = []
for tag in tags:
try:
if self.historian_type == "osisoft_pi":
resp = requests.get(
f"{self.historian_url}/piwebapi/streams/{tag}/recorded",
params={"startTime": f"*-{hours_back}h", "endTime": "*"},
auth=(self.credentials.get("username"), self.credentials.get("password")),
verify=self.verify_ssl,
timeout=15,
)
if resp.status_code == 200:
items = resp.json().get("Items", [])
# Check for suspicious patterns
if len(items) == 0:
integrity_issues.append({
"tag": tag, "issue": "NO_DATA",
"detail": "No data points in expected timeframe - possible deletion",
})
else:
values = [i.get("Value", 0) for i in items if isinstance(i.get("Value"), (int, float))]
if values and len(set(values)) == 1 and len(values) > 100:
integrity_issues.append({
"tag": tag, "issue": "FLATLINE",
"detail": f"Constant value {values[0]} for {len(values)} points - possible replay/spoofing",
})
except requests.RequestException:
pass
for issue in integrity_issues:
self.alerts.append({
"severity": "HIGH",
"type": f"DATA_INTEGRITY_{issue['issue']}",
"timestamp": datetime.now().isoformat(),
"tag": issue["tag"],
"details": issue["detail"],
"mitre": "T0809 - Data Destruction" if issue["issue"] == "NO_DATA" else "T0832 - Manipulation of View",
})
return integrity_issues
def check_lateral_movement_indicators(self):
"""Check for indicators of historian being used as pivot point."""
indicators = []
# Check 1: Historian making outbound connections to Level 1 devices
# (Historian should receive data, not initiate connections to PLCs)
indicators.append({
"check": "Outbound connections to PLC subnets",
"description": "Historian initiating connections to Level 1 devices may indicate compromise",
"detection": "Monitor firewall logs for historian IP connecting to PLC ports (502, 102, 44818)",
})
# Check 2: New processes or services on historian
indicators.append({
"check": "Unauthorized processes on historian server",
"description": "Attackers may install tools on historian for lateral movement",
"detection": "Monitor process creation events (Sysmon EventID 1) on historian",
})
# Check 3: Unusual authentication to historian
indicators.append({
"check": "Authentication from unexpected sources",
"description": "Compromised IT systems authenticating to historian for pivoting",
"detection": "Monitor Windows Security Event 4624 for logons from non-baseline sources",
})
return indicators
def generate_report(self):
"""Generate historian attack detection report."""
print(f"\n{'='*70}")
print("HISTORIAN ATTACK DETECTION REPORT")
print(f"{'='*70}")
print(f"Historian Type: {self.historian_type}")
print(f"Historian URL: {self.historian_url}")
print(f"Report Time: {datetime.now().isoformat()}")
print(f"Total Alerts: {len(self.alerts)}")
if self.alerts:
print(f"\n--- ALERTS ---")
for alert in self.alerts:
print(f"\n [{alert['severity']}] {alert['type']}")
print(f" Time: {alert['timestamp']}")
print(f" Detail: {alert['details']}")
print(f" MITRE ICS: {alert.get('mitre', 'N/A')}")
print(f"\n--- LATERAL MOVEMENT CHECKS ---")
for indicator in self.check_lateral_movement_indicators():
print(f"\n Check: {indicator['check']}")
print(f" Risk: {indicator['description']}")
print(f" Detection: {indicator['detection']}")
if __name__ == "__main__":
detector = HistorianAttackDetector(
historian_type="osisoft_pi",
historian_url="https://pi-server.plant.local",
api_credentials={"username": "pi_reader", "password": "api_key_here"},
)
detector.set_baseline(
authorized_clients=["10.10.2.10", "10.10.2.20", "10.10.3.50", "10.10.150.10"],
authorized_query_patterns={},
)
detector.check_active_connections()
detector.check_data_integrity(tags=["REACTOR_01.TEMP", "PUMP_03.FLOW"], hours_back=24)
detector.generate_report()
关键概念
| 术语 | 定义 |
|---|---|
| OT Historian | 存储来自 SCADA/DCS 系统的时间序列过程数据的数据库服务器(OSIsoft PI、Ignition、Wonderware) |
| 枢轴点 | Historian 位于 IT 与 OT 网络之间的位置,使其成为攻击者在区域之间移动的首要目标 |
| 数据重放攻击 | 向 HMI 馈送历史数据以掩盖实时过程操纵(Stuxnet 技术) |
| OSIsoft PI | 部署最广泛的 OT historian,被 65% 的 Global 500 流程企业使用 |
| Ignition | Inductive Automation 的 SCADA 平台,带有 historian 模块,因 Python 脚本能力而日益成为攻击目标 |
| CVE-2025-0921 | Ignition SCADA 特权文件系统漏洞,允许通过恶意项目文件进行提权 |
输出格式
HISTORIAN ATTACK DETECTION REPORT
====================================
Historian: [type and hostname]
Date: YYYY-MM-DD
CONNECTION ANALYSIS:
Authorized Clients: [count]
Unauthorized Clients Detected: [count with IPs]
DATA INTEGRITY:
Tags Checked: [count]
Integrity Issues: [count]
Flatline Detections: [count]
Data Gaps: [count]
LATERAL MOVEMENT INDICATORS:
Outbound PLC Connections: [found/not found]
Unauthorized Processes: [found/not found]
Anomalous Authentication: [found/not found]
评论
支持 Markdown