Vulnerability Description
I find that OpENer is vulnerable to a Slowloris-style DoS attack. Because OpENer processes all network traffic in a single-threaded event loop without setting client sockets to non-blocking mode, an attacker can establish a single TCP connection, send an incomplete Ethernet/IP (ENIP) packet, and simply wait. This completely blocks the server's main network processing loop, preventing it from handling any other legitimate requests.
Root Cause
The root cause lies in the synchronous, blocking nature of the socket reads within the single-threaded network handler:
- Single-threaded event loop:
NetworkHandlerProcessCyclic() uses select() in a single thread to multiplex all incoming socket events.
- Missing non-blocking configuration: When a new TCP connection is accepted in
CheckAndHandleTcpListenerSocket(), the newly created new_socket is added to the select read set but is not set to non-blocking mode.
- Blocking reads: In
HandleDataOnTcpSocket(), the code expects a complete ENIP packet and issues direct, blocking reads: recv(socket, ..., 4, 0) followed by a loop reading data_size. If an attacker sends exactly 4 bytes (the ENIP command and length) and intentionally delays sending the rest of the payload, the second recv() call will block indefinitely until data arrives or a TCP timeout occurs. During this time, the entire NetworkHandlerProcessCyclic loop is stalled.
Trigger Conditions
- The target device is running OpENer and listening on the default ENIP TCP port (44818).
- The attacker establishes a standard TCP connection to the ENIP port.
- The attacker sends a partial payload (e.g., a 4-byte header specifying a large
data_size) and then holds the connection open without sending the remaining data.
Reproduction (Validated)
PoC.zip
1) Build:
Compile the OpENer project on a POSIX environment (e.g., Ubuntu):
- Change to the
bin/posix directory
- For a configuration invoke
setup_posix.sh (make sure -DOpENer_TRACES:BOOL=ON and -DOpENer_TRACE_LEVEL_ERROR:BOOL=ON are enabled to observe the error logs)
- Invoke the
make command
2) Run target:
Start the OpENer service, listening on the loopback interface:
./src/ports/POSIX/OpENer lo
3) Launch Attack:
In a new terminal, execute the slowloris attacker script to establish a connection and send an incomplete payload:
python3 slowloris_attacker.py
4) Run Legitimate Client Probe:
While the attacker script is holding the connection, run the normal client probe in another terminal to test the server's responsiveness:
python3 normal_client_probe.py
5) Observe Phenomena:
- The
normal_client_probe.py will report consecutive timeouts for its RegisterSession requests until the attacker finally closes the socket.
- Server logs may show delayed processing or print errors like
Error Code: 107 - Transport endpoint is not connected after the attacker closes the socket, proving the network loop was stalled.
Impact Assessment
This is a High severity availability issue. Industrial Control Systems (ICS) rely on deterministic and continuous communication. An unauthenticated attacker with minimal resources (a single TCP connection and negligible bandwidth) can remotely paralyze the OpENer protocol stack, causing legitimate Engineering Workstations or PLCs to lose communication with the device. This leads to a total loss of Ethernet/IP service availability.
Vulnerability Description
I find that OpENer is vulnerable to a Slowloris-style DoS attack. Because OpENer processes all network traffic in a single-threaded event loop without setting client sockets to non-blocking mode, an attacker can establish a single TCP connection, send an incomplete Ethernet/IP (ENIP) packet, and simply wait. This completely blocks the server's main network processing loop, preventing it from handling any other legitimate requests.
Root Cause
The root cause lies in the synchronous, blocking nature of the socket reads within the single-threaded network handler:
NetworkHandlerProcessCyclic()usesselect()in a single thread to multiplex all incoming socket events.CheckAndHandleTcpListenerSocket(), the newly creatednew_socketis added to theselectread set but is not set to non-blocking mode.HandleDataOnTcpSocket(), the code expects a complete ENIP packet and issues direct, blocking reads:recv(socket, ..., 4, 0)followed by a loop readingdata_size. If an attacker sends exactly 4 bytes (the ENIP command and length) and intentionally delays sending the rest of the payload, the secondrecv()call will block indefinitely until data arrives or a TCP timeout occurs. During this time, the entireNetworkHandlerProcessCyclicloop is stalled.Trigger Conditions
data_size) and then holds the connection open without sending the remaining data.Reproduction (Validated)
PoC.zip
1) Build:
Compile the OpENer project on a POSIX environment (e.g., Ubuntu):
bin/posixdirectorysetup_posix.sh(make sure-DOpENer_TRACES:BOOL=ONand-DOpENer_TRACE_LEVEL_ERROR:BOOL=ONare enabled to observe the error logs)makecommand2) Run target:
Start the OpENer service, listening on the loopback interface:
3) Launch Attack:
In a new terminal, execute the slowloris attacker script to establish a connection and send an incomplete payload:
4) Run Legitimate Client Probe:
While the attacker script is holding the connection, run the normal client probe in another terminal to test the server's responsiveness:
5) Observe Phenomena:
normal_client_probe.pywill report consecutive timeouts for itsRegisterSessionrequests until the attacker finally closes the socket.Error Code: 107 - Transport endpoint is not connectedafter the attacker closes the socket, proving the network loop was stalled.Impact Assessment
This is a High severity availability issue. Industrial Control Systems (ICS) rely on deterministic and continuous communication. An unauthenticated attacker with minimal resources (a single TCP connection and negligible bandwidth) can remotely paralyze the OpENer protocol stack, causing legitimate Engineering Workstations or PLCs to lose communication with the device. This leads to a total loss of Ethernet/IP service availability.