EIPStackGroup / EIPStackGroup/OpENer

Denial of Service (DoS) via Single-Connection Slowloris Attack in TCP Network Handler

Open
#562 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C
Stars
857
Forks
314
Avg merge
18d 2h
Merged PRs (30d)
1

Description

## Vulnerability Description
I find that OpENer 2.3.0 (commit 76b95cf) 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:
1. **Single-threaded event loop:** `NetworkHandlerProcessCyclic()` uses `select()` in a single thread to multiplex all incoming socket events.
2. **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.
3. **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
1. The target device is running OpENer and listening on the default ENIP TCP port (44818).
2. The attacker establishes a standard TCP connection to the ENIP port.
3. 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](https://github.com/user-attachments/files/27006269/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:
```bash
./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:
```bash
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:
```bash
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.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.