Back to Intelligence

CVE-2026-91018: lwIP Double Free Vulnerability in ICS/OT Devices — Detection and Remediation Guide

SA
Security Arsenal Team
September 23, 2026
14 min read

CISA has published ICS Advisory ICSA-26-265-02 covering CVE-2026-91018, a double free vulnerability in lwIP (Lightweight IP), the open-source TCP/IP stack embedded in an enormous range of operational technology (OT) and IoT firmware. The flaw carries a CVSS v3 base score of 8.8 (High) and affects lwIP API versions 2.0.1 through 2.2.1. Successful exploitation can crash the device, cause a denial of service, or corrupt memory in a way that opens the door to arbitrary code execution on the victim system.

If you run a SOC that covers industrial environments, this advisory deserves immediate attention — not because a public exploit exists today, but because of where lwIP lives. It is baked into firmware for PLCs, RTUs, sensor gateways, building automation controllers, medical devices, and network appliances from vendors who may never individually disclose that their product contains it. CISA lists affected deployments across Chemical, Communications, Critical Manufacturing, Energy, Financial Services, Healthcare and Public Health, Transportation Systems, and Water and Wastewater Systems — essentially the full critical infrastructure landscape, deployed worldwide.

The defensive challenge here is twofold: (1) you almost certainly cannot patch most affected devices quickly, because lwIP is compiled into vendor firmware, and (2) memory corruption in a network stack is remotely triggerable, meaning the network path to these devices is your real attack surface. This post walks through the vulnerability, how to hunt for exploitation attempts and crash telemetry, and how to mitigate risk while waiting on vendor firmware.

Technical Analysis

Affected Products and Versions

AttributeDetail
ProductlwIP (Lightweight IP) TCP/IP stack
Affected versionsAPI >= 2.0.1 and <= 2.2.1
CVECVE-2026-91018
WeaknessDouble Free (CWE-415)
CVSS v38.8 (High)
AdvisoryCISA ICSA-26-265-02
Vendor HQSweden (lwIP originated at the Swedish Institute of Computer Science)
DeploymentWorldwide, embedded in ICS/OT/IoT firmware

How the Vulnerability Works

A double free (CWE-415) occurs when a program calls free() (or its equivalent) on the same memory region twice. In a network stack like lwIP, packet buffers (pbufs) are allocated and freed constantly as packets move through receive, processing, and transmit paths. If an error-handling path, reassembly routine, or connection teardown sequence frees a pbuf that has already been released back to the heap pool, the allocator's internal metadata is corrupted.

From a defender's perspective, the exploitation chain looks like this:

  1. Remote trigger: The attacker sends crafted network traffic to any service listening on the lwIP stack — this could be an embedded web server, Modbus/TCP handler, SNMP agent, or a custom protocol listener the vendor built on top of lwIP sockets.
  2. Erroneous free path: The crafted traffic drives the stack into a state where a buffer is freed twice. No authentication is necessarily required — the flaw lives in packet processing logic.
  3. Impact:
    • Best case (for the attacker, worst is code execution): heap metadata corruption trips a sanity check or dereferences a poisoned pointer, crashing the stack or the entire RTOS task — an immediate denial of service. For an OT device, a crash often means a watchdog reboot and a process outage window.
    • Worst case: the attacker grooms the heap so the second free lets them reclaim the freed chunk with attacker-controlled data, hijacking allocator metadata and achieving arbitrary code execution with the privileges of the network stack — which, on a typical embedded RTOS, is effectively the whole device.

Heap exploitation on embedded targets is harder than on desktop platforms (smaller heaps, deterministic allocators, no ASLR but also no easy shell), but "hard" is not "impossible," and CISA's own language confirms code execution is a credible outcome.

Exploitation Status

As of the advisory's publication:

  • No public proof-of-concept exploit has been observed.
  • No confirmed in-the-wild exploitation has been reported.
  • CVE-2026-91018 is not currently listed in CISA's Known Exploited Vulnerabilities (KEV) catalog.

