## https://sploitus.com/exploit?id=1A8BEE35-3D8B-5217-A51A-9AE6A5DD1043
# OPC UA Authentication Challenge Gateway
**CDDC 2026 | Modular ICS/OT Security Layer**
---
## Overview
This module is a standalone, modular OPC UA authentication challenge layer that sits in front of any ICS/OT testbed as a network-level gateway. Competitors must discover and exploit a deliberate CVE-2023-27321 class flaw (CWE-295) in OPC UA session authentication before they can reach the testbed behind it.
```
[Competitor Laptop]
|
| (must bypass OPC UA auth flaw)
|
[This Module โ Raspberry Pi]
|
| (network access unlocked after bypass)
|
[Testbed โ PLC, SCADA, HMI, etc.]
|
[Flag delivered by testbed]
```
**This module does NOT deliver flags.** It only controls network access to your testbed. Your testbed handles flag delivery.
---
## How It Works
### The Vulnerability (CVE-2023-27321 Class / CWE-295)
OPC UA requires two places where a client declares its identity:
- **Place 1**: X.509 Certificate SAN URI field (cryptographic โ e.g. `urn:cddc:legitimate:client`)
- **Place 2**: OPC UA CreateSession `ApplicationDescription.ApplicationUri` (protocol-layer string)
Per OPC UA Specification Part 4 ยง5.4.1, a compliant server **must** verify these two match. This server only checks Place 2. A competitor who sets their OPC UA session `ApplicationUri` to the authorized pattern โ regardless of what their certificate actually says โ will bypass authentication.
### The Network Gateway
The Pi sits physically in series between the competitor and the testbed:
```
Competitor Laptop โ eth1 (USB adapter) โ Pi โ eth0 โ Switch โ PLC
โ HMI
```
By default, iptables DROP rules block all traffic from eth1 to the testbed. After a successful bypass, `bypass_trigger.py` removes the DROP rules for the configured hold period, then restores them on reset. The testbed is unreachable at the network level โ no PLC modification required.
---
## File Structure
```
opcua_challenge/
โโโ gateway.py # OPC UA challenge server
โโโ bypass_trigger.py # Network isolation controller
โโโ config.json # All deployment values (generated by install.sh)
โโโ gen_certs.sh # Certificate generator
โโโ exploit.py # Full attacker-style exploit (answer key)
โโโ exploit_simple.py # Minimal proof-of-concept exploit (answer key)
โโโ install.sh # One-shot installer
โโโ setup.conf # Operator configuration (fill before install)
โโโ certs/ # Generated certificates (created by install.sh)
โโโ server_cert.der # Server certificate โ load in gateway.py
โโโ server_key.pem # Server private key โ keep secret
โโโ sample_client_cert.der # Hint artifact โ give to competitors
โโโ sample_client_key.pem # Hint artifact โ give to competitors
competitor_package/ # Files to hand to competitors (created by install.sh)
โโโ server_cert.der
โโโ sample_client_cert.der
โโโ sample_client_key.pem
```
### File Descriptions
**`gateway.py`** โ The OPC UA challenge server. Runs on port 4840 with `Basic256Sha256_SignAndEncrypt`. Monkey-patches `InternalSession.create_session` to capture `ApplicationUri` from the session layer (the only moment it exists in memory). Contains a deliberate `ChallengeUserManager` that ignores client certificate SAN. Main loop checks `System_Override` node every second and calls `bypass_trigger.py` when a valid URI is detected.
**`bypass_trigger.py`** โ Decoupled network isolation controller. Called by `gateway.py` with two functions: `trigger_bypass()` and `reset_bypass()`. Supports two isolation methods configured via `config.json` โ `iptables` (series/Pi-in-path) or `smrt` (TP-Link TL-SG105E VLAN control). Rebuilds clean iptables rules on every startup to prevent rule accumulation.
**`config.json`** โ All runtime values. Generated automatically by `install.sh` from `setup.conf`. Contains OPC UA settings, network isolation settings, and hint text shown to competitors.
**`gen_certs.sh`** โ Generates server and sample client certificates. Server cert SAN contains `urn:freeopcua:python:server`. Sample client cert SAN contains `urn:cddc:legitimate:client` โ deliberately NOT the admin URI. The gap between sample client SAN and the required session URI is the core teaching moment of the challenge.
**`exploit.py`** โ Full attacker-style CLI exploit with recon mode, colored output, and argument parsing. Use as the answer key / verification tool.
**`exploit_simple.py`** โ Minimal proof-of-concept exploit. Heavily commented to explain every step. Read this first.
**`install.sh`** โ One-shot installer. Handles internet detection, dependency installation, config generation, certificate generation, NetworkManager configuration, IP forwarding, iptables setup, and competitor package creation. Supports `--uninstall` flag to cleanly remove everything.
**`setup.conf`** โ Operator configuration file. Fill this in before running `install.sh`. All deployment values live here โ never edit `config.json` directly.
---
## Deployment
### Prerequisites
- Raspberry Pi 5 (or any Linux device with two network interfaces)
- USB ethernet adapter (for the competitor-facing interface)
- Python 3.12+ (3.14 breaks asyncua)
- Internet access for initial install (via WiFi if company network blocks direct access)
### Physical Setup
```
Competitor Laptop
|
| (ethernet cable)
|
[USB Ethernet Adapter โ eth1]
|
[Raspberry Pi]
|
[Built-in Ethernet โ eth0]
|
| (ethernet cable)
|
[Switch]
|
+โโโ PLC (192.168.2.3)
+โโโ HMI (192.168.2.4)
```
### Installation (5 Steps)
**Step 1 โ Fill in setup.conf**
Open `setup.conf` and set all required values:
```bash
AUTHORIZED_URI="urn:cddc:admin:console"
NAMESPACE_URI="http://nshc.security.cddc"
ENDPOINT_PATH="/cddc/industrial/"
TESTBED_HOSTS="192.168.2.3,192.168.2.4"
COMPETITOR_INTERFACE="eth1"
TESTBED_INTERFACE="eth0"
TESTBED_IP="192.168.2.20"
COMPETITOR_IP="10.10.10.1"
HOLD_SECONDS=10
ISOLATION_METHOD="iptables"
HINT_SECURITY="..."
HINT_SPEC="..."
```
**Step 2 โ Copy files to Pi**
```bash
scp -r ./opcua_challenge k@cddc-pi:~/opcua_challenge
```
**Step 3 โ Run installer**
```bash
ssh k@cddc-pi
cd ~/opcua_challenge
sudo ./install.sh
```
The installer automatically:
- Detects internet access (tries direct, falls back to wlan0)
- Installs system packages and Python dependencies
- Generates config.json and certificates
- Creates competitor_package/ with correct permissions
- Configures eth0 and eth1 static IPs via NetworkManager
- Enables IP forwarding permanently
- Sets up iptables rules in correct order
- Creates run.sh
**Step 4 โ Start the gateway**
```bash
sudo /opt/opcua_challenge/run.sh
```
**Step 5 โ Give competitors their package**
```bash
/opt/opcua_challenge/competitor_package/
server_cert.der # Add to OPC UA client trust store
sample_client_cert.der # Hint artifact
sample_client_key.pem # Hint artifact
```
### Uninstall
```bash
sudo ./install.sh --uninstall
```
Removes all files, NetworkManager connections, iptables rules, and sysctl changes.
---
## Isolation Methods
### Method 1: iptables (Series/Pi-in-Path)
Set `ISOLATION_METHOD="iptables"` in `setup.conf`.
Pi sits physically in series between competitor and testbed. All traffic must pass through the Pi. iptables DROP rules block testbed hosts by default. On bypass, DROP rules are removed. On reset, DROP rules are restored and conntrack table is flushed to kill existing connections.
**Pros:** Works with any switch including unmanaged. Simple and reliable.
**Cons:** Requires USB ethernet adapter on Pi. Physical rewiring needed.
### Method 2: smrt (TP-Link TL-SG105E VLAN Control)
Set `ISOLATION_METHOD="smrt"` in `setup.conf`.
Pi controls port VLAN isolation on a TP-Link TL-SG105E Easy Smart switch via the `smrt` library (github.com/philippechataignon/smrt). Competitor port is isolated in VLAN 20 by default. On bypass, competitor port is moved into testbed VLAN 10. No physical rewiring needed.
**Pre-requisites:**
1. Configure VLANs on switch via web interface:
- VLAN 10 (testbed): Pi port + PLC port + HMI port
- VLAN 20 (isolated): Competitor port only
2. Clone smrt: `git clone https://github.com/philippechataignon/smrt.git /opt/smrt`
3. Run discovery: `python3 /opt/smrt/discovery.py -i eth0`
4. Set `switch_mac` in config.json
**Pros:** No USB adapter needed. No physical rewiring.
**Cons:** Requires specific switch model (TL-SG105E).
---
## Competitor Experience
1. Competitor connects laptop cable to the Pi's eth1 (USB adapter)
2. Competitor gets DHCP/static IP in 10.10.10.x range
3. Competitor scans and finds OPC UA server at 10.10.10.1:4840
4. Competitor connects with UaExpert using provided sample client cert โ connection succeeds
5. Competitor browses node tree and finds hint nodes (Security_Policy, Session_Binding_Spec)
6. Competitor reads hints and realizes the server checks ApplicationUri
7. Competitor reads OPC UA Spec ยง5.4.1 and realizes the cert SAN is never verified
8. Competitor writes a custom client that sets ApplicationUri to the authorized pattern
9. Bypass succeeds โ network access to testbed unlocked for configured hold period
10. Competitor interacts with testbed to capture flag
---
## Adjusting Difficulty
**Easier:** Increase `HOLD_SECONDS` to give more time to interact with testbed after bypass.
**Harder:** Change `AUTHORIZED_URI` to something less obvious and make `HINT_SECURITY` more cryptic.
**For testing:** Set `ISOLATION_METHOD` to `none` in config.json to disable network isolation and test OPC UA layer alone.
---
## Troubleshooting
**"Permission denied" connecting to gateway:**
Competitor must add `server_cert.der` to their OPC UA client trust store. In UaExpert: Security โ Manage Certificates โ Trusted.
**"asyncua not found" when running with sudo:**
Use the run.sh script which sets PYTHONPATH correctly:
```bash
sudo /opt/opcua_challenge/run.sh
```
**Bypass fires but testbed still unreachable:**
Check IP forwarding is still active:
```bash
cat /proc/sys/net/ipv4/ip_forward # Should be 1
```
Check iptables rules:
```bash
sudo iptables -L FORWARD -n -v
```
Check Pi can reach testbed:
```bash
ping 192.168.2.3
```
**"Address already in use" on port 4840:**
Kill existing gateway process:
```bash
sudo pkill -f gateway.py
```
**Competitor can still reach testbed after reset:**
Conntrack table may have stale connections. Flush manually:
```bash
sudo conntrack -F
```