A factory-floor controller answering every Modbus command on the wire with no questions asked
The Delta Electronics DVP12SE programmable logic controller listens on Modbus TCP (default port 502) and accepts function codes — read coils, write single register, write multiple registers, force coil — from any client that can reach the port. There is no challenge, no key, no session binding, no source-IP gate. Affected firmware spans the DVP12SE series shipped under default configuration; Delta's advisory and CISA ICS-CERT treat the entire DVP-SE family configuration as in-scope until a firmware build that introduces ACLs or an auth proxy is deployed.
There is no published CVSS yet, which is the entire reason this needs an honest reassessment. Vendor labeling for legacy PLCs tends to under-call this class because *'Modbus has no auth by design'* is treated as a known limitation, not a vuln. That framing is wrong for a defender — when an attacker can flip a coil, the impact is physical actuation, and CWE-306 on an OT controller is not the same risk as CWE-306 on a webhook receiver.
5 steps from start to impact.
Reach TCP/502 on the PLC
- Routable path to PLC IP
- TCP/502 reachable (no firewall ACL)
- Most production OT networks are air-gapped or behind a data diode/firewall
- Engineering VLANs typically blocked from corporate
modbus enumerates externally.Fingerprint the device
auxiliary/scanner/scada/modbusclient, Smod — all do this in one command.- TCP session established on 502
- Some PLCs disable device-ID responses, but DVP-SE answers by default
Enumerate coils and holding registers
- Modbus session
- Knowledge of register map (often public for Delta DVP)
- Register semantics are program-specific; mapping them to physical effect takes engineering knowledge
Write to control registers — physical impact
- Write function codes not filtered upstream
- PLC in RUN mode
- Hardware key-switch in RUN-only mode blocks program changes but NOT register writes
- Mechanical interlocks may catch the dangerous state
Denial of control
- Network throughput to PLC
- Watchdogs and redundant PLCs limit some scenarios
The supporting signals.
| In-the-wild exploitation | No confirmed campaign tied to this CVE, but the technique class (unauth Modbus write) is canonical — used in Industroyer, Triton, and Volt Typhoon OT reconnaissance reporting (CISA AA24-038A). |
|---|---|
| Public PoC | Trivial — pymodbus, modbus-cli, metasploit auxiliary/scanner/scada/modbusclient, smod. No exploit dev required; this is a one-liner. |
| EPSS | 0.0031 (~0.3%) — low, reflecting that EPSS undercounts ICS where most assets are not internet-reachable. |
| KEV status | Not listed as of 2026-07-01. |
| CVSS vector | Not published by Delta or CISA at disclosure. ICS-aware reassessment is roughly AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H if the PLC is on a reachable network — a true ~9.x. Friction (network reachability) is what pulls this back. |
| Affected versions | Delta Electronics DVP12SE series PLC, Modbus TCP service, default configuration. Confirm exact firmware band against the Delta PSIRT bulletin. |
| Fixed version | Delta has historically shipped firmware updates that add IP allowlisting rather than authentication. Track Delta PSIRT for the DVP-SE firmware build that adds an ACL feature, and disable Modbus TCP entirely if unused. |
| Exposure (Shodan/Censys) | Shodan port:502 product:Delta returns small-hundreds of public hits globally — most DVP-SE deployments are NOT internet-exposed, but flat OT networks are common internally. |
| Disclosure date | 2026-06-30 (CISA ICS-CERT advisory expected within the next bulletin cycle). |
| CWE | CWE-306: Missing Authentication for Critical Function — the protocol-by-design framing does NOT excuse the controller role. |
noisgate verdict.
The single decisive factor is the role multiplier: the affected component is a PLC, which by definition occupies a high-value OT control role where the chain ends in physical actuation or safety impact. Public internet exposure is rare, but inside any flat OT network the attack is one Modbus write away, which sets the floor at HIGH.
Why this verdict
- Role multiplier — canonical OT controller: DVP12SE is a PLC driving physical actuators; the chain succeeds with outcome 'physical process manipulation / safety event' in 100% of deployments where the attacker can reach port 502. Per the role-multiplier rule the floor is CRITICAL for the canonical OT-control role, but exposure friction pulls the practical verdict to HIGH.
- Friction — reachability is the gate: Public Shodan exposure is well under 1% of installed base. Real risk lives on the internal/OT side where segmentation is the only control. This is the single biggest downward adjustment.
- Friction — exploitation is trivial when reachable: No skill barrier. Pymodbus one-liner. EPSS 0.0031 reflects exposure, not difficulty.
- No vendor baseline: Assessed, not upgraded/downgraded. CISA ICS advisories typically rate this class CVSS v3.1 9.8 or v4 8.7; our 8.2 reflects exposure friction discount.
- Not KEV-listed, no observed targeted campaign yet — keeps it out of immediate-emergency tier.
Why not higher?
Not CRITICAL because the vast majority of DVP12SE units are not reachable from an untrusted network — they sit behind OT firewalls, data diodes, or are simply not routed. CRITICAL is reserved for cases where the chain lands on >1% of the installed base without prior compromise, and DVP-SE public exposure is below that threshold per Shodan/Censys footprint scans.
Why not lower?
Not MEDIUM because the affected component is an OT controller: when the chain does land (insider, vendor laptop, IT→OT pivot, USB-borne implant beaconing out), the impact is physical actuation or safety. The role-multiplier hard rule forbids dropping below HIGH for a PLC with unauth write primitives, regardless of how narrow exposure is.
What to do — in priority order.
- Enforce strict network segmentation between IT and OT (Purdue Level 3.5 DMZ) — The only durable control. Place DVP12SE on a dedicated control VLAN, deny all inbound except from named engineering workstations. Deploy within 30 days per the noisgate mitigation SLA for HIGH.
- Block TCP/502 at the OT firewall except from explicit engineering source IPs — Use an allowlist of HMI/engineering-workstation IPs. Drop everything else. Deploy as a firewall rule today; verify with
nmap -p502from a non-allowlisted host within 30 days. - Deploy OT-aware monitoring (Claroty / Nozomi / Dragos / Tenable.ot) — Alert on any Modbus write function code (0x05, 0x06, 0x0F, 0x10) from a non-engineering source, and on any new client touching port 502. Stand up within 30 days.
- Disable Modbus TCP on the PLC if not in use — If the controller is read only by a single SCADA poll, consider serial-only or proxy through a Modbus gateway that enforces ACLs.
- Put the PLC keyswitch in RUN (not REMOTE/PROGRAM) where possible — Blocks program download/upload; does NOT block register writes but raises the bar against persistence.
- Plan firmware tracking via Delta PSIRT — Apply the Delta firmware update that introduces ACL/auth features as soon as it ships — within 180 days per the noisgate remediation SLA for HIGH.
- EDR on the PLC — PLCs do not run EDR. This is OT, not IT.
- Patching the corporate fleet — the vuln is in the controller firmware and protocol exposure, not in any Windows component.
- TLS / VPN to the HMI — does not stop a malicious engineering workstation or insider with valid network position.
- Modbus 'security' add-on (MBAPS / port 802) — DVP12SE does not support it; assuming you'll just 'switch ports' is wrong.
Crowdsourced verification payload.
Run this from an auditor workstation on the OT engineering VLAN (NOT from the corporate network — you should not have a route from there). Requires pip install pymodbus==3.6.*. Invoke as python check_dvp12se.py 10.20.30.41. No PLC credentials needed (that is the point). Read-only — issues only Read Device Identification and Read Holding Registers; does NOT write.
#!/usr/bin/env python3
# noisgate verification: CVE-2026-12819 Delta DVP12SE unauth Modbus TCP
# Read-only probe. Exits 2=VULNERABLE, 0=PATCHED/UNREACHABLE, 1=UNKNOWN.
import sys, socket
try:
from pymodbus.client import ModbusTcpClient
except ImportError:
print('UNKNOWN: pip install pymodbus==3.6.*')
sys.exit(1)
def probe(host, port=502, timeout=4):
# 1. TCP reachability
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
try:
s.connect((host, port))
except Exception as e:
print(f'PATCHED-OR-UNREACHABLE: {host}:{port} not reachable ({e})')
return 0
finally:
s.close()
# 2. Modbus session + unauth read
c = ModbusTcpClient(host, port=port, timeout=timeout)
if not c.connect():
print('UNKNOWN: TCP open but Modbus handshake failed')
return 1
try:
# Read 1 holding register at address 0 without any credentials
rr = c.read_holding_registers(address=0, count=1, slave=1)
if rr is None or rr.isError():
print('UNKNOWN: Modbus reply error (could be slave-ID mismatch; try --slave)')
return 1
print(f'VULNERABLE: {host}:{port} accepted unauthenticated Modbus read. Reg0={rr.registers}')
return 2
finally:
c.close()
if __name__ == '__main__':
if len(sys.argv) < 2:
print('usage: check_dvp12se.py <ip> [port]')
sys.exit(1)
host = sys.argv[1]
port = int(sys.argv[2]) if len(sys.argv) > 2 else 502
sys.exit(probe(host, port))
If you remember one thing.
Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.