Treat this as a pre-exploitation window. Memory corruption bugs in widely deployed embedded stacks historically attract researcher attention quickly once disclosed, and the window between disclosure and weaponization for network-stack bugs has been shrinking. The time to build detection and segmentation is now, while exploitation remains theoretical.

Detection & Response

You cannot install an EDR agent on a PLC running lwIP. Detection for this class of vulnerability lives at two layers: network telemetry (crafted traffic and connection anomalies directed at OT devices) and availability/crash telemetry (syslog, SNMP traps, and monitoring data showing devices crashing, rebooting, or dropping off the network — the most likely observable of both failed and successful exploitation attempts).

Sigma Rules

YAML
---
title: Embedded or ICS Device Crash Telemetry Indicating Memory Corruption
id: 9c4e7b12-3a58-4f91-b2d6-7e8f0a1c2d3e
status: experimental
description: Detects syslog messages indicative of memory corruption, double free, kernel panic, or watchdog resets on embedded/ICS devices. Repeated crash-reboot cycles on lwIP-based devices may indicate exploitation attempts against CVE-2026-91018.
references:
  - https://www.cisa.gov/news-events/ics-advisories/icsa-26-265-02
  - https://attack.mitre.org/techniques/T1499/
author: Security Arsenal
date: 2026/09/22
tags:
  - attack.impact
  - attack.t1499
logsource:
  product: linux
  service: syslog
detection:
  selection:
    - 'double free'
    - 'free(): invalid pointer'
    - 'kernel panic'
    - 'watchdog reset'
    - 'watchdog: BUG'
    - 'segmentation fault'
    - 'corrupted size'
    - 'heap corruption'
    - 'fatal signal'
    - 'BUG: unable to handle'
  condition: selection
falsepositives:
  - Legitimate hardware faults and flaky embedded devices (baseline per-device reboot frequency before tuning)
  - Firmware update processes that trigger watchdog reboots
level: medium
---
title: High-Volume Connection Attempts to ICS Protocol Services
id: 2f8a1d45-6c79-4e02-a3b4-8d9e0f1a2b3c
status: experimental
description: Detects a single source generating an abnormally high number of connection attempts to common ICS/embedded service ports (Modbus, EtherNet/IP, DNP3, BACnet). Crafted traffic against a vulnerable lwIP stack (CVE-2026-91018) will typically manifest as connection flooding or malformed-session bursts during heap grooming or fuzzing.
references:
  - https://www.cisa.gov/news-events/ics-advisories/icsa-26-265-02
  - https://attack.mitre.org/techniques/T1190/
author: Security Arsenal
date: 2026/09/22
tags:
  - attack.initial_access
  - attack.t1190
logsource:
  category: firewall
detection:
  selection:
    dst_port:
      - 502
      - 20000
      - 44818
      - 47808
    action: 'allow'
  condition: selection | count(src_ip, dst_ip) > 100
falsepositives:
  - HMI polling and SCADA scan cycles (baseline normal polling rates per source/destination pair; legitimate HMI traffic is steady-state, exploitation bursts are anomalous)
  - Authorized vulnerability scanners (allowlist scanner IPs)
level: high
---
title: IT-to-OT Segment Communication from Unmanaged Host
description: Detects network connections from hosts outside the authorized OT management group into industrial protocol ports. Any communication with lwIP-based embedded devices should originate from a small, known set of engineering workstations, HMIs, and jump hosts; anything else is an exploitation pathway for CVE-2026-91018.
id: 4b6c8e02-1a3d-4f5b-9c7d-2e4f6a8b0c1d
status: experimental
references:
  - https://www.cisa.gov/news-events/ics-advisories/icsa-26-265-02
  - https://attack.mitre.org/techniques/T1190/
author: Security Arsenal
date: 2026/09/22
tags:
  - attack.initial_access
  - attack.t1190
logsource:
  category: network_connection
  product: windows
detection:
  selection:
    DestinationPort:
      - 502
      - 20000
      - 44818
      - 47808
  filter_known_ot_clients:
    SourceIp:
      - '10.10.20.0/24'
      - '10.10.30.15'
  condition: selection and not 1 of filter_known_ot_clients*
