Skip to content

Threat Hunt Plan: Siemens Industrial Edge "Node2Shell" Brotli Decoder Compromise — Exploitation and Post-Exploitation Hunt

Date: 2026-11-03 | Revision 1.0

Hunt Objective and Scope

This hunt determines whether a remote, unauthenticated attacker has reached, exploited, or pivoted from a Siemens Industrial Edge Management Application service by abusing the "Node2Shell" out-of-bounds write in the bundled Brotli decoder (CVE-2026-111497, CVSS v3.1 9.8 / v4.0 9.2, CWE-787). In scope: all hosts running Industrial Edge Management Applications and their configurators (Cloud Connector Configurator, Common Configurator on the Industrial Edge Management instance and on the edge device, Databus Configurator, Edge OPC UA Server Configurator, Flow Creator Configurator, and Data Flow Monitoring), the network paths that deliver compressed input to those services, and any OT segment reachable from a compromised edge host. Out of scope for exploitation telemetry (but in scope for exposure): Siemens-managed Industrial Edge Hub and Industrial Edge Management Cloud, which Siemens remediated on the central service on 2026-10-28/29 and front with Firewall/WAF; installations without Management Applications and the Industrial Edge Device Kit are not affected because the Brotli library is present but neither used nor exposed.

Time window: 2026-09-28 (approximately 30 days before Siemens' 2026-10-30 publication, to capture pre-disclosure reconnaissance and any opportunistic probing of the open-source Brotli flaw) through the present. The window starts before disclosure deliberately; adjust the start earlier if local threat intelligence indicates earlier interest in the Brotli decoder.

Data floor pre-flight (mandatory before any leg runs): a time picker is not retention. Measure the oldest retained event per family and record the floor as a DATE, never a duration:

#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2|NetworkReceiveAcceptIP4|NetworkConnectIP4|DnsRequest)$/
| groupBy([#event_simpleName], function=min(@timestamp, as=oldest), limit=max)

 

Any part of the window earlier than the measured floor is Non-Conclusive, not clean. Event-based clauses below (inbound connection bursts, process restarts, child-process spawns, outbound C2) are bounded by the floor. State-based clauses — an internet-exposed instance that exists now, a dropped payload still on disk, a configurator child process still running, a modified autostart entry — can be re-queried as current state and are not bounded by the floor; each hypothesis states which kind it is.

Hypotheses and Hunt Procedures

Hypothesis 1: An external actor can reach an internet-facing Siemens Industrial Edge Management instance or configurator web interface, observable as an exposed service in passive internet-scan indices. Execute FIRST; its output scopes the internal hypotheses. This is a state-based hypothesis (current exposure), not bounded by the data floor.

MITRE ATT&CK: Reconnaissance | T1595 — Active Scanning | defender-side passive discovery of what an actor would enumerate before targeting the IEM web surface. Initial Access | T1133 — External Remote Services | an internet-reachable IEM instance is a direct entry path. ICS | T0883 — Internet Accessible Device | an edge-management node bridging to OT must not be internet-reachable.

Collection Queries — passive external indices (STRICTLY passive; never probe target infrastructure). Author unscoped first (proves the filter matches the product), then scope to owned space. API keys are referenced by env-var name only (SHODAN_API_KEY, CENSYS_API_SECRET, CENSYS_ORG_ID, NETLAS_API_KEY) — never inline a key.

Shodan:
http.title:"Industrial Edge Management"

 

http.html:"Industrial Edge"

 

http.favicon.hash:<self-derived-hash>
// Derive the favicon hash from an instance you own, passively, by reusing an index-computed hash — never by fetching /favicon.ico from a target.
// Scope (apply ONE owned anchor per query — never AND two anchors): net:<your_public_CIDR> OR org:"<Your Org Name>" OR asn:AS<your_ASN>

 

Censys (CenQL — current host.services form; do NOT cross with legacy services.* CSL):
host.services.software.product: "Industrial Edge"

 

host.services: (port = "443" and software.product: "Industrial Edge")
// Scope: and (host.autonomous_system.asn = <your_ASN> or host.ip: "<your_public_CIDR>")

 

Netlas:
http.title:"Industrial Edge Management"

 

host:<your_public_CIDR> AND port:(443 OR 9443 OR 8443)

 

→ Cross-reference every exposed service and version banner against the CISA Known Exploited Vulnerabilities catalog (https://www.cisa.gov/known-exploited-vulnerabilities-catalog) and CVE-2026-111497; a KEV match is presumptive actor-associated initial-access risk. Rank by ransomware ratio, not entry count.
→ Expected Positive shape: a host in owned IP space returning an Industrial Edge Management UI title or favicon on 443/9443/8443, attributable by ≥2 independent ownership signals (PTR, ASN, cert CN/SAN, WHOIS/RDAP, own cloud inventory).

 

Analysis Queries:

- Attribute each hit with ≥2 independent ownership signals before reporting; shared hosting and CDN break attribution both ways, so an unattributable hit is Non-Conclusive, not Negative.
- Version-triage each banner and record the index scan timestamp; a banner is stale in both directions, so an in-range banner is a prompt to verify current state and a fixed banner is not proof of safety if the scan predates the window.
- Corroborate in ≥2 of Shodan / Censys / Netlas before escalating; absence from one index only means that index did not observe the host.
- Historical pivot: establish whether the exposure spans the 2026-09-28→present window; continuous exposure across it means the instance was presumptively enumerated.
- Pivot: feed every confirmed-owned in-range instance into Hypotheses 2 and 3 as a priority target and hunt it exhaustively. Internet reachability of infrastructure serving an OT control environment is a finding in its own right under IEC 62443 and NERC CIP, independent of confirmed exploitation.

 

Control — unscoped filter proves the product matches before scoping: run each engine query WITHOUT the owned-space anchor first and confirm it returns Industrial Edge instances globally; only then add the owned-space scope. A scoped query that returns zero while the unscoped query also returns zero means the filter is wrong, not that exposure is absent.

Verdict criteria: Positive — ≥1 owned, in-range IEM/configurator instance confirmed exposed in ≥2 indices; Negative — no owned instance exposed AND the unscoped control returned Industrial Edge instances (filter proven) AND the owned public-IP/cloud inventory is complete; Non-Conclusive — hits unattributable, or the public-IP inventory is incomplete; missing authoritative public-IP and cloud-asset inventory prevents determining whether an exposed instance is owned.

Hypothesis 2: A remote, unauthenticated attacker has delivered a crafted Brotli-compressed stream to an Industrial Edge Management Application service that decompresses untrusted input, observable as anomalous inbound HTTP requests carrying brotli-encoded bodies to the configurator services, temporally correlated with configurator process crashes or restarts. Event-based (inbound connections, process restarts) — bounded by the data floor.

MITRE ATT&CK: Initial Access | T1190 — Exploit Public-Facing Application | the Brotli decoder in a public-facing IEM service is driven out of bounds by crafted compressed input. Impact | T0814 — Denial of Service | decoder-state corruption can crash the service.

Collection Queries:

CrowdStrike Falcon LogScale (CQL) — inbound sessions to configurator service ports (Industrial Edge runs predominantly on Linux/containers; use event_platform=Lin where the Falcon sensor is deployed on the host):

#event_simpleName=NetworkReceiveAcceptIP4 event_platform=Lin
| in(LocalPort, values=["443","9443","8443","9091"])
| groupBy([aid, ComputerName, RemoteAddressIP4, LocalPort], function=count(as=sessions), limit=max)
| sort(sessions, order=desc, limit=200)
→ Expected Positive shape: a burst of inbound sessions to a configurator port from a non-baseline or external RemoteAddressIP4, clustered in time.

 

CrowdStrike Falcon LogScale (CQL) — configurator process restarts (crash-loop signature of a decoder DoS):

#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ event_platform=Lin
| ImageFileName=/\/(cloudconnector|common-?configurator|databus|opcua|flowcreator|dataflow)[^\/]*$/i
| groupBy([aid, ComputerName, ImageFileName], function=count(as=starts), limit=max)
| starts > 3
→ Expected Positive shape: a configurator image starting repeatedly within minutes, indicating a crash-restart loop coincident with the inbound burst above.

 

BPF packet capture (VLAN-aware two-branch form is REQUIRED — repeat the whole primitive in each branch; an untagged filter matched 0 in real exports where 100% of frames were 802.1Q-tagged):

tcp​dump -i <iface>​ -w /captures/iem_%Y%m%d_%H%M%S.pcap -G 3600 -C 100 '(host <IEM_IP> and (tcp port 443 or tcp port 9443 or tcp port 8443)) or (vlan and host <IEM_IP> and (tcp port 443 or tcp port 9443 or tcp port 8443))'

 

Datadog — container logs for the Industrial Edge namespace (edge apps run as containers; source:kubernetes is the primary visibility where the Datadog Agent is deployed):

source:kubernetes @kubernetes.namespace_name:"industrial-edge" (@http.status_code:>=500 OR message:"brotli" OR message:"BrotliDecoderDecompress" OR message:"decompress")
// time range: 2026-09-28T00:00Z to current
// Analytics: Table view, group by @kubernetes.pod_name; a cluster of 5xx or decoder-error lines on a configurator pod is the collection signal.

 

Datadog Live Process Monitoring (Infrastructure > Processes — NOT a log source; requires process_config.process_collection.enabled: true):

command:sh user:root
// Fallback where Live Process Monitoring is unavailable: source:kubernetes @kubernetes.namespace_name:"industrial-edge" message:"/bin/sh"
// time range: 2026-09-28T00:00Z to current

 

Windows Event collection (Industrial Edge is predominantly Linux/container; include only where a Windows host runs or administers the Common Configurator — otherwise note this as not applicable):

- Event ID 4688 (process creation) — new child of the configurator service
- Event ID 5156 (Windows Filtering Platform permitted connection) — inbound to the configurator port
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4688; StartTime=(Get-Date '2026-09-28')} | Where-Object { $_.Message -match 'configurator|industrial.?edge' } | Export-Csv -NoTypeInformation C:\hunt\iem_4688.csv

 

SNMP polling — a decoder DoS can reboot the edge device; poll the device and the facing switch port and diff at 60-second intervals (treat any v1/v2c community access to an OT asset as a finding in its own right; prefer v3 authPriv):

snmpget -v2c -c <community> <edge_device_ip> SNMPv2-MIB::sysUpTime.0
snmpget -v2c -c <community> <switch_ip> IF-MIB::ifInErrors.<ifIndex> IF-MIB::ifOutErrors.<ifIndex> IF-MIB::ifHCInOctets.<ifIndex>
// Collect trap-receiver logs for the window; flag coldStart (1.3.6.1.6.3.1.1.5.1) and warmStart (1.3.6.1.6.3.1.1.5.2) from the edge device IP range — an unexpected restart is consistent with a decoder crash.

 

Analysis Queries:

CrowdStrike Falcon LogScale (CQL) — correlate the inbound burst with the restart loop by host and time:

#event_simpleName=/^(NetworkReceiveAcceptIP4|ProcessRollup2|SyntheticProcessRollup2)$/ event_platform=Lin
| case{ #event_simpleName=NetworkReceiveAcceptIP4 | in(LocalPort, values=["443","9443","8443","9091"]) ; #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | ImageFileName=/\/(cloudconnector|common-?configurator|databus|opcua|flowcreator|dataflow)[^\/]*$/i ; }
| groupBy([aid, ComputerName], function=[count(as=events), min(@timestamp, as=first), max(@timestamp, as=last)], limit=max)
→ Expected Positive shape: the same aid showing both an inbound session spike and a configurator restart cluster within a short interval.

 

Wireshark / tshark — inbound requests advertising a brotli-encoded body (works only where traffic is decrypted at a reverse proxy/WAF or a TLS keylog is available; IEM services are TLS on 443/9443, so plaintext capture sees the handshake only):

tsh​ark -r iem.pcap -Y 'http.request and http.content_encoding contains "br"' -T fields -e ip.src -e http.host -e http.request.method -e http.request.uri
// On HTTPS without decryption, substitute the WAF/reverse-proxy access log; filter request records where the request Content-Encoding header equals "br".

 

Datadog Log Analytics — 5xx rate per configurator pod over the window:

source:kubernetes @kubernetes.namespace_name:"industrial-edge" @http.status_code:>=500
// Use Timeseries view; group by @kubernetes.pod_name; time range 2026-09-28T00:00Z to current. A step-change in 5xx on one pod aligned to an inbound burst corroborates a decoder DoS.

 

Datadog Monitor (one per hypothesis):

Type: Log Alert
Query: source:kubernetes @kubernetes.namespace_name:"industrial-edge" (message:"BrotliDecoderDecompress" OR message:"brotli" OR @http.status_code:>=500)
Evaluation window: last 10 minutes
Alert condition: count > 20
Message: "ALERT: Industrial Edge configurator decoder-error / 5xx spike — possible Node2Shell exploitation; investigate inbound sessions and process restarts @soc-oncall"
Prerequisites: Datadog Agent on the edge hosts with Kubernetes log collection enabled for the industrial-edge namespace.

 

Control — event-family and field presence on the same window and scope: a zero inbound-session result is Negative only if the family and the port field are populated.

#event_simpleName=NetworkReceiveAcceptIP4 event_platform=Lin | LocalPort = /./ | count()
#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ event_platform=Lin | ImageFileName = /./ | count()

 

If either control is 0 (no Falcon Linux telemetry from the edge host, or the field is unpopulated), the leg is a coverage gap reported Non-Conclusive, never Negative.

Verdict criteria: Positive — an inbound brotli-encoded request or inbound session burst from a non-baseline source correlated with a configurator crash/restart or decoder-error log on the same host; Negative — no such correlation AND both controls non-zero AND decrypted request visibility (WAF log or keylog) was available; Non-Conclusive — no Falcon Linux telemetry, or HTTPS traffic undecrypted so request Content-Encoding was unobservable; missing decrypted-request visibility prevents determining whether a brotli-encoded body was delivered.

Hypothesis 3: An attacker who corrupted the Brotli decoder has achieved code execution on an Industrial Edge host and is establishing control or pivoting toward OT assets, observable as unexpected child processes under a configurator service, new executables/scripts written to edge application directories, and outbound connections from the edge host to external C2 or into OT segments. Mixed: child-process spawns and outbound connections are event-based (floor-bounded); a surviving dropped payload or a still-running implant is state-based (re-queryable now).

MITRE ATT&CK: Execution | T1059 — Command and Scripting Interpreter | a shell or interpreter spawned by a decoder-handling service indicates code execution. Command and Control | T1071 — Application Layer Protocol | an implant on the edge host beacons outbound. ICS | T0866 — Exploitation of Remote Services | the compromised edge node is used to reach OT services it bridges to.

Collection Queries:

CrowdStrike Falcon LogScale (CQL) — interpreter or network tool spawned by a configurator service:

#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ event_platform=Lin
| ParentBaseFileName=/^(cloudconnector|common-configurator|databus-configurator|opcua-configurator|flowcreator|dataflow-monitoring)/i
| ImageFileName=/\/(sh|bash|dash|ash|python3?|perl|ruby|nc|ncat|socat|curl|wget)$/i
| table([aid, ComputerName, ParentBaseFileName, ImageFileName, CommandLine], limit=100000)
→ Expected Positive shape: an interactive shell, scripting interpreter, or network tool whose parent is a configurator service process.

 

CrowdStrike Falcon LogScale (CQL) — new executables/scripts written under edge application paths (use the catch-all file-write families; FileWritten alone is invalid and returns 0):

#event_simpleName=/(NewExecutableWritten|NewScriptWritten|ElfFileWritten|PeFileWritten)$/ event_platform=Lin
| TargetFileName=/\/(var\/lib\/edge|opt\/edge|data\/|tmp\/)/i
| table([aid, ComputerName, #event_simpleName, TargetFileName], limit=100000)
→ Expected Positive shape: an ELF or script written into an edge working/temp directory outside a maintenance window.

 

YARA — file-system scan of edge application directories for dropped payload artifacts (classic YARA 4.5 recursive disk scan):

ya​ra -r rules/node2shell_edge.yar /var/lib/edge /opt/edge /tmp >​>​ /hunt/edge_file_hits.txt

 

Datadog — unexpected shell in the Industrial Edge namespace (Template DA):

source:kubernetes @kubernetes.namespace_name:"industrial-edge" (message:"/bin/sh" OR message:"/bin/bash" OR message:"socat" OR message:"ncat")
// Analytics: Table view, group by @kubernetes.pod_name; time range 2026-09-28T00:00Z to current.
command:python user:root
// Live Process Monitoring free-text / tag filter (Infrastructure > Processes); fallback log search above where Live Process Monitoring is not enabled.

 

OT Data Collection: Claroty xDome — Devices → All Devices → Advanced Filters: Manufacturer = Siemens AND Device Type matching the edge appliance; export the host list, then Network → Communication → Communication Analysis with Side A pinned to each edge host address and Side B spanning OT Purdue levels to enumerate flows crossing into the control environment (correlate each hit to its device by asset_id, never by hostname; Communication Analysis is capped at ~one month and can under-report, so reconcile before any volumetric claim).

OT Data Collection: Claroty CTD — on-prem; use the CTD baseline-deviation and PCAP export for edge-host conversations (CTD and xDome are different platforms with sharply different PCAP retention — do not collapse to generic "Claroty").

OT Data Collection: Dragos Platform — Communications Hub: source-zone = the edge/IT zone AND dest-zone = Level2/Level1 AND protocol IN [S7CommPlus, CIP, Modbus, OPC UA, RDP, SSH] over the window; pivot each cross-Purdue session from the edge host to its asset record and event PCAP; triage Notifications for Initial Access (T0866) and Lateral Movement tags with an Activity-Group flag.

OT Data Collection: Nozomi Guardian — run via the OpenAPI (GET /api/open/query/do?query=<N2QL>): links where sent.bytes or received.bytes between an edge-host node_id and an OT node, filtered by is_public and node_type; review Variable view for any process-variable excursion outside the learned band with no corresponding operator action during the window.

OT Data Collection: Tenable OT — Inventory → All Assets filter AssetType in (IndustrialGateway, OtServer, OtWorkstation) and vendor = Siemens to locate the edge hosts; Events → Event Category = Configuration Events (Change in state, Change in firmware version) across the window; export each matching event.

OT Data Collection: Armis Centrix — in:ipConnections to enumerate flows from the edge host into OT (scope by behavior, not hostname): in:ipConnections protocol:S7CommPlus,OPC,PROFINET,Modbus,RDP,SSH role:Client (analyst sets the time frame in the UI picker; omit timeFrame from the query body).

OT Data Collection: Forescout eyeInspect — GET /api/v1/alerts with l7_proto and the hunt start_timestamp/end_timestamp, group by event_type_names and src_ip filtered to the edge-host IP; retrieve the PCAP for each matching alert_id where sensor capture is enabled.

Analysis Queries:

CrowdStrike Falcon LogScale (CQL) — attribute outbound external connections to the owning process by reversing the join direction (NetworkConnectIP4 does not expose ImageFileName; drive the join from the long-lived ProcessRollup2 side):

#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ event_platform=Lin
| ImageFileName=/\/(cloudconnector|common-?configurator|databus|opcua|flowcreator|dataflow|sh|bash|python3?|socat|nc|ncat)[^\/]*$/i
| join(query={#event_simpleName=NetworkConnectIP4 event_platform=Lin | !cidr(RemoteAddressIP4, subnet=["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16"])}, field=[aid, TargetProcessId], key=[aid, ContextProcessId], include=[RemoteAddressIP4, RemotePort], limit=200000)
| groupBy([aid, ComputerName, ImageFileName, RemoteAddressIP4, RemotePort], function=count(as=conns), limit=max)
→ Expected Positive shape: a configurator or shell process making an outbound connection to a public IP (candidate C2 beacon).

 

YARA — process-memory scan of configurator and child PIDs (classic YARA 4.5; the PID is the positional target — there is no -p PID flag; CrowdStrike Falcon RTR can execute this on remote hosts):

ya​ra rules/node2shell_edge.yar <configurator_pid>​
→ Expected Positive shape: a configurator process whose memory holds reverse-shell, loader, or credential-access str​ings not present in a clean baseline.

 

Control — process-family presence on the same window/scope; a zero child-process or write result is Negative only if the family is populated:

#event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ event_platform=Lin | ImageFileName = /./ | count()

 

A zero whose control is 0 or unrun is Non-Conclusive, never Negative.

Verdict criteria: Positive — a shell/interpreter child of a configurator service, OR a dropped executable in an edge directory, OR an outbound connection from a configurator/shell process to a non-baseline public IP or into an OT segment; Negative — none of the above AND the process-family control is non-zero AND Falcon Linux telemetry covers the edge host; Non-Conclusive — no Falcon telemetry on the edge host, or OT-platform flow export unavailable; missing edge-host endpoint telemetry prevents determining whether code execution occurred.

Threat Actor Profile

No named threat actor, APT group, or malware family is attributed to CVE-2026-111497 in Siemens SSA-443410 or in the surrounding disclosure at the time of writing, and the advisory does not report observed exploitation against Industrial Edge. The realistic actor set is therefore framed by the vulnerability class rather than by attribution.

Opportunistic / commodity actors: a pre-authentication, remotely reachable memory-corruption flaw in a widely embedded open-source library (Brotli) is attractive for broad, untargeted scanning once a public exploit emerges. Access path is direct delivery of a crafted Brotli-compressed stream to any internet-reachable service that decompresses untrusted input. Sophistication varies; expect mass probing first, then reliable denial-of-service, with memory-disclosure and code-execution attempts following any public proof-of-concept.

Targeted / OT-focused actors: an edge-management node that bridges enterprise and control networks is a high-value pivot. Activity consistent with OT-capable intrusion sets (per vendor and government reporting on ICS threats generally — not an attribution of this CVE to any group) would use the edge host as a credential source and a lateral-movement springboard: authentications and flows originating FROM the appliance into OT segments, reconnaissance of control protocols, and staging rather than immediate disruption. Carry the standard attribution caveats — a vendor cluster name is not a government attribution, aliases are overlaps not identities, and hacktivist self-claims deserve the harshest skepticism. Update this section from the OT/ICS threat-actor and incident intelligence base if a group is later linked to Brotli/Node2Shell exploitation.

Data Sources Required

Network: full-packet capture or WAF/reverse-proxy decrypted access logs at the IEM boundary (the request Content-Encoding header is only visible decrypted), NetFlow, and firewall logs for the configurator service ports (443/9443/8443 and any product-specific port). Endpoint: CrowdStrike Falcon telemetry on the edge hosts (ProcessRollup2/SyntheticProcessRollup2, NetworkReceiveAcceptIP4, NetworkConnectIP4, file-write families) — note that an edge appliance without the Falcon sensor has no CQL telemetry of its own; Datadog Kubernetes/container logs and Live Process Monitoring for the industrial-edge namespace; Windows Security event logs only where a Windows host administers the Common Configurator. OT/ICS: asset inventory and communication/flow records from every deployed OT monitoring platform (Claroty xDome/CTD, Dragos, Nozomi, Tenable OT, Armis, Forescout eyeInspect), historian/SCADA alarms, and SNMP counters and trap logs for the edge device and its facing switch port. External attack surface: the passive internet-scan indices queried read-only (Shodan / Censys / Netlas), the CISA KEV catalog, and the organization's authoritative public IP and cloud-asset inventories — the last of which is required for attribution and without which the exposure determination cannot be made.

Detection Signatures

SIGMA rule 1 (process_creation) — interpreter or network tool spawned by an Industrial Edge configurator service:

title: Industrial Edge Configurator Spawns Shell or Network Tool
id: 7c1e4a2b-3d4f-4a6b-9c8d-1e2f3a4b5c6d
status: experimental
description: Detects a shell, scripting interpreter, or network utility whose parent is a Siemens Industrial Edge configurator service, consistent with code execution after Node2Shell (CVE-2026-111497) exploitation.
references:
- https://cert-portal.siemens.com/productcert/html/ssa-443410.html
author: 1898 & Co.
date: 2026-11-03
logsource:
product: linux
category: process_creation
detection:
selection_parent:
ParentImage|endswith:
- '/cloudconnector'
- '/common-configurator'
- '/databus-configurator'
- '/opcua-configurator'
- '/flowcreator'
- '/dataflow-monitoring'
selection_child:
Image|endswith:
- '/sh'
- '/bash'
- '/dash'
- '/python3'
- '/nc'
- '/ncat'
- '/socat'
condition: selection_parent and selection_child
falsepositives:
- Legitimate maintenance scripts run during an approved change window
level: high

 

SIGMA rule 2 (network_connection) — outbound connection from an edge host to a public address:

title: Industrial Edge Host Outbound Connection to Public Address
id: 2b9d8e1a-4c5f-4b7a-8d6e-9f0a1b2c3d4e
status: experimental
description: Detects an outbound connection from an Industrial Edge configurator or shell process to a non-RFC1918 address, consistent with Node2Shell post-exploitation C2.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-111497
author: 1898 & Co.
date: 2026-11-03
logsource:
product: linux
category: network_connection
detection:
selection:
Image|contains:
- '/cloudconnector'
- '/common-configurator'
- '/databus'
- '/opcua'
- '/flowcreator'
- '/dataflow'
- '/socat'
- '/ncat'
filter_rfc1918:
DestinationIp|cidr:
- '10.0.0.0/8'
- '172.16.0.0/12'
- '192.168.0.0/16'
condition: selection and not filter_rfc1918
falsepositives:
- Configurator reaching a sanctioned external license or update endpoint
level: high

 

SIGMA rule 3 (webserver) — inbound request carrying a brotli-encoded body to an IEM service:

title: Inbound Brotli-Encoded Request Body to Industrial Edge Service
id: 5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c
status: experimental
description: Detects an inbound HTTP request whose Content-Encoding is br reaching an Industrial Edge service, the delivery vector for a crafted Brotli stream exploiting CVE-2026-111497. Requires decrypted visibility (WAF or reverse proxy).
references:
- https://cert-portal.siemens.com/productcert/html/ssa-443410.html
author: 1898 & Co.
date: 2026-11-03
logsource:
category: webserver
detection:
selection:
cs-method: 'POST'
cs-content-encoding|re: '(?i)\bbr\b'
condition: selection
falsepositives:
- A legitimate client that compresses request bodies with brotli (rare; baseline per application)
level: medium

 

Snort/Suricata rule 1 — inbound brotli-encoded request body to a configurator port (fires only where the sensor sees decrypted traffic; on TLS 443 this requires inbound decryption):

alert tcp any any -> $IEM_SERVERS [443,9443,8443] (msg:"1898 Node2Shell inbound Brotli-encoded request body to Industrial Edge"; flow:to_server,established; http.method; content:"POST"; http.header; content:"Content-Encoding"; nocase; pcre:"/Content-Encoding\x3a\s*br/Hi"; detection_filter:track by_src, count 5, seconds 60; classtype:attempted-admin; sid:1000001; rev:1; reference:url,cert-portal.siemens.com/productcert/html/ssa-443410.html;)

 

Snort/Suricata rule 2 — outbound connection from the IEM subnet to a public address after exploitation:

alert tcp $IEM_SERVERS any -> !$HOME_NET any (msg:"1898 Node2Shell possible post-exploitation egress from Industrial Edge host"; flow:to_server,established; detection_filter:track by_src, count 3, seconds 300; classtype:trojan-activity; sid:1000002; rev:1; reference:url,nvd.nist.gov/vuln/detail/CVE-2026-111497;)

 

YARA rules are defined below. Rule Edge_Brotli_Exploit_Dropped_Payload targets files dropped to disk after exploitation: it anchors on reverse-shell and loader string fragments commonly staged on a compromised Linux edge host, OR'd so any one branch fires, and it is scoped by being run only against the edge application directories to limit false positives on unrelated system binaries. Rule Edge_Configurator_InMemory_Exec targets the configurator process memory: it looks for the co-occurrence of an interactive-shell path with a socket/exec primitive, structured as an AND of a shell indicator and a networking primitive so an ordinary configurator that merely contains the string "/bin/sh" in a help message does not alone trigger. Rule Edge_Host_Linux_Credential_Access targets Linux credential-theft tooling on the pivot host: it fires on direct reads of process memory maps or known Linux credential-dumper names, with a fullword constraint to reduce substring collisions.

rule Edge_Brotli_Exploit_Dropped_Payload
{
meta:
description = "Reverse-shell / loader artifacts dropped on a Siemens Industrial Edge host after Node2Shell (CVE-2026-111497) exploitation"
author = "1898 & Co."
date = "2026-11-03"
reference = "https://cert-portal.siemens.com/productcert/html/ssa-443410.html"
strings:
$elf = { 7f 45 4c 46 } // ELF magic — a native payload written into an app/tmp dir
$s1 = "/dev/tcp/" ascii // bash reverse-shell redirector
$s2 = "socket(AF_INET" ascii // raw socket reverse shell
$s3 = "sh -i" ascii // interactive shell invocation
$s4 = "python -c 'import socket" ascii // python reverse-shell one-liner
$s5 = "bash -i >& /dev/tcp" ascii // classic bash reverse shell
condition:
($elf at 0 and 1 of ($s*)) or 2 of ($s*)
}

 

rule Edge_Configurator_InMemory_Exec
{
meta:
description = "Shell + networking primitive co-resident in Industrial Edge configurator process memory (post-exploit execution)"
author = "1898 & Co."
date = "2026-11-03"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2026-111497"
strings:
$shell1 = "/bin/sh" ascii
$shell2 = "/bin/bash" ascii
$net1 = "connect" ascii fullword
$net2 = "dup2" ascii fullword
$net3 = "execve" ascii fullword
condition:
1 of ($shell*) and 2 of ($net*)
}

 

rule Edge_Host_Linux_Credential_Access
{
meta:
description = "Linux credential-dumping indicators on a compromised Industrial Edge pivot host"
author = "1898 & Co."
date = "2026-11-03"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2026-111497"
strings:
$m1 = "/proc/" ascii
$m2 = "/maps" ascii
$t1 = "mimipenguin" ascii fullword
$t2 = "/etc/shadow" ascii
$t3 = "lazagne" ascii nocase fullword
condition:
($m1 and $m2) or any of ($t*)
}

 

Indicators of Compromise

Network: inbound HTTP requests to an Industrial Edge configurator service (ports 443/9443/8443 and product-specific ports) carrying a request Content-Encoding header of "br"; inbound session bursts from a single external source to a configurator port; outbound connections from an edge host or a configurator/shell process to a non-RFC1918 address; new flows originating FROM an edge host into OT Purdue levels (S7CommPlus, OPC UA, Modbus, CIP) with no prior baseline.

Host: a shell, scripting interpreter, or network utility (sh/bash/python/socat/nc/ncat) whose parent is a configurator service; new ELF or script files written under edge application or temp directories (for example /var/lib/edge, /opt/edge, /tmp) outside a maintenance window; configurator process crash-restart loops; unexpected device reboot (SNMP coldStart/warmStart) coincident with inbound traffic.

OT/operational: engineering or program-download activity, mode changes, or configuration writes to controllers traced to an edge-host source address; any authentication originating from the edge appliance into an OT segment; SNMP v1/v2c community access to an OT asset (a weakness in its own right). Behavioral patterns are scoped to this hunt and are not published atomic indicators.

False Positive Baseline

1. Browsers and HTTP clients routinely advertise Accept-Encoding: gzip, deflate, br on requests — this is response-side negotiation and is NOT the exploit vector; only a request Content-Encoding of "br" on an inbound request body is relevant. Do not alert on Accept-Encoding.

2. Siemens-managed Industrial Edge Hub and Industrial Edge Management Cloud sit behind Firewall/WAF and were remediated on the central service; traffic to those managed endpoints should not be treated as exposure.

3. Configurator process restarts that align to a sanctioned update, redeploy, or node reboot during an approved maintenance window — correlate against the change calendar before escalating.

4. Administrative shells on an edge host launched by an authorized operator during a maintenance window (attribute to a known jump host and account).

5. Internal vulnerability scanners and asset-discovery tools probing IEM ports — confirm the source is a sanctioned scanner subnet.

6. EASM misattribution: shared hosting, CDN, and carrier infrastructure can make an unrelated host look owned (and vice-versa); an Industrial Edge banner on a CDN edge or a provider-ASN host is Non-Conclusive until confirmed by ≥2 independent ownership signals. Honeypots also cluster where OT findings look best.

Escalation Criteria

1. Any inbound brotli-encoded request body to a configurator service correlated with a configurator crash/restart on the same host — engage IR immediately (active exploitation).

2. Any shell, interpreter, or network tool spawned by a configurator service process (SIGMA rule 1) — engage IR (code execution).

3. Any outbound connection from a configurator/shell process or the IEM subnet to a non-baseline public IP (SIGMA rule 2 / Snort sid:1000002) — engage IR (candidate C2).

4. Any YARA hit from Edge_Brotli_Exploit_Dropped_Payload against the edge application or temp directories — isolate the host and preserve the file for analysis.

5. Any YARA hit from Edge_Configurator_InMemory_Exec against a configurator or child PID — capture process memory and isolate the host.

6. Any YARA hit from Edge_Host_Linux_Credential_Access on an edge host — treat all credentials reachable from that host as compromised and begin rotation.

7. Any new flow from an edge host into an OT Purdue level (program download, mode change, or control-protocol write traced to the edge source) — engage OT IR and consider controlled isolation of the edge node from the control segment.

8. A confirmed owned, in-range internet-exposed IEM/configurator instance (Hypothesis 1) corroborated in ≥2 indices, spanning the window, or matching a KEV entry — treat as presumptively enumerated and prioritize for exhaustive internal hunting and immediate exposure removal.

Hunt Completion Criteria and Reporting

The hunt is complete when every hypothesis has a verdict rendered strictly by its Verdict criteria, and when each leg's control has been run and recorded. The report must: give a verdict per hypothesis (a residual finding in a secondary leg does not downgrade a primary verdict); make absence claims only from a single-predicate filter (filter for the artifact and count), never by reading a distribution table; quote only queries whose LogScale status reads Done (a progressively rendered aggregate looks final when partial — include the status bar in screenshots); and record every limitation with its direction. Named directional limitations for this hunt: IEM services are TLS, so request Content-Encoding is observable only where traffic is decrypted at a WAF/reverse proxy or a keylog exists (inbound only); an edge appliance without the Falcon sensor produces no CQL telemetry; Datadog visibility depends on the Agent and Kubernetes log collection being enabled for the industrial-edge namespace; xDome Communication Analysis is capped at ~one month and can under-report sessions and bytes; OT-platform PCAP availability depends on sensor capture being enabled. Any leg whose control was 0 or not run is reported Non-Conclusive, naming the missing input — never as clean. Record the EASM sweep's coverage and per-hit provenance, and recommend making the external-exposure check a recurring control. Report threat-intel claims with their source.

Advisory IoC Reference

IOC Type IOC
CVE CVE-2026-111497 | CVSS v3.1 9.8 / v4.0 9.2 | Siemens Industrial Edge Mgmt Apps (Brotli 1.0.3–1.2.0) | Out-of-bounds write (CWE-787) in the Brotli decoder; remote unauthenticated crafted stream → info disclosure, DoS, possible RCE
Threat Actor None attributed in source material — monitor CISA and Siemens ProductCERT feeds
Malware None published in source material — monitor CISA and vendor feeds
Network IOC None published in source material — monitor https://www.cisa.gov/known-exploited-vulnerabilities-catalog
File IOC None published in source material — monitor https://cert-portal.siemens.com/productcert/html/ssa-443410.html
Behavioral Inbound HTTP request with Content-Encoding: br to an Industrial Edge configurator port (443/9443/8443)
Behavioral Configurator service process crash-restart loop coincident with an inbound session burst
Behavioral Shell/interpreter/network tool (sh, bash, python, socat, nc, ncat) spawned by a configurator service
Behavioral New ELF or script written under an edge application/temp directory (e.g. /var/lib/edge, /opt/edge, /tmp) outside a maintenance window
Behavioral Outbound connection from a configurator/shell process or the IEM subnet to a non-RFC1918 address
Behavioral New flow from an edge host into an OT Purdue level (program download, mode change, or control-protocol write traced to the edge source)
Behavioral Unexpected edge-device reboot (SNMP coldStart/warmStart) coincident with inbound traffic to a configurator port