Date: 3 September 2026 | Revision 1.0
This hunt determines whether any adversary has discovered, reached, or exploited an RSLinx Classic communications service in the environment using a crafted Common Industrial Protocol (CIP) message, and whether any RSLinx Classic service failure recorded during the hunt window was induced rather than a software fault. CISA disclosed four denial-of-service vulnerabilities in RSLinx Classic on 1 September 2026 as advisory ICSA-26-244-01. CVE-2026-9621 (CVSS v3.1 8.6, CVSS v4.0 9.2) is an integer overflow or wraparound; CVE-2026-9622 (CVSS v3.1 8.6, CVSS v4.0 9.2) is an integer underflow reached through the CIP Forward Close service; CVE-2026-9624 (CVSS v3.1 7.5, CVSS v4.0 8.7) is an integer underflow arising from insufficient data length validation; and CVE-2026-9625 (CVSS v3.1 7.5, CVSS v4.0 8.7) is a classic buffer overflow triggered by an oversized embedded message request. All four are network-reachable by an unauthenticated attacker, all four crash the service, and all four are corrected in RSLinx Classic version 4.60. No workaround exists, and CISA reports no known public exploitation at the time of writing, so this is a pre-exploitation posture hunt rather than an incident response.
The population in scope is every Windows host running RSLinx Classic at version 4.50 or earlier. Building that population is itself part of the hunt: RSLinx Classic is most often installed as a dependency of FactoryTalk View, Studio 5000 Logix Designer, RSLogix 5000, or a third-party historian or reporting product rather than as a deliberate standalone deployment, so an inventory drawn from license or purchase records will be materially incomplete. The authoritative discriminator is the listening socket — any host answering on TCP port 44818 inside the control network is in scope until its software inventory says otherwise. The network scope is every segment from which a CIP message can reach one of those hosts: Purdue Levels 2 through 3, the Level 3.5 industrial demilitarized zone, the enterprise network wherever a conduit exists, every remote-access path terminating in the control network, and the organization's internet-facing address space.
A structural difference from a controller-directed vulnerability shapes this hunt and should be understood before execution. The vulnerable component here is a Windows service, not a PLC, which means the victim host carries full endpoint telemetry — process, network, service, and crash records are all available where a faulted controller would have offered nothing. Endpoint evidence is therefore the primary leg of this hunt and network capture is corroborating, which is the reverse of the usual OT arrangement. The corollary is that the terminal indicator — an unexpected RSLinx service termination — is an event that operations staff have very often learned to treat as routine software instability, so a large part of this hunt's value lies in re-examining service crashes that were previously dismissed.
The hunt window is 90 days back from the execution date, extended to 180 days for the external-exposure hypothesis so that continuous exposure spanning the disclosure date can be established. Hypothesis 1 (external attack surface) executes FIRST and its output scopes the remainder: an internet-reachable RSLinx Classic listener that is confirmed organization-owned converts the internal hunt for that host from a sample into an exhaustive pass. Out of scope: vulnerabilities in FactoryTalk, Studio 5000, or the controllers themselves except where they bear on reachability to an RSLinx host, and any active scanning of RSLinx Classic services, which is prohibited for the duration of this hunt because the affected service crashes on malformed input by definition.
Hypothesis 1: an external actor can reach an internet-facing RSLinx Classic listener or a Rockwell Automation software surface belonging to the organization, observable as an exposed service in passive internet-scan indices. Execute FIRST; its output scopes the internal hypotheses.
MITRE ATT&CK: Reconnaissance | T1595 Active Scanning (defender-side: passive discovery of what an actor would enumerate) · Initial Access | T1133 External Remote Services · Initial Access | T1190 Exploit Public-Facing Application · ICS | T0883 Internet Accessible Device · ICS | T0819 Exploit Public-Facing Application — an internet-reachable CIP listener removes every network precondition for all four CVEs and exposes the service to any unauthenticated actor.
Collection Queries — passive external indices (STRICTLY passive; never probe target infrastructure):
Shodan — run each anchor as its own query; space-separated filters combine with AND, so combining anchors over-constrains to a false zero:
port:44818 "EtherNet/IP"
port:2222 "EtherNet/IP"
port:44818,2222 net:<your_public_CIDR>
port:44818,2222 org:"<Your Org Name>"
port:44818,2222 asn:AS<your_ASN>
http.html:"Rockwell"
http.html:"FactoryTalk"
http.html:"RSLinx"
// count-only first pass — does not consume result credits
shodan count 'port:44818 net:<your_public_CIDR>'
// full pull with the fields needed for attribution and version triage
shodan search --fields ip_str,port,org,hostnames,product,version 'port:44818,2222 net:<your_public_CIDR>'
Censys (CenQL — use the current host.services form; the legacy services.* CSL form silently returns zero rows with no error):
host.services: (port = "44818")
host.services: (port = "2222")
host.services.software.vendor: "Rockwell Automation"
host.services.endpoints.http.body: "FactoryTalk"
host.services: (port = "44818") and (host.autonomous_system.asn = <your_ASN> or host.ip: "<your_public_CIDR>")
Netlas (tie-breaker only, when Shodan and Censys disagree):
host:<your_public_CIDR> AND port:(44818 OR 2222)
http.body:"RSLinx"
certificate.subject.common_name:"<your_domain>"
An RSLinx Classic host is a Windows server, so where a CIP listener is found on organization space, sweep the same address for the Windows management surface that would let an actor reach the host by a second route:
port:3389,5985,5986,445 net:<your_public_CIDR>
Cross-reference every exposed service and banner version against the CISA Known Exploited Vulnerabilities catalog at https://www.cisa.gov/known-exploited-vulnerabilities-catalog and against the four advisory CVEs directly. API credentials are referenced by environment-variable name only (SHODAN_API_KEY, CENSYS_API_SECRET, CENSYS_ORG_ID, NETLAS_API_KEY) and are never inlined into a query, script, or command; a missing key is non-blocking — skip that engine and record the coverage gap.
Analysis Queries — attribution, triage, and corroboration:
Attribute every hit with at least two independent ownership signals (PTR record, ASN registration, TLS certificate CN or SAN, WHOIS or RDAP registrant, the organization's own public IP and cloud asset inventory) before reporting it. Shared hosting, CDN, and carrier infrastructure break attribution in both directions; an unattributable hit is inconclusive, not negative.
Do not assign an OT severity tier from a port number alone. TCP/44818 and TCP/2222 are registered to EtherNet/IP, which is what an index reports whether or not the host was observed speaking CIP. Require a banner, service fingerprint, or handshake artifact before recording "EtherNet/IP exposed"; until then the finding reads "service on an OT-registered port, protocol unconfirmed". This matters more here than usual, because an RSLinx Classic host will also present ordinary Windows services and can be misread in either direction.
Apply the honeypot guardrail. OT protocols are the most heavily honeypotted services on the internet and Conpot exists specifically to answer EtherNet/IP. Down-weight any hit carrying Shodan's honeypot tag or a Conpot fingerprint and never report it as organizational exposure. A genuine RSLinx Classic host will present a Windows fingerprint alongside the CIP port; a bare CIP port with no supporting Windows surface is a honeypot candidate.
Version-triage each banner against the affected range (4.50 and earlier) and record the index scan timestamp with every hit. A banner is stale in both directions: an in-range version is a prompt to verify current state from the organization's own inventory, and a 4.60 banner is not evidence of safety if the scan predates the exposure window.
Corroborate in at least two of Shodan, Censys, and Netlas before escalating. Absence from an index is evidence only that the index did not observe the host; never write "not exposed" on the strength of one engine or of a sweep whose anchors were incomplete.
Historical pivot: establish whether each exposure spans 1 September 2026. Continuous exposure across the disclosure date means the host was presumptively enumerated by the population of actors that reads CISA ICS advisories.
Reconcile every externally observed, organization-attributed host against the authoritative public IP and cloud asset inventory. A host presenting an organization certificate or hostname that is absent from the inventory is a shadow-infrastructure finding in its own right, regardless of its RSLinx version.
Feed every confirmed-owned, in-range instance into Hypotheses 2, 3, and 4 as a priority target and hunt it exhaustively rather than by sampling.
Hypothesis 2: an actor has delivered a crafted CIP message to an RSLinx Classic service, observable as inbound CIP traffic on TCP/UDP 44818 or UDP 2222 from an unauthorized source correlated with an unexpected termination and restart of the RSLinx Classic service on the receiving host.
MITRE ATT&CK: ICS | T0814 Denial of Service — the direct effect of all four CVEs · ICS | T0881 Service Stop — the RSLinx Classic service is halted by the crafted message · ICS | T0815 Denial of View and ICS | T0813 Denial of Control — the operator loses graphics, live values, and command through the failed communications path · ICS | T0829 Loss of View and ICS | T0826 Loss of Availability · Enterprise | T1499.004 Endpoint Denial of Service: Application or System Exploitation — the enterprise-side equivalent for a crafted-input service crash.
Collection Queries:
CrowdStrike Falcon LogScale (CQL) — build the RSLinx host population from the listening socket rather than from the software inventory, which is the discriminator this hunt depends on:
#event_simpleName=NetworkListenIP4
| in(LocalPort, values=["44818","2222"])
| table([@timestamp, aid, ComputerName, LocalAddressIP4, LocalPort, Protocol, ContextProcessId], limit=20000)
CrowdStrike Falcon LogScale (CQL) — enumerate every inbound CIP session and resolve it to the accepting process. NetworkReceiveAcceptIP4 carries no image name, so the listener is recovered by correlating ContextProcessId to the ProcessRollup2 TargetProcessId on the same agent:
(#event_simpleName=NetworkReceiveAcceptIP4 OR #event_simpleName=/ProcessRollup2/)
| case {
#event_simpleName=NetworkReceiveAcceptIP4 | falconPID := ContextProcessId ;
#event_simpleName=/ProcessRollup2/ | falconPID := TargetProcessId ;
}
| groupBy([aid, falconPID], function=collect([ComputerName, RemoteAddressIP4, LocalAddressIP4, LocalPort, ImageFileName, CommandLine, UserName]), limit=100000)
| in(LocalPort, values=["44818","2222"])
CrowdStrike Falcon LogScale (CQL) — RSLinx process lifecycle, which is the crash record. EndOfProcess does not carry an image name, so the process identity comes from the ProcessRollup2 side of the correlation and never from the termination event itself:
(#event_simpleName=EndOfProcess OR #event_simpleName=/ProcessRollup2/)
| falconPID := TargetProcessId
| groupBy([aid, falconPID], function=collect([ComputerName, FileName, ImageFileName, CommandLine, ProcessStartTime, ProcessEndTime]), limit=100000)
| FileName = /^(RSLINX\.EXE|RsvcHost\.exe|RSLinxNG\.exe)$/i
CrowdStrike Falcon LogScale (CQL) — service restart events, the second independent record of the same crash:
#event_simpleName=ServiceStarted
| ServiceDisplayName = /rslinx/i
| table([@timestamp, aid, ComputerName, ServiceDisplayName, ServiceType, ContextProcessId], limit=20000)
The RSLinx Classic executable name varies by installed version and by whether the product runs as a service or as a desktop application; confirm the exact binary and service display names on one known host before relying on the regex above, and widen it if the local build differs.
BPF packet capture — capture CIP traffic at the switch port or SPAN facing each RSLinx Classic host. These filters observe mirrored traffic only; no packet is injected toward the service:
# all EtherNet/IP toward the RSLinx host, rolling hourly files, 1 GiB cap each
tcpdump -i <span_iface> -s 0 -G 3600 -C 1024 -w /captures/rslinx_%Y%m%d_%H%M%S.pcap 'host <rslinx_host_ip> and (tcp port 44818 or udp port 44818 or udp port 2222)'
# inbound explicit messaging only, scoped to one host under investigation
tcpdump -i <span_iface> -s 0 -w /captures/rslinx_inbound.pcap 'dst host <rslinx_host_ip> and tcp dst port 44818'
# session establishment rate baseline
tcpdump -i <span_iface> -nn 'dst host <rslinx_host_ip> and tcp dst port 44818 and tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack = 0'
# oversized frames toward the CIP listener — the CVE-2026-9625 embedded-message shape
tcpdump -i <span_iface> -s 0 -w /captures/rslinx_large.pcap 'dst host <rslinx_host_ip> and tcp dst port 44818 and greater 900'
Datadog Log Search — the Windows-side crash record, which is available on every RSLinx host whether or not endpoint detection and response covers it:
source:windows @evt.id:(7031 OR 7034) "RSLinx"
// time range: last 90 days
source:windows @evt.id:1000 "RSLINX"
// time range: last 90 days
source:windows @evt.id:7036 "RSLinx"
// time range: last 90 days
Datadog Log Search — the boundary record of inbound CIP toward the RSLinx population:
source:(firewall OR syslog) ("44818" OR "2222") -@network.client.ip:10.* -@network.client.ip:172.16.* -@network.client.ip:192.168.*
// time range: last 90 days
Datadog CloudTrail — cloud-side changes that could have opened a path to the control network during the window:
source:cloudtrail @evt.name:(AuthorizeSecurityGroupIngress OR CreateRoute OR CreateVpnConnection OR ModifyVpcPeeringConnectionOptions)
// Analytics: Table view, group by @network.client.ip, @userIdentity.arn; time range: last 90 days
Datadog Live Process Monitoring — Infrastructure > Processes, not a log search. Confirms the RSLinx service is running and identifies the account it runs under:
command:rslinx
command:RsvcHost
// Fallback where Live Process Monitoring is not enabled on OT-locked hosts:
source:windows @evt.id:4688 ("RSLINX" OR "RsvcHost")
// time range: last 90 days
Windows Event IDs to collect from every RSLinx Classic host:
Get-WinEvent -FilterHashtable @{LogName='System'; Id=7031,7034,7036; StartTime=(Get-Date).AddDays(-90)} |
Where-Object { $_.Message -match 'RSLinx' } |
Select-Object TimeCreated, MachineName, Id, Message |
Export-Csv -NoTypeInformation C:\hunt\rslinx_service_events.csv
Get-WinEvent -FilterHashtable @{LogName='Application'; Id=1000,1001; StartTime=(Get-Date).AddDays(-90)} |
Where-Object { $_.Message -match 'RSLINX|RsvcHost' } |
Select-Object TimeCreated, MachineName, Id, Message |
Export-Csv -NoTypeInformation C:\hunt\rslinx_app_errors.csv
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=5156; StartTime=(Get-Date).AddDays(-90)} |
Where-Object { $_.Message -match '44818|2222' } |
Select-Object TimeCreated, MachineName, Message |
Export-Csv -NoTypeInformation C:\hunt\rslinx_wfp_inbound.csv
OT Data Collection: Claroty xDome — Devices > All Devices > Advanced Filters: Category = IT or OT with Purdue Level IN (Level 2, Level 3) and a service on port 44818, Export CSV to seed the RSLinx host population. Network > Communication > Communication Analysis: Side A unscoped, Communication: Protocol = CIP and Port = 44818, Side B pinned to the RSLinx host set, Time Frame Past Month, run once per month of the window. Note that a one-side-pinned Communication Analysis export cannot be pivoted into a per-site matrix, so confirm the SIDE A SITE NAME column carries more than one distinct value before drawing any per-site conclusion from it. Alerts & Threats > All Alerts: Alert Category = Threat Alert with the RSLinx hosts as Device Filters. API: POST /api/v1/devices/, POST /api/v1/alerts/, and POST /api/v1/device/communication-map/ per asset_id for pulls above the 100,000-row export ceiling.
OT Data Collection: Claroty CTD — where on-prem CTD is deployed, its continuous full-PCAP retention is the only source that recovers the actual bytes of a CIP message sent weeks before the hunt. Request the retrospective packet window around each RSLinx service crash through the CTD /v1/events/<id>/pcap surface rather than relying on the sensor-local buffer that xDome provides.
OT Data Collection: Dragos Platform — Assets: scope to the Windows hosts at Purdue Levels 2 and 3 carrying a Now-priority vulnerability, and cross-reference against the RSLinx population from the listener query. Communications Hub: filter dest-asset = the RSLinx host set AND protocol = EtherNet/IP or CIP across the window, sort by source, and pivot each session to its asset record and event PCAP. Query the Industrial Protocols Query Focused Dataset for Forward Close request volume by source, which is the CVE-2026-9622 axis specifically. Export the event PCAP from any firing notification, or Export PCAP for window from the Communications detail, and pull it from SiteStore for offline analysis. API: SiteStore Assets, Notifications, and Vulnerabilities collections.
OT Data Collection: Nozomi Guardian — query the links table, where traffic lives; the nodes table returns endpoints, not conversations. Note that head without an explicit N silently returns 10 rows, and a field-to-field comparison requires the $ prefix or it compares against a string literal and returns a false zero:
links | where protocol == "enip" | join nodes to ip | join nodes from ip
| select from_ip to_ip protocol joined_node_to_ip.label->to_label joined_node_from_ip.label->from_label
| head 5000
links | where protocol == "enip" and from_zone != to_zone | head 5000
nodes | where_link protocol == enip | head 2000
alerts | where time >= days_ago(90) | where mitre_attack ~= "T0814" | sort record_created_at asc | head 500
OT Data Collection: Armis Centrix — the analyst sets the time window in the UI time-picker; no timeFrame clause appears in the query body. Add the tenant's own OT boundary literal to the endpoint scoping only after enumerating it from the console Sites and Boundaries view — do not substitute a shipped default such as Corporate, which parses cleanly and returns a false zero at most tenants:
in:services
port:44818,2222
in:ipConnections
serverPort:44818,2222
in:ipConnections
serverPort:44818,2222
endpointA:(networkLocation:"External")
OT Data Collection: Tenable OT — Inventory > All Assets filtered to AssetType IN (Server, OtServer, Historian, Eng, OtWorkstation) for the candidate RSLinx host population; note that Eng and OtWorkstation are distinct from Workstation, and scoping to Workstation alone misses exactly the hosts that matter. Events: pull Intrusion Detection and Network Scan policy violations across the window, which surface under Events and not under Risks > Findings. Programmatic pulls go through the GraphQL endpoint at /graphql or through pyTenable; there is no REST /api/v1/ query surface for OT Security. Exclude TenableSensor, TenableIcp, and TenableEm from any asset count reported to stakeholders.
OT Data Collection: Forescout eyeInspect — use the Command Center Asset Inventory filtered by Purdue level and role for the candidate host population, and the Alerts view filtered by protocol and MITRE ATT&CK for ICS technique for CIP-decoded events. eyeInspect exposes no analyst-facing query language, so source fan-in analysis requires forwarding the CEF or syslog stream to the SIEM and querying there; the Forescout OT Network Security Monitoring App for Splunk is the canonical path. Pivot from an alert to the underlying flow and packet capture where sensor PCAP is enabled.
SNMP polling — poll the switch port facing each RSLinx Classic host to establish the traffic and error baseline the crash windows are read against. Provision SNMPv3 credentials through snmp.conf or createUser rather than inline, since inline passphrases are visible in the process table, and treat any v1 or v2c community-string access to a control-network asset as a finding in its own right:
snmpwalk -v2c -c <community> <switch_ip> IF-MIB::ifTable
snmpget -v2c -c <community> <switch_ip> IF-MIB::ifHCInOctets.<ifIndex> IF-MIB::ifHCOutOctets.<ifIndex> IF-MIB::ifInErrors.<ifIndex> IF-MIB::ifOutErrors.<ifIndex>
snmpwalk -v3 -l authPriv -u <user> -a SHA-256 -x AES-256 <switch_ip> system
Poll at 60-second intervals through the hunt window and diff successive values; the 32-bit ifInOctets and ifOutOctets counters wrap on high-speed links, so use the 64-bit ifHCInOctets (1.3.6.1.2.1.31.1.1.1.6) and ifHCOutOctets (.10) for volumetric baselining. Collect SNMP trap logs for the window and flag linkDown and linkUp traps (1.3.6.1.6.3.1.1.5.3 and .4) around the RSLinx host port, and authenticationFailure traps (1.3.6.1.6.3.1.1.5.5) indicating unauthorized access attempts on control-network interfaces.
Historian and SCADA alarm correlation — export the communication loss, device offline, and data quality alarms for every controller and device that reaches the plant through an RSLinx Classic host, across the full window, together with the operator action log. A CIP service crash presents to the process side as simultaneous communication loss across the entire set of devices behind that RSLinx instance, which is a distinctive signature no single device fault reproduces. For each such event, record the timestamp to the second, the number of devices that went to bad quality at once, the duration, and whether a maintenance work order or change record exists. Cluster loss events with no change record are the candidate set.
YARA file-system scan — scan the RSLinx host population and the engineering estate for CIP exploitation tooling and staged payloads:
yara -r /opt/hunt/rules/rslinx_cip_hunt.yar C:\Users\ >> /opt/hunt/hits_users.txt
yara -r /opt/hunt/rules/rslinx_cip_hunt.yar C:\ProgramData\ >> /opt/hunt/hits_programdata.txt
yara -r /opt/hunt/rules/rslinx_cip_hunt.yar C:\Windows\Temp\ >> /opt/hunt/hits_temp.txt
Analysis Queries:
CrowdStrike Falcon LogScale (CQL) — RSLinx crash-loop detection. Repeated starts of the same binary within an hour on one host is the signature of a service being crashed and restarted rather than of a single software fault:
#event_simpleName=/ProcessRollup2/
| FileName = /^(RSLINX\.EXE|RsvcHost\.exe|RSLinxNG\.exe)$/i
| bucket(span=1h, limit=500, field=[aid, ComputerName], function=count(as=RSLinxStarts))
| RSLinxStarts > 3
| sort(RSLinxStarts, order=desc, limit=200)
CrowdStrike Falcon LogScale (CQL) — inbound CIP session rate per source. A client opening more than a hundred CIP sessions to one RSLinx host in a five-minute window is behaving nothing like FactoryTalk or Studio 5000:
#event_simpleName=NetworkReceiveAcceptIP4
| in(LocalPort, values=["44818","2222"])
| bucket(span=5m, limit=500, field=[aid, RemoteAddressIP4], function=count(as=CipAccepts))
| CipAccepts > 100
| sort(CipAccepts, order=desc, limit=200)
CrowdStrike Falcon LogScale (CQL) — rarity analysis on inbound CIP sources. Sanctioned CIP clients are few and persistent; the analytic signal is the source that appears once or twice across the whole window:
#event_simpleName=NetworkReceiveAcceptIP4
| in(LocalPort, values=["44818","2222"])
| groupBy([RemoteAddressIP4], function=count(as=SessionCount), limit=100000)
| sort(SessionCount, order=asc, limit=50)
Wireshark display filters — apply against the captured PCAP; the tshark equivalent is given for each so the analysis is scriptable across a large capture set:
enip
cip
enip.command == 0x006f || enip.command == 0x0070
cip.service == 0x4e
cip.service == 0x54 || cip.service == 0x5b
enip.length > 504
cip.class == 0x06
tcp.port == 44818 && tcp.flags.reset == 1
tcp.port == 44818 && tcp.analysis.retransmission
tshark -r /captures/rslinx_20260901_120000.pcap -Y 'enip' -T fields -e frame.time -e ip.src -e ip.dst -e enip.command -e enip.length -e enip.session > enip_summary.tsv
tshark -r /captures/rslinx_20260901_120000.pcap -Y 'cip.service == 0x4e' -T fields -e frame.time -e ip.src -e ip.dst -e cip.service | sort | uniq -c | sort -rn
tshark -r /captures/rslinx_20260901_120000.pcap -Y 'enip.length > 504' -T fields -e frame.time -e ip.src -e ip.dst -e enip.command -e enip.length
tshark -r /captures/rslinx_20260901_120000.pcap -q -z conv,tcp | head -50
Four analytic axes map one-to-one onto the four CVEs and should be read separately rather than as one anomaly score. The Forward Close service code 0x4E on the Connection Manager object (class 0x06) is the CVE-2026-9622 axis; because Forward Close is exercised constantly during normal connection teardown, the signal is rate and source rather than presence, and a baseline is mandatory before any threshold is read. The encapsulation length field is the CVE-2026-9624 axis: a frame whose declared length is inconsistent with its actual payload is the closest network-observable proxy for the length-validation defect. Oversized frames carrying an embedded message request are the CVE-2026-9625 axis. CVE-2026-9621 has no distinctive wire signature beyond a malformed length or size field, so it is hunted by correlation with the crash record rather than by content.
The determinative correlation for this entire hypothesis is temporal: take the timestamp of every RSLinx service termination from the Windows System and Application logs and from the Falcon process record, and extract the CIP traffic in the sixty seconds preceding each one. A crash preceded by CIP from a source outside the authorized set, or by a malformed or oversized frame from any source, is escalated. A crash with a clean preceding traffic record and a faulting-module signature consistent with a known software fault is dispositioned as operational.
Datadog Log Analytics — crash frequency and inbound source correlation:
source:windows @evt.id:(7031 OR 7034) "RSLinx"
// Analytics: Timeseries view, group by host; time range: last 90 days
source:(firewall OR syslog) ("44818" OR "2222")
// Analytics: Table view, group by @network.client.ip; time range: last 90 days
Datadog Audit Trail — establish whether any monitor, log pipeline, or integration covering the RSLinx hosts or the OT boundary was disabled or modified during the window, which would create the visibility gap an actor needs:
source:datadog @evt.name:Monitor @asset.type:monitor
// Analytics: Table view, group by @usr.email, @action; time range: last 90 days
source:datadog @evt.name:Integration
// Analytics: Table view, group by @usr.email, @action; time range: last 90 days
Datadog Monitor definition:
Type: Log Alert
Query: source:windows @evt.id:(7031 OR 7034) "RSLinx"
Evaluation window: last 10 minutes
Alert condition: count > 0
Message: "ALERT: RSLinx Classic service terminated unexpectedly — treat as possible exploitation of ICSA-26-244-01 until the preceding CIP traffic is reviewed @ot-soc-pagerduty"
Prerequisites: Windows System and Application event logs forwarded to Datadog from every RSLinx Classic host; host tagging that identifies the RSLinx population
Create via: Monitors > New Monitor > Log Alert OR POST /api/v1/monitors
Windows Event Log PowerShell analysis — correlate the crash timeline against the inbound connection record on the same host, which is the single highest-value analysis in this hunt and works even where no packet capture existed:
$crashes = Get-WinEvent -FilterHashtable @{LogName='System'; Id=7031,7034; StartTime=(Get-Date).AddDays(-90)} |
Where-Object { $_.Message -match 'RSLinx' }
foreach ($c in $crashes) {
$start = $c.TimeCreated.AddSeconds(-60)
$end = $c.TimeCreated.AddSeconds(5)
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=5156; StartTime=$start; EndTime=$end} |
Where-Object { $_.Message -match '44818|2222' } |
Select-Object @{n='CrashTime';e={$c.TimeCreated}}, TimeCreated, MachineName, Message |
Export-Csv -Append -NoTypeInformation C:\hunt\cip_before_rslinx_crash.csv
}
Get-WinEvent -FilterHashtable @{LogName='Application'; Id=1000; StartTime=(Get-Date).AddDays(-90)} |
Where-Object { $_.Message -match 'RSLINX|RsvcHost' } |
ForEach-Object { [pscustomobject]@{ Time=$_.TimeCreated; Host=$_.MachineName; Detail=($_.Message -split "`n" | Select-Object -First 6) -join ' | ' } } |
Export-Csv -NoTypeInformation C:\hunt\rslinx_faulting_modules.csv
Group the Application Error records by faulting module and offset. A population of crashes sharing one faulting module and offset is either a single reproducible software fault or a single reproducible exploit, and the network record for those specific timestamps is what distinguishes them. Preserve any Windows Error Reporting crash dump referenced by the Event ID 1001 records before it is aged out, since it is the only artifact that can confirm which parsing path failed.
YARA memory scan — scan the RSLinx host and any suspect client for in-memory CIP crafting artifacts. Classic YARA 4.5 takes the PID as a positional target; there is no -p flag for process scanning, and YARA-X does not support process memory at all:
yara /opt/hunt/rules/rslinx_cip_hunt.yar 4812
Get-Process | Where-Object { $_.ProcessName -match 'python|powershell|rslinx|RsvcHost' } |
ForEach-Object { & yara C:\hunt\rules\rslinx_cip_hunt.yar $_.Id } >> C:\hunt\yara_mem_hits.txt
CrowdStrike Custom IOAs can carry the process-creation and command-line halves of these rules as standing detection content, and Falcon Real Time Response can put the YARA binary and rule file and run the scan on a remote host, which is the practical route on control-network Windows hosts that cannot be reached interactively.
Hypothesis 3: an actor has enumerated the control network for RSLinx Classic listeners and fingerprinted their versions ahead of exploitation, observable as CIP connection attempts or List Identity requests fanning out across many addresses from a single source, and as inbound CIP from hosts that have no operational reason to speak it.
MITRE ATT&CK: ICS | T0846 Remote System Discovery — enumeration of CIP listeners by protocol response · ICS | T0888 Remote System Information Discovery — the EtherNet/IP identity response discloses product and revision, which is what an actor needs to select in-range targets · Enterprise | T1046 Network Service Discovery · Enterprise | T1592 Gather Victim Host Information.
An EtherNet/IP List Identity request (encapsulation command 0x0063) answered by an RSLinx Classic host returns the product name and revision directly, which makes a single broadcast or sweep sufficient to build the affected-host list. This is the cheapest step available to an actor and the loudest one available to a defender, which is why it is hunted separately from the exploitation itself.
Collection Queries:
CrowdStrike Falcon LogScale (CQL) — fan-out from a monitored host. A source reaching more than ten distinct addresses on the CIP ports is enumerating, not communicating:
#event_simpleName=NetworkConnectIP4
| in(RemotePort, values=["44818","2222"])
| groupBy([aid, ComputerName, LocalAddressIP4], function=[count(as=ConnCount), count(field=RemoteAddressIP4, distinct=true, as=DistinctTargets)], limit=100000)
| DistinctTargets > 10
| sort(DistinctTargets, order=desc, limit=200)
CrowdStrike Falcon LogScale (CQL) — fan-in at the RSLinx hosts, which catches an enumeration launched from an unmonitored source that the fan-out query above cannot see:
#event_simpleName=NetworkReceiveAcceptIP4
| in(LocalPort, values=["44818","2222"])
| groupBy([aid, ComputerName, RemoteAddressIP4], function=count(as=InboundCipSessions), limit=100000)
| sort(InboundCipSessions, order=desc, limit=200)
CrowdStrike Falcon LogScale (CQL) — CIP enumeration and exploitation tooling execution on any monitored host:
#event_simpleName=/ProcessRollup2/
| CommandLine = /(pycomm3|cpppo|plcscan|cipscan|enip[_\-]?info|enip[_\-]?enumerate|forwardclose)/i
| table([@timestamp, aid, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, SHA256HashData], limit=20000)
BPF packet capture — capture the enumeration traffic specifically. List Identity is commonly sent to UDP/44818, including as a broadcast:
tcpdump -i <span_iface> -s 0 -w /captures/enip_listidentity.pcap 'udp port 44818'
tcpdump -i <span_iface> -s 0 -w /captures/enip_broadcast.pcap 'udp port 44818 and (ip broadcast or ip multicast)'
tcpdump -i <span_iface> -nn -c 5000 'tcp dst port 44818 and tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack = 0'
Datadog Log Search — the boundary record of a sweep, including the denied attempts that never reached a host:
source:(firewall OR syslog) ("44818" OR "2222") ("deny" OR "drop" OR "reject")
// time range: last 90 days
Windows Event IDs to collect: 5156 and 5157 from every control-network Windows host, which record permitted and blocked inbound and outbound connections and therefore capture a sweep launched from a compromised host even where no packet capture existed at the time.
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=5157; StartTime=(Get-Date).AddDays(-90)} |
Where-Object { $_.Message -match '44818|2222' } |
Select-Object TimeCreated, MachineName, Message |
Export-Csv -NoTypeInformation C:\hunt\cip_blocked_connections.csv
OT Data Collection: Claroty xDome — Alerts & Threats > All Alerts filtered to the scanning and reconnaissance categories across the window; Network > Communication > Communication Analysis with Side A pinned to a suspect source address and Side B unscoped, then read the distinct peer count. Alerts & Threats > Threats > Malicious IPs cross-referenced against every external source observed reaching the CIP ports.
OT Data Collection: Dragos Platform — triage Notifications filtered to ATT&CK for ICS Discovery techniques across the window and escalate any carrying an Activity Group tag. Query the Industrial Protocols Query Focused Dataset for List Identity and identity-request volume grouped by source address.
OT Data Collection: Armis Centrix — run the pre-flight decisionData exposure probe on the SSL Connection activity root before relying on the port-scan fields, since decisionData provisioning varies by tenant and an unexposed attribute renders as a clean zero with the error below the grid:
in:activity
type:"Port Scan Detected"
decisionData:(targetPorts:44818 scanType:"Many Targets")
OT Data Collection: Nozomi Guardian — rank enumeration sources from the links table and confirm against the alerts table:
links | where protocol == "enip" | group_by from_ip | sort count desc | head 100
alerts | where time >= days_ago(90) | where mitre_attack ~= "T0846" | head 500
OT Data Collection: Tenable OT — Events filtered to Intrusion Detection and Network Scan policy violations across the window; the Network Map grouped by Purdue level shows which levels the scanning source reached.
OT Data Collection: Forescout eyeInspect — the Alerts view carries protocol-decoded reconnaissance events; forward the CEF stream to the SIEM for source fan-out analysis, which eyeInspect cannot express natively.
Analysis Queries:
Wireshark display filters — isolate the enumeration and read what the responses disclosed:
enip.command == 0x0063
enip.command == 0x0063 && udp
enip.command == 0x0004
tshark -r /captures/enip_listidentity.pcap -Y 'enip.command == 0x0063' -T fields -e frame.time -e ip.src -e ip.dst | sort | uniq -c | sort -rn
tshark -r /captures/enip_listidentity.pcap -Y 'enip' -T fields -e ip.src | sort | uniq -c | sort -rn | head -20
CrowdStrike Falcon LogScale (CQL) — timeline correlation between a broad enumeration and a subsequent single-target burst, which is the sequence that precedes exploitation:
#event_simpleName=NetworkReceiveAcceptIP4
| in(LocalPort, values=["44818","2222"])
| bucket(span=1h, limit=500, field=[RemoteAddressIP4], function=count(field=ComputerName, distinct=true, as=DistinctHostsReached))
| DistinctHostsReached > 5
| sort(DistinctHostsReached, order=desc, limit=200)
Datadog Log Analytics — sweep-source ranking from the boundary logs:
source:(firewall OR syslog) ("44818" OR "2222")
// Analytics: Top List view, group by @network.client.ip; sort descending for the widest sources; time range: last 90 days
Datadog Monitor definition:
Type: Log Alert
Query: source:(firewall OR syslog) ("44818" OR "2222") ("deny" OR "drop" OR "reject")
Evaluation window: last 15 minutes
Alert condition: count > 25
Message: "ALERT: repeated denied EtherNet/IP connection attempts at the OT boundary — possible RSLinx Classic enumeration ahead of ICSA-26-244-01 exploitation @ot-soc-pagerduty"
Prerequisites: Level 3.5 boundary firewall deny logging enabled and forwarded to Datadog; threshold tuned against the site's normal denied-flow rate
Create via: Monitors > New Monitor > Log Alert OR POST /api/v1/monitors
OT protocol analysis — for every identity response captured, extract the vendor identifier, product name, and revision, and match the revision against the affected range. The set of RSLinx Classic hosts whose version was disclosed to an unbaselined source during the window is the priority remediation list regardless of whether exploitation followed, because the actor now holds a version-accurate target list.
Hypothesis 4: an actor has compromised a host inside the control network or at its boundary, obtained the credentials and CIP reachability that host holds, and used it as the launch point for RSLinx-directed activity, observable as unauthorized logons, credential-access artifacts, and inbound CIP from a host that has no operational reason to originate it.
MITRE ATT&CK: Enterprise | T1021.001 Remote Services: Remote Desktop Protocol and Enterprise | T1021.002 SMB/Windows Admin Shares — the common paths from the enterprise network onto a control-network Windows host · Enterprise | T1003 OS Credential Dumping — control-network Windows hosts hold the accounts that reach everything else in the zone · ICS | T0859 Valid Accounts · ICS | T0866 Exploitation of Remote Services · ICS | T0867 Lateral Tool Transfer — CIP tooling staged onto a control-network host · ICS | T0818 Engineering Workstation Compromise.
Collection Queries:
CrowdStrike Falcon LogScale (CQL) — network and interactive logons to the RSLinx and engineering host population:
#event_simpleName=UserLogon
| in(LogonType, values=["3","10"])
| ComputerName = /(rslinx|ftview|hist|scada|ews|eng)/i
| table([@timestamp, aid, ComputerName, UserName, LogonType, LogonDomain, RemoteAddressIP4], limit=20000)
CrowdStrike Falcon LogScale (CQL) — CIP tooling written to disk. There is no generic FileWritten event in Falcon; the family regex catches every per-file-type write event:
#event_simpleName=/FileWritten$/
| TargetFileName = /.*\\(pycomm3|cpppo|plcscan|cipscan|enip).*/i
| table([@timestamp, aid, ComputerName, TargetFileName, SHA256HashData, ContextProcessId], limit=20000)
Datadog Log Search — remote-access sessions terminating on the control-network Windows population:
source:windows @evt.id:4624 @evt.LogonType:(3 OR 10)
// time range: last 90 days
source:windows @evt.id:4648
// time range: last 90 days
Datadog CloudTrail — cloud identity activity that could grant a path to the control network:
source:cloudtrail @evt.name:(AssumeRole OR GetSessionToken OR CreateAccessKey) -@network.client.ip:10.*
// Analytics: Table view, group by @userIdentity.arn, @network.client.ip; time range: last 90 days
Datadog Live Process Monitoring — Infrastructure > Processes:
command:python user:<engineering_service_account>
command:powershell user:<engineering_service_account>
// Fallback log search where Live Process Monitoring is not deployed on OT-locked hosts:
source:windows @evt.id:4688 ("python.exe" OR "powershell.exe" OR "psexec")
// time range: last 90 days
Windows Event IDs to collect from the RSLinx and engineering population: 4624 (successful logon with logon type), 4625 (failed logon), 4648 (explicit credential use), 4672 (special privileges assigned), 4688 (process creation with command line), 4697 and 7045 (service installed), 4104 (PowerShell script block), 4720 and 4732 (account created, added to privileged group), 1102 (audit log cleared).
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4624; StartTime=(Get-Date).AddDays(-90)} |
Where-Object { $_.Properties[8].Value -in @(3,10) } |
Select-Object TimeCreated, MachineName, @{n='User';e={$_.Properties[5].Value}}, @{n='LogonType';e={$_.Properties[8].Value}}, @{n='SourceIP';e={$_.Properties[18].Value}} |
Export-Csv -NoTypeInformation C:\hunt\control_network_logons.csv
Get-WinEvent -FilterHashtable @{LogName='System'; Id=7045; StartTime=(Get-Date).AddDays(-90)} |
Select-Object TimeCreated, MachineName, Message |
Export-Csv -NoTypeInformation C:\hunt\control_network_services.csv
OT Data Collection: Claroty xDome — Devices > All Devices > Advanced Filters: Device Type = Engineering Workstation and Server at Purdue Levels 2 and 3, Export CSV; for each host open the device profile Communications tab to confirm its peer set and the Insights tab for insecure-protocol and end-of-life findings. Alerts & Threats > All Alerts: Alert Category = Threat Alert with that population as Device Filters.
OT Data Collection: Dragos Platform — Communications Hub: source-zone = Level 4 AND dest-zone = Level 2 or Level 3 AND protocol IN [RDP, SSH, VNC, SMB] across the window, and pivot each cross-Purdue session to its asset record and event PCAP. On a confirmed session, open a Case, attach the event PCAP and the SSH-sessions Query Focused Dataset output, and follow the attached playbook.
OT Data Collection: Armis Centrix — enumerate the tenant's own alert types from in:alerts with no type clause before writing any alert-scoped query; Armis alert types are policy-derived and therefore tenant-specific, and a literal lifted from documentation is rejected as an invalid value rather than returning an empty set:
in:ipConnections
serverPort:44818,2222
endpointA:(device:(category:"Computers"))
OT Data Collection: Tenable OT — Inventory > All Assets filtered to AssetType IN (Eng, OtWorkstation, Server) for the launch-point population; Events for Project Upload and Project Download policy violations across the window.
YARA file-system scan of the control-network Windows population:
yara -r /opt/hunt/rules/rslinx_cip_hunt.yar C:\Users\ >> /opt/hunt/hits_ews_users.txt
yara -r /opt/hunt/rules/rslinx_cip_hunt.yar "C:\Program Files (x86)\Rockwell Software\" >> /opt/hunt/hits_rockwell.txt
Analysis Queries:
CrowdStrike Falcon LogScale (CQL) — rarity analysis on children of the RSLinx service. On a population of near-identical control-network builds, a process that RSLinx has spawned only once or twice across the estate is the analytic definition of anomalous:
#event_simpleName=/ProcessRollup2/
| ParentBaseFileName = /^(RSLINX\.EXE|RsvcHost\.exe|RSLinxNG\.exe)$/i
| groupBy([FileName, ParentBaseFileName], function=count(as=ExecCount), limit=100000)
| sort(ExecCount, order=asc, limit=50)
A child process of the RSLinx Classic service is a finding on discovery regardless of what it is. RSLinx is a communications broker and has no legitimate reason to spawn an interpreter, a shell, or a utility, so any result from the query above is investigated rather than triaged — and it is also the signal that would distinguish a memory-corruption outcome more serious than the documented crash.
Datadog Log Analytics — logon-source distribution across the control-network Windows population, which surfaces the host or subnet that should not be reaching it:
source:windows @evt.id:4624 @evt.LogonType:(3 OR 10)
// Analytics: Table view, group by host, @evt.SourceIP; time range: last 90 days
Datadog Audit Trail — account and access changes touching the control-network or OT service accounts during the window:
source:datadog @evt.name:"Access Management"
// Analytics: Table view, group by @usr.email, @action; time range: last 90 days
source:datadog @evt.name:"Organization Management"
// Analytics: Table view, group by @usr.email, @action; time range: last 90 days
Datadog Monitor definition:
Type: Log Alert
Query: source:windows @evt.id:4688 ("pycomm3" OR "cpppo" OR "plcscan" OR "cipscan")
Evaluation window: last 5 minutes
Alert condition: count > 0
Message: "ALERT: CIP-capable scanning or crafting tooling executed on a control-network host — possible staging against ICSA-26-244-01, immediate investigation required @ot-soc-pagerduty"
Prerequisites: Windows Security log with command-line process auditing (Event ID 4688 with command line) forwarded from every control-network Windows host
Create via: Monitors > New Monitor > Log Alert OR POST /api/v1/monitors
Windows Event Log PowerShell analysis — persistence and script review on the control-network population:
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TaskScheduler/Operational'; Id=106; StartTime=(Get-Date).AddDays(-90)} |
Select-Object TimeCreated, MachineName, Message |
Export-Csv -NoTypeInformation C:\hunt\control_network_tasks.csv
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-PowerShell/Operational'; Id=4104; StartTime=(Get-Date).AddDays(-90)} |
Where-Object { $_.Message -match 'Sockets|TcpClient|44818|EtherNet|ForwardClose' } |
Select-Object TimeCreated, MachineName, Message |
Export-Csv -NoTypeInformation C:\hunt\ps_cip_scriptblocks.csv
YARA memory scan for credential-access tooling on the control-network population, since the credentials that reach the RSLinx hosts are held there:
Get-Process | Where-Object { $_.ProcessName -match 'lsass|python|powershell|rundll32' } |
ForEach-Object { & yara C:\hunt\rules\rslinx_cip_hunt.yar $_.Id } >> C:\hunt\yara_cred_hits.txt
Process-memory scanning of LSASS requires SeDebugPrivilege on the scanning account. Where a control-network host cannot be reached interactively, Falcon Real Time Response can put the YARA binary and rule file and run the scan remotely under the same privilege constraint.
Nation-state ICS-capable actors are the most consequential threat here and the least likely to use these vulnerabilities as an opening move. Groups with demonstrated Rockwell and EtherNet/IP capability treat the communications layer as infrastructure worth understanding and controlling rather than crashing, and a denial-of-service primitive is more valuable to them held in reserve as a component of a timed disruptive operation than spent on reconnaissance. Their access path is multi-stage: enterprise compromise, credential theft, lateral movement into the control network, and only then CIP-directed traffic from a host that legitimately speaks it. Sophistication is high and operational security is deliberate. For this actor class the detectable stages are the enumeration in Hypothesis 3 and the lateral movement in Hypothesis 4; the service crash would be the last signal and the least recoverable one.
Hacktivist and ideologically motivated actors are the most likely to attempt these vulnerabilities soonest and the least discriminating about where. The pattern established across 2023 through 2026 — unsophisticated groups locating internet-exposed control devices through public scan indices and manipulating them for publicity — maps directly onto this advisory, which requires no credentials, no foothold, and no tooling beyond a crafted packet. Sophistication is low but targeting is broad and index-driven, which is exactly why Hypothesis 1 runs first: for this actor class internet exposure is not one risk factor among several, it is the entire access path. The particular hazard here is that a crashed RSLinx service produces a visible, photographable operational effect — blank HMI screens — which is precisely the outcome a publicity-motivated actor is seeking.
Ransomware operators and financially motivated intrusion sets are relevant through a different mechanism. They rarely target the communications layer deliberately, but an intrusion set already inside the enterprise that discovers CIP reachability to an RSLinx host gains a coercion lever that pairs with an IT-side encryption event: the ability to sever plant visibility on demand, repeatedly, without touching the control logic. Their access path is commodity — phishing, exposed remote access, an unpatched edge appliance — and their sophistication is moderate with well-resourced tooling. Hypothesis 4 is the leg of this hunt that addresses them.
Opportunistic actors and misconfigured tooling round out the profile, and for these four vulnerabilities the boundary between them is thin. A service that crashes on malformed input will crash for an unmodified vulnerability scanner, an asset discovery appliance with an aggressive CIP probe, or a researcher's fuzzing harness pointed at the wrong address range, none of which intended anything. That is a real operational risk and it is also a real analytical hazard: an outage with an innocent cause looks identical at the process layer to an outage with a hostile one, and only the source attribution distinguishes them. Insiders — contractors, integrators, engineers — already hold the CIP reachability every other actor must work to obtain, and their activity is separated from sanctioned work only by timing, authorization, and change-record correlation.
Network: packet capture or SPAN/TAP-fed capture at the switch ports facing each RSLinx Classic host and at the Level 3.5 boundary, covering TCP/UDP 44818 and UDP 2222; NetFlow or IPFIX from the industrial core and distribution switches; firewall permit and deny logs from the Level 3.5 boundary and every conduit into the control network; VPN and remote-access broker session logs; DNS query logs from the control-network resolvers.
Endpoint: CrowdStrike Falcon telemetry from every RSLinx Classic host, engineering workstation, FactoryTalk server, historian collector, and control-network jump host — specifically ProcessRollup2 and SyntheticProcessRollup2, EndOfProcess, NetworkListenIP4, NetworkReceiveAcceptIP4, NetworkConnectIP4, ServiceStarted, the FileWritten event family, UserLogon and UserLogonFailed2, and ScheduledTaskRegistered. Windows System, Application, Security, PowerShell/Operational, and TaskScheduler/Operational event logs with command-line process auditing and script block logging enabled. Windows Error Reporting crash dumps for every RSLinx Classic service failure, which are the only artifact that identifies the failing parsing path and which age out on a default retention that is shorter than this hunt's window.
OT and ICS: process historian tag history and alarm records covering communication loss, device offline, and data quality alarms for every device reached through an RSLinx Classic host; SCADA and HMI alarm and operator action logs; the software inventory for every control-network Windows host, including dependency-installed components; SNMP interface counters and trap logs from the switches facing the RSLinx hosts; OT monitoring platform exports from every deployed platform — Claroty xDome or CTD, Dragos Platform, Nozomi Guardian or Vantage, Armis Centrix, Tenable OT, and Forescout eyeInspect — covering asset inventory, directional flow records, protocol-decoded alerts, and service inventory by port.
Vendor and change: the RSLinx Classic version installed on every host and the date it was installed; the list of applications on each host that consume RSLinx, which determines the upgrade blast radius and is required before remediation can be sequenced; change management records and maintenance work orders for the entire window, which are the only source that separates a sanctioned restart from an unsanctioned crash; and the authorized CIP client list per RSLinx host, without which no source-based analysis in this plan can be dispositioned.
External attack surface: the passive internet-scan indices queried read-only — Shodan, Censys, and Netlas — accessed with credentials referenced by environment-variable name only; the CISA Known Exploited Vulnerabilities catalog; and the organization's authoritative public IP address inventory and cloud asset inventory. Without the last of these, an externally observed host cannot be attributed to the organization and the exposure determination cannot be made at all; an incomplete inventory bounds the sweep exactly where shadow infrastructure accumulates.
SIGMA rule 1 — unsanctioned CIP-capable tooling executed on a control-network host. This is the process-creation half of Hypotheses 3 and 4 and the highest-value standing detection this hunt produces, because CIP crafting tooling has no legitimate reason to run outside a small, known engineering population.
title: CIP EtherNet-IP Tooling Execution on Control Network Host
id: 2a6f1c94-7d38-4e05-b1a7-6c02de935f41
status: experimental
description: Detects execution of Python CIP libraries, PLC scanners, or packet-crafting tooling capable of generating the crafted EtherNet/IP CIP messages that crash RSLinx Classic per ICSA-26-244-01.
references:
- https://www.cisa.gov/news-events/ics-advisories/icsa-26-244-01
- https://nvd.nist.gov/vuln/detail/CVE-2026-9622
author: 1898 & Co.
date: 2026/09/03
logsource:
category: process_creation
product: windows
detection:
selection_tooling:
CommandLine|contains:
- 'pycomm3'
- 'cpppo'
- 'plcscan'
- 'cipscan'
- 'ForwardClose'
- 'forward_close'
selection_scanner:
Image|endswith:
- '\nmap.exe'
- '\python.exe'
CommandLine|contains:
- '44818'
- 'enip-info'
- 'enip-enumerate'
condition: selection_tooling or selection_scanner
falsepositives:
- Sanctioned OT asset discovery tooling operated by the OT security team during an approved assessment window
- Vendor commissioning activity performed by a Rockwell integrator with a change record
level: high
tags:
- attack.discovery
- attack.t1046
- attack.impact
- attack.t1499.004
SIGMA rule 2 — inbound CIP connection to an RSLinx Classic host from a source outside the authorized client set. This uses the network-connection logsource and catches a compiled or renamed tool whose command line rule 1 would never see.
title: Inbound EtherNet-IP CIP Connection to RSLinx Classic Host From Unauthorized Source
id: 5e7b3a20-9f14-4c68-8d35-b0a71ce4f902
status: experimental
description: Detects an inbound connection to EtherNet/IP CIP ports 44818 or 2222 accepted by the RSLinx Classic service from a source outside the enumerated authorized client set, which is the delivery path for all four ICSA-26-244-01 vulnerabilities.
references:
- https://www.cisa.gov/news-events/ics-advisories/icsa-26-244-01
author: 1898 & Co.
date: 2026/09/03
logsource:
category: network_connection
product: windows
detection:
selection:
DestinationPort:
- 44818
- 2222
Initiated: 'false'
filter_authorized:
SourceIp|cidr:
- '10.20.30.0/24'
condition: selection and not filter_authorized
falsepositives:
- Authorized CIP clients absent from the filter CIDR because the authorized client list was not updated after a subnet change
- OT asset discovery appliances performing scheduled inventory against the RSLinx host
level: high
tags:
- attack.initial_access
- attack.t1190
- attack.impact
- attack.t1499.004
The authorized-source CIDR in rule 2 is illustrative and must be replaced with the site's own enumerated CIP client ranges before deployment. Where the authorized set is a small number of individual addresses rather than a range, express them as discrete SourceIp values instead of a CIDR — the rule is only as good as the client enumeration behind it, and a filter that is too broad silently disables the detection.
SIGMA rule 3 — RSLinx Classic service terminated unexpectedly. This uses the Windows System log service and is the terminal indicator for all four CVEs. It is deliberately written at medium level because it will fire on genuine software faults; its value is that it forces every crash into a triage workflow where the preceding CIP traffic is reviewed, which is the step this hunt exists to institutionalize.
title: RSLinx Classic Service Terminated Unexpectedly
id: c8104fb7-3e29-45a6-9b70-1d5fa2e60c83
status: experimental
description: Detects unexpected termination of the RSLinx Classic service, the terminal indicator for CVE-2026-9621, CVE-2026-9622, CVE-2026-9624 and CVE-2026-9625. Every occurrence requires review of the inbound CIP traffic in the preceding sixty seconds before it is dispositioned as a software fault.
references:
- https://www.cisa.gov/news-events/ics-advisories/icsa-26-244-01
author: 1898 & Co.
date: 2026/09/03
logsource:
product: windows
service: system
detection:
selection:
Provider_Name: 'Service Control Manager'
EventID:
- 7031
- 7034
param1|contains: 'RSLinx'
condition: selection
falsepositives:
- Genuine RSLinx Classic software faults unrelated to crafted input
- Service termination during a sanctioned upgrade or host maintenance window
level: medium
tags:
- attack.impact
- attack.t1499.004
- attack.t1489
SIGMA rule 4 — RSLinx Classic application crash with faulting module detail. This uses the Windows Application log and complements rule 3 by capturing the faulting module and offset, which is what allows a population of crashes to be grouped and separated into software-fault and exploit clusters.
title: RSLinx Classic Application Error With Faulting Module
id: f0392b6d-4a17-49ce-85b2-7ad6c1e30845
status: experimental
description: Detects a Windows Application Error for the RSLinx Classic executable, recording the faulting module and offset needed to group crashes and separate reproducible software faults from reproducible exploitation of ICSA-26-244-01.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-9625
author: 1898 & Co.
date: 2026/09/03
logsource:
product: windows
service: application
detection:
selection:
Provider_Name: 'Application Error'
EventID: 1000
Data|contains:
- 'RSLINX'
- 'RsvcHost'
condition: selection
falsepositives:
- Genuine application faults on a host with an unrelated stability problem
level: medium
tags:
- attack.impact
- attack.t1499.004
Snort/Suricata rule 1 — oversized EtherNet/IP encapsulation toward an RSLinx Classic host. This is the closest network-observable proxy for the CVE-2026-9625 embedded-message overflow and the CVE-2026-9624 length-validation defect. It is written as a detection_filter so a single anomalous frame from a misbehaving legitimate client does not page the SOC while a sustained attempt does. Local SIDs start at 1,000,000 per the Suricata local range.
alert tcp any any -> $RSLINX_HOSTS 44818 (msg:"OT ENIP oversized encapsulation toward RSLinx Classic - possible ICSA-26-244-01 exploitation attempt"; flow:to_server,established; dsize:>700; content:"|6f 00|"; offset:0; depth:2; detection_filter:track by_src, count 3, seconds 60; classtype:attempted-dos; reference:cve,2026-9625; reference:cve,2026-9624; reference:url,www.cisa.gov/news-events/ics-advisories/icsa-26-244-01; metadata:service enip; sid:1000011; rev:1;)
Snort/Suricata rule 2 — anomalous CIP Forward Close rate toward an RSLinx Classic host. This is the CVE-2026-9622 axis. Forward Close (CIP service 0x4E on the Connection Manager object) is exercised during every normal connection teardown, so the rule is a rate detection rather than a content signature, and its threshold must be tuned against the site's own baseline before deployment, or it will generate enough noise that an operator disables it.
alert tcp any any -> $RSLINX_HOSTS 44818 (msg:"OT CIP Forward Close rate anomaly toward RSLinx Classic - possible CVE-2026-9622 exploitation attempt"; flow:to_server,established; content:"|4e|"; detection_filter:track by_src, count 50, seconds 60; classtype:attempted-dos; reference:cve,2026-9622; metadata:service enip; sid:1000012; rev:1;)
Snort/Suricata rule 3 — inbound CIP to an RSLinx Classic host from outside the authorized client set. This is a segmentation-assurance rule rather than an exploit signature; it converts the CIP conduit policy into a detection so that a policy failure becomes visible rather than silent.
alert tcp !$CIP_CLIENTS any -> $RSLINX_HOSTS 44818 (msg:"OT CIP explicit messaging to RSLinx Classic from unauthorized source - conduit policy violation"; flow:to_server,established; threshold:type limit, track by_src, count 1, seconds 300; classtype:policy-violation; reference:cve,2026-9621; metadata:service enip; sid:1000013; rev:1;)
YARA rule 1 targets the on-disk staging artifacts of CIP exploitation tooling. It is structured around three independent branches — Python CIP library imports, EtherNet/IP and CIP protocol constants written as source-level literals, and known scanner strings — because an actor may stage any one of them without the others, and requiring all three would produce a rule that only matches a complete toolkit. The condition demands two matches from any branch, or one branch match paired with the CIP port literal, which suppresses the common false positive of a documentation file or package manifest that merely names a library. The Forward Close and Connection Manager strings are included specifically because CVE-2026-9622 is reached through that service and tooling built for it will name it.
rule CIP_Exploitation_Tooling_Artifacts
{
meta:
description = "CIP/EtherNet-IP exploitation and enumeration tooling staged on a control-network host; supports the ICSA-26-244-01 RSLinx Classic hunt"
author = "1898 & Co."
date = "2026-09-03"
reference = "https://www.cisa.gov/news-events/ics-advisories/icsa-26-244-01"
strings:
$lib1 = "pycomm3" ascii wide nocase // Python CIP client library import or package path
$lib2 = "from cpppo" ascii nocase // cpppo EtherNet/IP library import form
$lib3 = "cpppo.server.enip" ascii nocase // cpppo ENIP module path
$lib4 = "LogixDriver" ascii nocase // pycomm3 driver class
$cip1 = "ForwardClose" ascii wide nocase // CIP Connection Manager service targeted by CVE-2026-9622
$cip2 = "ForwardOpen" ascii wide nocase // its counterpart, present in any connection-manager tooling
$cip3 = "UnconnectedSend" ascii wide nocase // CIP embedded-message service exercised by CVE-2026-9625
$cip4 = "ConnectionManager" ascii wide nocase // CIP object class 0x06 by name
$enc1 = "ListIdentity" ascii wide nocase // ENIP encapsulation command 0x0063 by name
$enc2 = "RegisterSession" ascii wide nocase // ENIP encapsulation command 0x0065 by name
$enc3 = "SendRRData" ascii wide nocase // ENIP encapsulation command 0x006f by name
$enc4 = { 63 00 00 00 00 00 00 00 } // ListIdentity header bytes as a literal in a crafted-packet script
$scan1 = "plcscan" ascii wide nocase // known PLC enumeration tool
$scan2 = "enip-enumerate" ascii nocase // nmap ENIP NSE script name
$scan3 = "enip-info" ascii nocase // nmap ENIP NSE script name
$port = "44818" ascii wide // CIP explicit messaging port as a literal
condition:
2 of ($lib*, $cip*, $enc*, $scan*) or
($port and 1 of ($lib*, $cip*, $scan*))
}
YARA rule 2 targets the same activity in process memory rather than on disk, which is the only place it appears when the tooling is run from an interpreter, loaded in memory, or deleted after execution. The condition is deliberately looser than rule 1's because a live process holds far fewer complete artifacts than a staged file, and it anchors on the co-occurrence of a protocol constant with a socket or port artifact so that a process merely holding the string 44818 in a log buffer does not match. It is written to be run against a suspect client process and against the RSLinx service process itself, where an in-memory protocol constant alongside socket artifacts is expected and therefore requires the analyst to compare against a known-good host rather than treat a hit as conclusive on its own.
rule RSLinx_CIP_Crafting_In_Memory
{
meta:
description = "In-memory artifacts of EtherNet/IP CIP message crafting on a control-network host; run against live process IDs"
author = "1898 & Co."
date = "2026-09-03"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2026-9621"
strings:
$cmd1 = { 6f 00 } // ENIP SendRRData encapsulation command little-endian
$cmd2 = { 65 00 } // ENIP RegisterSession encapsulation command little-endian
$cmd3 = { 63 00 } // ENIP ListIdentity encapsulation command little-endian
$s1 = "ForwardClose" ascii wide nocase // Connection Manager service name held by a crafting library
$s2 = "UnconnectedSend" ascii wide nocase // embedded-message service name
$s3 = "RegisterSession" ascii wide nocase // session establishment routine name
$s4 = "pycomm3" ascii wide nocase // interpreter-resident library name
$s5 = "cpppo" ascii wide nocase // interpreter-resident library name
$sock1 = "socket.SOCK_STREAM" ascii nocase // raw socket construction in an interpreted script
$sock2 = "System.Net.Sockets" ascii wide // .NET socket construction from PowerShell
$port = "44818" ascii wide // CIP explicit messaging port
condition:
(1 of ($s*) and 1 of ($sock*, $port)) or
(2 of ($cmd*) and 1 of ($sock*, $port))
}
YARA rule 3 is the standing credential-access rule, included because Hypothesis 4 involves lateral movement onto control-network Windows hosts and credential access on them. Its five branches cover the tools that actually appear on Windows hosts in industrial environments, and the fifth is a catch-all pairing a memory-read API with an LSASS reference so a renamed or novel tool is still caught by behaviour rather than by name. Scanning LSASS memory requires SeDebugPrivilege on the scanning account; where a control-network host cannot be reached interactively, Falcon Real Time Response can put the YARA binary and rule file and run the scan remotely.
rule Credential_Dump_Tool_Memory_Artifacts
{
meta:
description = "Credential dumping tool artifacts in process memory on control-network Windows hosts with CIP reachability"
author = "1898 & Co."
date = "2026-09-03"
reference = "https://attack.mitre.org/techniques/T1003/"
strings:
$mim1 = "sekurlsa::logonpasswords" ascii wide nocase // mimikatz credential extraction command
$mim2 = "lsadump::sam" ascii wide nocase // mimikatz SAM dump command
$mim3 = "privilege::debug" ascii wide nocase // mimikatz privilege escalation command
$mim4 = "mimikatz" ascii wide nocase // tool name in memory
$mim5 = { 6d 69 6d 69 6b 61 74 7a } // "mimikatz" as literal hex, survives simple renaming
$wce1 = "wce.exe" ascii wide nocase // Windows Credential Editor binary name
$gsec = "gsecdump" ascii wide nocase // gsecdump tool name
$com1 = "MiniDump" ascii wide nocase // comsvcs.dll MiniDump export
$com2 = "comsvcs" ascii wide nocase // comsvcs.dll LOLBIN reference
$api1 = "NtReadVirtualMemory" ascii wide // memory-read API used by dumpers
$api2 = "ReadProcessMemory" ascii wide // memory-read API used by dumpers
$lsass = "lsass.exe" ascii wide nocase // the target process
condition:
any of ($mim*) or
($wce1 and $lsass) or
$gsec or
($com1 and $com2 and $lsass) or
(1 of ($api*) and $lsass and 1 of ($mim*, $wce1, $gsec, $com2))
}
All three rules are distributed as a single file, rules/rslinx_cip_hunt.yar, which is the path referenced by every YARA invocation in Section 2.
Network indicators. Inbound CIP traffic on TCP/UDP 44818 or UDP 2222 to an RSLinx Classic host from any source outside the enumerated authorized client set is an indicator by construction, regardless of content. Within authorized traffic, the indicators are an EtherNet/IP encapsulation length field inconsistent with the actual frame payload; an EtherNet/IP frame materially larger than the baseline distribution, particularly one carrying an embedded message request; a CIP Forward Close request rate from one source that is inconsistent with its connection establishment rate, which is the shape of Forward Close abuse rather than Forward Close use; a session establishment rate to one RSLinx host exceeding the sanctioned client baseline by an order of magnitude; TCP resets or retransmission storms on port 44818 immediately preceding a service crash; and any CIP session originating from a guest, unmanaged, or unclassified segment or from an external address.
Host indicators. Unexpected termination of the RSLinx Classic service (Windows Event ID 7031 or 7034) with no corresponding change record — the terminal indicator for all four CVEs. Repeated RSLinx service starts within a short window, indicating a crash loop rather than a single fault. A population of RSLinx Application Error records (Event ID 1000) sharing one faulting module and offset. Any child process of the RSLinx Classic service, which has no legitimate cause. Execution on a control-network Windows host of a Python interpreter importing pycomm3 or cpppo, of plcscan or a comparable enumeration tool, of nmap with the EtherNet/IP NSE scripts, or of a PowerShell script block constructing a raw socket toward port 44818 or naming ForwardClose. Files matching the CIP tooling YARA rule written outside the Rockwell Software installation tree. A network or interactive logon to an RSLinx or engineering host from outside the authorized administrative source set. Credential-access artifacts in the memory of any process on a host with CIP reachability. Audit log clearing (Event ID 1102) on any control-network host.
Operational indicators. Simultaneous loss of communications or transition to bad data quality across every device reached through a single RSLinx Classic instance — the process-side signature of a CIP service crash, and one that no individual device fault reproduces. Blank or stale HMI graphics with the underlying process continuing to run normally. A gap in historian collection bounded at both ends by a service restart. Repeated short visibility outages across a shift with no equipment cause identified, which is the pattern a sustained denial-of-view attack produces and the one most likely to have been recorded as software instability rather than investigated.
External exposure indicators. An organization-attributed host answering on TCP/UDP 44818 or UDP 2222 in any passive index, corroborated in at least two engines and confirmed organization-owned by at least two independent ownership signals. A Rockwell or FactoryTalk surface reachable from the internet. An organization-owned certificate or hostname presented by a host absent from the authoritative public IP inventory, which is a shadow-infrastructure finding regardless of what service it runs. Continuous exposure of any of the above spanning 1 September 2026, which means the host was presumptively enumerated following the advisory's publication.
Genuine RSLinx Classic software faults are the dominant false positive and the reason Section 5's service-crash rule is written at medium level rather than high. RSLinx Classic is mature software carrying long-lived compatibility code, and it crashes for ordinary reasons: memory exhaustion on a long-running host, driver conflicts, resource contention with a co-located historian or visualization server, and faults triggered by a legitimate but unusual client request. Suppress by triage rather than by allowlist — every crash enters the workflow and is dispositioned against its preceding CIP traffic and its faulting-module signature, because a rule that suppresses crashes by default suppresses exactly the event this hunt exists to catch.
Sanctioned CIP client activity is continuous and high-volume. FactoryTalk View SE and ME, Studio 5000 Logix Designer, RSLogix 5000, FactoryTalk Historian and its collectors, FactoryTalk Transaction Manager, third-party OPC servers, MES gateways, and reporting systems all speak CIP to RSLinx Classic constantly and legitimately, and several of them establish and tear down connections at a rate that will trip an untuned Forward Close threshold. Build the authorized client list per RSLinx host before any source-based analysis is read, record the normal session and Forward Close rate for each client, and tune the Section 5 rate thresholds against that baseline rather than against a generic figure.
OT asset discovery and vulnerability management tooling generates CIP connection fan-out by design, and in this case can generate the outage as well. Claroty, Dragos, Nozomi, Armis, Tenable OT, and Forescout all perform some form of active or semi-active enumeration depending on configuration, and Tenable OT's active queries and Forescout's ICS Patrol are built to interrogate devices directly. Enumerate the collector and sensor addresses for every deployed platform, confirm with each platform owner which active features are enabled, and record them before reading any fan-out or fan-in result. An unattributed scan is frequently the organization's own tooling on an address nobody documented.
Enterprise IT scanning and monitoring routinely touches OT-registered ports without OT intent. A vulnerability scanner whose scope was widened by an inaccurate asset list will sweep 44818 across a control-network range, and against these four vulnerabilities that scan may itself crash the service. Confirm any suspected scan against the IT scanning schedule and scope, and treat the finding as a segmentation defect in its own right regardless of intent — the scanner should not have had the reachability.
Maintenance and upgrade activity produces the full signature of this attack. A sanctioned RSLinx upgrade, a host patch cycle, a server reboot, or a controlled failover produces service stops, restarts, simultaneous device communication loss, and historian gaps, and does so legitimately. Correlate every candidate event against the change management record and the maintenance calendar as the first triage step, and treat the absence of a change record as the discriminator rather than the presence of the operational symptom.
Passive-index misattribution is the false positive specific to Hypothesis 1 and the most consequential to get wrong. Shared hosting, CDN edge nodes, and carrier infrastructure will present hostnames or certificates in the organization's namespace on hardware it does not control, and a genuinely owned cloud asset will present provider-owned reverse DNS and ASN that hide ownership entirely. Require at least two independent ownership signals before reporting any hit as organizational exposure, treat an unattributable hit as inconclusive rather than negative, and apply the honeypot guardrail — an EtherNet/IP port answering on the open internet is as likely to be a Conpot instance as a real RSLinx host, and reporting a honeypot as client exposure is the most avoidable failure this hunt can produce.
1. Any organization-attributed host confirmed in at least two passive indices as answering on TCP/UDP 44818 or UDP 2222, whether or not a version is readable, is escalated to incident response and to the network team for exposure removal within the same business day.
2. Any organization-attributed internet-facing Rockwell Automation or RSLinx surface whose observed exposure window spans 1 September 2026 is escalated as presumptively enumerated, and the host and everything reachable behind it are hunted exhaustively rather than by sampling.
3. Any externally observed host presenting an organization-owned certificate or hostname that is absent from the authoritative public IP or cloud asset inventory is escalated as a shadow-infrastructure finding requiring ownership resolution, independent of its patch state.
4. Any unexpected RSLinx Classic service termination during the window that is preceded within sixty seconds by inbound CIP from a source outside the authorized client set is escalated to incident response immediately, and the host is preserved for evidence capture.
5. Any unexpected RSLinx Classic service termination with no corresponding change record, maintenance work order, or identified software cause is escalated for investigation even where the preceding traffic record is incomplete, and the missing traffic record is itself recorded as a visibility gap.
6. Any RSLinx Classic crash loop — more than three service starts on one host within an hour — is escalated immediately as a sustained denial-of-view condition regardless of the apparent cause.
7. Any CIP traffic on 44818 or 2222 reaching an RSLinx Classic host from an external address, from a guest or unmanaged segment, or from any source outside the enumerated authorized client set is escalated as both a security finding and a segmentation control failure.
8. Any EtherNet/IP frame captured with an encapsulation length field inconsistent with its actual payload, or carrying an oversized embedded message request, is escalated immediately and the capture preserved, as this is the closest available observation of the trigger condition for CVE-2026-9624 and CVE-2026-9625.
9. Any CIP Forward Close request rate from a single source that is inconsistent with that source's connection establishment rate is escalated as a candidate CVE-2026-9622 exploitation attempt, with the packet capture for the interval preserved.
10. Any CIP connection fan-out to more than ten distinct addresses from a single source that cannot be attributed to a documented OT monitoring collector or an approved assessment is escalated as enumeration ahead of exploitation.
11. Any child process spawned by the RSLinx Classic service is escalated to incident response immediately and the host is isolated pending triage, as RSLinx has no legitimate reason to spawn a process and this is the signal that would distinguish an outcome more serious than the documented crash.
12. Any hit on the YARA rule CIP_Exploitation_Tooling_Artifacts against any control-network host filesystem is escalated to incident response, the host is isolated pending triage, and the staged file is acquired for analysis before any remediation.
13. Any hit on the YARA rule RSLinx_CIP_Crafting_In_Memory against a live process on a control-network host, other than a confirmed baseline match on the RSLinx service process itself, is escalated immediately, the process is captured with a memory image before termination, and the host is treated as compromised until proven otherwise.
14. Any hit on the YARA rule Credential_Dump_Tool_Memory_Artifacts against any process on a host with CIP reachability is escalated to incident response as a credential-access event, and every account that has authenticated to that host is treated as compromised and rotated.
15. Any interactive or network logon to an RSLinx Classic host from outside the authorized administrative source set is escalated as unauthorized access to a host in the CIP conduit.
16. Any evidence of audit log clearing, monitor disablement, crash dump deletion, or log-forwarding interruption on a control-network host or at the OT boundary during the window is escalated as anti-forensic activity, and the hunt window expands to cover the full period of lost visibility.
The hunt is complete when all four hypotheses have been executed and dispositioned. Hypothesis 1 is complete when the organization's full public IP range set and cloud asset inventory have been swept in at least two passive indices, every hit has been attributed with at least two independent ownership signals or explicitly recorded as inconclusive, every attributed hit has been version-triaged with the index scan timestamp recorded, and the historical pivot across 1 September 2026 has been performed for each. Hypothesis 2 is complete when the RSLinx host population has been established from the listening-socket evidence rather than from license records, every host has been covered by the endpoint and Windows event queries across the window, and every RSLinx service termination in the window has been correlated against the preceding inbound CIP record and dispositioned as operational or suspicious. Hypothesis 3 is complete when the full window has been searched for CIP fan-out and fan-in from every available source — endpoint, boundary firewall, OT platform, and packet capture — and every enumeration event has been attributed to a documented collector, an approved assessment, or escalated. Hypothesis 4 is complete when every RSLinx and engineering host has been covered by the logon, process, and YARA queries, and every logon from outside the authorized administrative source set has been dispositioned.
A hypothesis is dispositioned Negative only where the query executed successfully against a populated data source across the full window. Where a data source was absent, a sensor did not cover a segment, a Windows event log had rolled before the window opened, a crash dump had aged out, a platform returned zero from a scoping clause that could not be validated, or a passive index could not sweep a range, the disposition is Non-Conclusive with the missing input named explicitly — never Negative. This distinction carries unusual weight for this advisory, because the completeness of the RSLinx host population is itself uncertain by construction: the product is installed as a dependency, and a host that was never enumerated cannot be cleared. State the population confidence explicitly in the report rather than implying completeness.
The hunt report must contain: the RSLinx Classic host inventory with installed version, discovery method, and affected-or-not determination for each, together with an explicit statement of how the population was bounded and what could not be enumerated; the list of applications consuming RSLinx on each host, which is the upgrade blast radius and the input remediation planning requires; the external attack surface result stated as engines queried, ranges swept, timestamps, and per-hit provenance, with coverage limitations named where anchors were incomplete; the disposition of every RSLinx service termination in the window with its supporting evidence and its faulting-module grouping; the per-hypothesis disposition with the data sources that carried it; every escalation raised and its outcome; the detection content produced, with a statement of which rules were deployed and which require a data source that is not currently forwarded; the false-positive dispositions and the authorized CIP client list built during the hunt, both of which are reusable; and a named remediation status per host — upgraded to 4.60, scheduled with a date, or accepted with a documented and dated mitigation plan naming its compensating controls, which is the form a NERC CIP-007-6 R2 auditor expects.
Three recommendations are standing outputs of this hunt regardless of its findings. First, the authorized CIP client list built during Hypothesis 2 should be retained and maintained as configuration, because every source-based detection in Section 5 depends on it and an unmaintained list silently disables them. Second, RSLinx Classic service crashes should be routed into the security triage workflow permanently rather than only for the duration of this hunt, since the entire premise of Hypothesis 2 is that these events have historically been dismissed as instability. Third, the passive external-exposure sweep should be established as a recurring control rather than a one-time activity, because exposure is created by change and a clean result today constrains nothing about next quarter.
| IOC Type | IOC |
|---|---|
| CVE | CVE-2026-9621 | CVSS v3.1 8.6 / CVSS v4.0 9.2 | Rockwell Automation RSLinx Classic 4.50 and earlier | Integer overflow or wraparound (CWE-190); a crafted CIP packet crashes the RSLinx Classic service and requires a restart; CVSS v3.1 scope is Changed and CVSS v4.0 subsequent-system availability is High; fixed in 4.60; no workaround. |
| CVE | CVE-2026-9622 | CVSS v3.1 8.6 / CVSS v4.0 9.2 | Rockwell Automation RSLinx Classic 4.50 and earlier | Integer underflow (CWE-191) reached through the CIP Forward Close service (service code 0x4E, Connection Manager class 0x06); a crafted Forward Close packet crashes the service; fixed in 4.60; no workaround. |
| CVE | CVE-2026-9624 | CVSS v3.1 7.5 / CVSS v4.0 8.7 | Rockwell Automation RSLinx Classic 4.50 and earlier | Integer underflow (CWE-191) from insufficient data length validation during CIP packet parsing; a crafted packet crashes the service; fixed in 4.60; no workaround. |
| CVE | CVE-2026-9625 | CVSS v3.1 7.5 / CVSS v4.0 8.7 | Rockwell Automation RSLinx Classic 4.50 and earlier | Classic buffer overflow (CWE-120) triggered by a crafted CIP packet carrying an oversized embedded message request; crashes the service; fixed in 4.60; no workaround. |
| Threat Actor | None attributed in source material — CISA reports no known public exploitation specifically targeting these vulnerabilities; monitor https://www.cisa.gov/known-exploited-vulnerabilities-catalog for KEV addition |
| Malware | None published in source material — no malware family, implant, or tool is associated with ICSA-26-244-01 at time of writing |
| Network IOC | None published in source material — no IP addresses, domains, or C2 infrastructure have been released; monitor https://www.cisa.gov/news-events/ics-advisories/icsa-26-244-01 for advisory updates |
| File IOC | None published in source material — no file hashes or filenames have been released; monitor https://www.cisa.gov/news-events/cybersecurity-advisories for follow-on technical advisories |
| Behavioral | Inbound CIP traffic on TCP/UDP 44818 or UDP 2222 to an RSLinx Classic host from a source outside the enumerated authorized client set |
| Behavioral | Unexpected RSLinx Classic service termination — Windows Event ID 7031 or 7034 naming RSLinx — with no corresponding change record or maintenance work order |
| Behavioral | More than three RSLinx Classic service starts on one host within a one-hour window, indicating a crash loop rather than a single software fault |
| Behavioral | Windows Application Error (Event ID 1000) for RSLINX.EXE or RsvcHost.exe, with a population of such crashes sharing one faulting module and offset |
| Behavioral | Any child process spawned by the RSLinx Classic service — RSLinx is a communications broker and has no legitimate cause to spawn a process |
| Behavioral | EtherNet/IP encapsulation length field inconsistent with the actual frame payload, or a frame materially outside the baseline length distribution, on TCP 44818 |
| Behavioral | EtherNet/IP frame carrying an oversized embedded message request (CIP UnconnectedSend) toward an RSLinx Classic host |
| Behavioral | CIP Forward Close (service code 0x4E) request rate from a single source inconsistent with that source's connection establishment rate |
| Behavioral | CIP session establishment rate to a single RSLinx Classic host exceeding 100 accepts in a five-minute window from one source |
| Behavioral | CIP connection fan-out to more than ten distinct addresses from a single source, or EtherNet/IP List Identity (encapsulation command 0x0063) sweep across the control network |
| Behavioral | Simultaneous loss of communications or transition to bad data quality across every device reached through a single RSLinx Classic instance |
| Behavioral | Repeated short plant visibility outages across a shift with no identified equipment cause, historically recorded as software instability |
| Behavioral | Python interpreter importing pycomm3 or cpppo, or execution of plcscan / cipscan / nmap enip-info / enip-enumerate, on a control-network Windows host |
| Behavioral | PowerShell script block constructing a raw TCP or UDP socket referencing port 44818, ForwardClose, or ListIdentity |
| Behavioral | Network or interactive logon (Windows Event ID 4624, logon type 3 or 10) to an RSLinx Classic host from outside the authorized administrative source set |
| Behavioral | Organization-attributed host answering on TCP/UDP 44818 or UDP 2222 in two or more passive internet-scan indices, with exposure spanning 1 September 2026 |
| Behavioral | Audit log clearing (Windows Event ID 1102), monitor disablement, crash dump deletion, or log-forwarding interruption on a control-network host during the hunt window |