falsepositives:
  - Newly deployed engineering workstations not yet added to the authorized management list (maintain the allowlist as part of change control)
level: high

Note: Replace the example OT management addresses in the third rule with your own authorized engineering workstation and jump-host ranges. These rules are only as good as your asset inventory.

KQL Hunting (Microsoft Sentinel / Defender)

The following query hunts syslog telemetry (forwarded from ICS devices, syslog concentrators, or OT monitoring sensors) for crash and memory-corruption indicators, then surfaces devices exhibiting repeated crash-reboot behavior — the signature of someone probing a vulnerable network stack:

KQL — Microsoft Sentinel / Defender
// Hunt 1: ICS/embedded devices showing memory corruption or crash-reboot telemetry
let CrashTerms = dynamic(["double free", "free(): invalid pointer", "kernel panic", "watchdog reset", "watchdog: BUG", "segmentation fault", "corrupted size", "heap corruption", "fatal signal", "BUG: unable to handle"]);
Syslog
| where TimeGenerated > ago(24h)
| where SyslogMessage has_any (CrashTerms)
| summarize CrashEvents = count(), DistinctIndicators = dcount(SyslogMessage), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), SampleMessages = make_set(SyslogMessage, 5) by Computer, HostIP, Facility
| where CrashEvents >= 3
| extend RebootWindowMinutes = datetime_diff("minute", LastSeen, FirstSeen)
| sort by CrashEvents desc;

// Hunt 2: Anomalous connection bursts toward ICS protocol services via CEF/firewall ingestion
let ICSPorts = dynamic([502, 20000, 44818, 47808]);
CommonSecurityLog
| where TimeGenerated > ago(6h)
| where DestinationPort in (ICSPorts)
| summarize Connections = count(), UniqueDestinations = dcount(DestinationIP), DestPorts = make_set(DestinationPort) by SourceIP, DeviceAction, bin(TimeGenerated, 5m)
| where Connections > 100 or UniqueDestinations > 20
| sort by Connections desc;

// Hunt 3: Known IT endpoints initiating connections to OT protocol ports (Defender-sourced)
let AuthorizedOTClients = dynamic(["10.10.20.11", "10.10.20.12", "10.10.30.15"]); // engineering workstations / jump hosts
DeviceNetworkEvents
| where TimeGenerated > ago(24h)
| where RemotePort in (502, 20000, 44818, 47808)
| where not(IPv4InSubnet(LocalIP, "10.10.0.0/16")) == false // devices inside OT VLANs are expected talkers
| where not(LocalIP in (AuthorizedOTClients))
| summarize Connections = count(), OTTargets = make_set(RemoteIP, 20), Ports = make_set(RemotePort) by DeviceName, LocalIP, InitiatingProcessFileName, InitiatingProcessCommandLine
| sort by Connections desc;

Tune AuthorizedOTClients and the OT subnet logic to your Purdue model. Hunt 1's threshold of three crash events is intentionally conservative — a single watchdog reboot on an embedded device is routine; a device crashing three or more times in 24 hours after receiving network traffic is a device someone is poking.

Velociraptor VQL

Because the vulnerable endpoints are embedded devices without agents, Velociraptor's role here is hunting the syslog collectors and OT monitoring servers that aggregate device telemetry. Deploy this artifact against your syslog concentrator hosts to extract crash/corruption events tied to specific ICS device IPs:

VQL — Velociraptor
-- Security Arsenal: CVE-2026-91018 lwIP crash telemetry extraction from syslog collectors
-- Target: Linux syslog concentrators aggregating ICS/OT device logs
-- Identifies embedded devices emitting memory corruption, panic, or watchdog indicators

LET syslog_files = SELECT FullPath
FROM glob(globs=['/var/log/syslog', '/var/log/syslog.*', '/var/log/messages', '/var/log/remote/**/*.log'])
WHERE FullPath =~ '(syslog|messages|remote)'

