---
title: "Threat Hunt Plan: Siemens Industrial Edge \"Node2Shell\" Brotli Decoder Compromise — Exploitation and Post-Exploitation Hunt"
description: "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\""
---

[Advisories | 1898 & Co.](https://1898advisories.burnsmcd.com)

# [Threat Hunt Plan: Siemens Industrial Edge "Node2Shell" Brotli Decoder Compromise — Exploitation and Post-Exploitation Hunt](https://1898advisories.burnsmcd.com/threat-hunt-plan-siemens-industrial-edge-node2shell-brotli-decoder-compromise-exploitation-and-post-exploitation-hunt)

 Written by [The 1898 & Co. Team](https://1898advisories.burnsmcd.com/author/1898-co-team) | November 3, 2026

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=`

[View full post](https://1898advisories.burnsmcd.com/threat-hunt-plan-siemens-industrial-edge-node2shell-brotli-decoder-compromise-exploitation-and-post-exploitation-hunt)

```json
{
  "@context" : "http://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "The 1898 & Co. Team"
  },
  "dateModified" : "2026-11-03T22:25:02.739Z",
  "datePublished" : "2026-11-03T22:25:02Z",
  "headline" : "Threat Hunt Plan: Siemens Industrial Edge \"Node2Shell\" Brotli Decoder Compromise — Exploitation and Post-Exploitation Hunt",
  "image" : {
    "@type" : "ImageObject",
    "height" : 60,
    "url" : "/hs/hsstatic/content_shared_assets/static-1.4092/img/default-amp-logo.png",
    "width" : 60
  },
  "mainEntityOfPage" : "https://1898advisories.burnsmcd.com/threat-hunt-plan-siemens-industrial-edge-node2shell-brotli-decoder-compromise-exploitation-and-post-exploitation-hunt",
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "height" : 60,
      "url" : "/hs/hsstatic/content_shared_assets/static-1.4092/img/default-amp-logo.png",
      "width" : 60
    },
    "name" : "Advisories | 1898 & Co."
  }
}
```