SELECT FullPath AS LogFile,
       Line AS CrashEvent,
       timestamp(epoch=now()) AS CollectionTime,
       parse_string_with_regex(string=Line, regex='(?P<ts>\w+\s+\d+\s+\d+:\d+:\d+)\s+(?P<host>[\w\-\.]+)\s+(?P<msg>.*)').host AS ReportingDevice,
       parse_string_with_regex(string=Line, regex='(?P<ts>\w+\s+\d+\s+\d+:\d+:\d+)\s+(?P<host>[\w\-\.]+)\s+(?P<msg>.*)').msg AS Message
FROM foreach(row=syslog_files,
query={
  SELECT FullPath, Line
  FROM parse_lines(filename=FullPath, accessor='file')
  WHERE Line =~ 'double free|free\(\): invalid pointer|kernel panic|watchdog reset|watchdog: BUG|segmentation fault|corrupted size|heap corruption|fatal signal'
})
ORDER BY ReportingDevice

Pivot on ReportingDevice: any single device appearing repeatedly is either failing hardware or an active exploitation target. Correlate the timestamps against firewall logs for inbound connection bursts to that device — if crash events follow connection spikes from a specific source IP, you have your answer.

Remediation / Verification Script

The hardest part of remediating CVE-2026-91018 is finding lwIP in your environment at all — it's statically linked into firmware and rarely listed in product documentation. The following Bash script inventories firmware images, binaries, and source trees for lwIP version markers, and flags versions in the vulnerable range (2.0.1–2.2.1). Run it against extracted firmware filesystems, vendor SDKs, Yocto/Buildroot build trees, and any internally developed embedded source:

Bash / Shell
#!/usr/bin/env bash
# Security Arsenal - CVE-2026-91018 lwIP exposure scanner
# Scans firmware trees, binaries, and source for lwIP version markers.
# Usage: ./lwip_exposure_scan.sh /path/to/firmware_or_source_root

TARGET="${1:-.}"
REPORT="lwip_exposure_report_$(date +%Y%m%d_%H%M%S).txt"

echo "=== lwIP Exposure Scan: CVE-2026-91018 ===" | tee "$REPORT"
echo "Target: $TARGET" | tee -a "$REPORT"
echo "Date:   $(date -u)" | tee -a "$REPORT"
echo "" | tee -a "$REPORT"

# --- 1. Source trees: parse lwip/init.h for LWIP_VERSION ---
echo "[1] Scanning source trees for lwip/init.h version definitions..." | tee -a "$REPORT"
while IFS= read -r -d '' header; do
  major=$(grep -m1 'LWIP_VERSION_MAJOR' "$header" | grep -oE '[0-9]+' | head -1)
  minor=$(grep -m1 'LWIP_VERSION_MINOR' "$header" | grep -oE '[0-9]+' | head -1)
  rev=$(grep -m1 'LWIP_VERSION_REVISION' "$header" | grep -oE '[0-9]+' | head -1)
  if [[ -n "$major" && -n "$minor" && -n "$rev" ]]; then
    ver="$major.$minor.$rev"
    status="NOT VULNERABLE"
    # Vulnerable range: 2.0.1 <= ver <= 2.2.1
    if [[ "$major" -eq 2 ]]; then
      if [[ "$minor" -lt 2 && "$rev" -ge 1 ]]; then status="VULNERABLE (CVE-2026-91018)"; fi
      if [[ "$minor" -eq 2 && "$rev" -le 1 ]]; then status="VULNERABLE (CVE-2026-91018)"; fi
      if [[ "$minor" -eq 0 && "$rev" -eq 0 ]]; then status="REVIEW - 2.0.0 outside stated range, confirm API version"; fi
    fi
    echo "  FOUND lwIP $ver -> $status : $header" | tee -a "$REPORT"
  fi
done < <(find "$TARGET" -type f -path '*lwip*' -name 'init.h' -print0 2>/dev/null)

# --- 2. Binaries/firmware images: string sweep for lwIP markers ---
echo "" | tee -a "$REPORT"
echo "[2] Scanning binaries for embedded lwIP strings..." | tee -a "$REPORT"
find "$TARGET" -type f \( -name '*.bin' -o -name '*.elf' -o -name '*.img' -o -name '*.fw' -o -name '*.hex' -o -perm -111 \) 2>/dev/null | while read -r bin; do
  hits=$(strings -a "$bin" 2>/dev/null | grep -iE 'lwip|lwIP/[0-9]|ip4_frag|pbuf_alloc' | head -5)
  if [[ -n "$hits" ]]; then
    verstr=$(strings -a "$bin" 2>/dev/null | grep -oE 'lwIP/[0-9]+\.[0-9]+\.[0-9]+' | head -1)
    echo "  lwIP signature in: $bin ${verstr:+(version string: $verstr)}" | tee -a "$REPORT"
    echo "$hits" | sed 's/^/      marker: /' | tee -a "$REPORT"
  fi
done

# --- 3. Network-adjacent check: list listening services on this host that may sit on lwIP-based devices' path ---
echo "" | tee -a "$REPORT"
echo "[3] Local ICS protocol listeners (informational - validate segmentation):" | tee -a "$REPORT"
ss -tlnp 2>/dev/null | grep -E ':(502|20000|44818|47808)\b' | tee -a "$REPORT" || echo "  None found on this host." | tee -a "$REPORT"

echo "" | tee -a "$REPORT"
echo "Scan complete. Review $REPORT. Any VULNERABLE finding requires vendor firmware engagement." | tee -a "$REPORT"

For network-level exposure discovery, complement this with an authenticated OT asset inventory and a passive network monitor — active scanning of fragile ICS devices running a vulnerable network stack is itself a crash risk, which is an uncomfortable sentence to have to write, but here we are.

Remediation

1. Patch (the only true fix). Upgrade lwIP to a version newer than 2.2.1 containing the CVE-2026-91018 fix — pull the current stable release or the patched commit from the official lwIP repository (savannah.nongnu.org/projects/lwip). For vendor products embedding lwIP, open cases with each OEM citing ICSA-26-265-02 and demand a firmware timeline. Track these as formal exceptions with expiry dates if vendors slip.

2. Inventory first. You cannot patch what you haven't identified. Use the scanner above on firmware dumps and build trees, review SBOMs (if your vendors don't provide SBOMs, this advisory is your leverage to demand them), and cross-reference procurement records against known lwIP-consuming product families.

3. Compensating controls while awaiting firmware:

  • Network segmentation: Enforce deny-by-default between IT and OT zones. lwIP devices should be reachable only from designated HMIs, engineering workstations, and jump hosts — and only on required ports. The third Sigma rule and KQL Hunt 3 above operationalize this verification.
  • Disable unused services: Every listening service on an lwIP device is a trigger surface. Disable embedded web servers, Telnet, SNMP, and unused protocol handlers on affected devices where operationally tolerable.
  • Stateful inspection: Where supported, place ICS-aware firewalls in front of vulnerable segments to drop malformed and fragmented traffic before it reaches the stack. Ensure TCP reassembly is enabled on the inspection device, not the endpoint.
  • Rate limiting: Connection-rate limiting at the zone boundary blunts the traffic bursts needed for heap grooming, without impacting steady-state HMI polling.
  • Availability monitoring: Stand up device uptime and reboot alerting (SNMP traps, ICMP health checks, syslog watchdog detection per Hunt 1). A crashing device is your earliest exploitation indicator.

4. Incident response readiness. Pre-stage firmware recovery procedures and spare units for critical lwIP-based devices. If a device enters a crash loop, treat it as a potential incident — capture network traffic to that device before rebooting it back to health, or you will destroy the evidence of whether the crash was malicious.

5. Governance. CISA advisories for critical infrastructure sectors carry an implicit remediation expectation. Document your exposure assessment, compensating controls, and vendor engagement in your vulnerability management system with CVE-2026-91018 as the tracking key — auditors and insurers increasingly ask for exactly this paper trail.

Related Resources

Security Arsenal Healthcare Cybersecurity AlertMonitor Platform Book a SOC Assessment healthcare Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.