Someone left the drawbridge guard's key under the mat, and the drawbridge controls the water supply
CVE-2026-86325 is a stack-based buffer overflow in the account management interface of Moxa MGate protocol gateways — the MB3170, MB3270, MB3180, MB3280, MB3480, MB3660, and 5217 Series. All firmware versions up through v4.7 (MB3170/MB3270), v2.7 (MB3180), v4.6 (MB3280), v4.5 (MB3480), v3.4 (MB3660), and v1.5 (5217) are affected. The flaw stems from insufficient length validation of the account_name parameter: an attacker authenticated as a read-only user — the lowest privilege level — can submit an oversized account name that smashes the stack buffer, corrupting program execution flow. On these embedded devices (ARM/MIPS, no ASLR, no stack canaries in most firmware builds), this is effectively a code-execution primitive. The impact is memory read (credential theft), arbitrary memory write, and device denial-of-service — in a device that sits directly on the data path between PLCs/RTUs and SCADA masters.
The vendor assigned a CVSS v4.0 of 9.4 (Critical) in advisory MPSA-269540. That score is *justified*. These gateways are deployed almost exclusively in OT/ICS environments — energy substations, water treatment, chemical plants, manufacturing floors — bridging Modbus RTU/ASCII serial devices to Modbus TCP networks. Compromising one doesn't just own a single box; it gives an attacker man-in-the-middle position over industrial process data. The authentication requirement (read-only user) sounds like friction until you consider that the default admin credential is admin/moxa, many OT environments never change defaults, and a read-only account is trivially provisioned. Moxa has released patched firmware for most models, but OT patching cycles are measured in quarters, not days.
5 steps from start to impact.
Gain network access to MGate web management interface
- Network path to MGate HTTP/HTTPS interface (TCP 80 or 443)
- Attacker on OT network or device internet-exposed
- Properly segmented OT networks place these on isolated VLANs
- Industrial firewalls/DMZs should block direct internet access
- GreyNoise/Shodan show relatively few MGate-specific internet-facing instances
Moxa MGate or Server: MoxaHttp banner; network monitoring for unexpected connections to gateway management portsAuthenticate as read-only user
admin/moxa. Even if admin credentials are changed, a lower-privilege read-only account may retain factory defaults or be guessable. The read-only privilege level is sufficient — no admin access required. In OT environments, shared/default credentials are endemic; the 2021 CISA advisory for the same product family called out cleartext credential transmission (CVE-2021-4161) precisely because defaults were ubiquitous.- Valid read-only user credentials (or default creds unchanged)
- Organizations that enforce credential rotation and disable default accounts raise the bar
- HTTPS enforcement prevents credential sniffing on the wire
Submit crafted account_name to overflow stack buffer
account_name value that exceeds the expected stack buffer size. Because the firmware performs no length validation on this parameter, the oversized input overwrites the return address and adjacent stack frames. On these ARM/MIPS embedded platforms running older toolchains, there are typically no stack canaries, ASLR, or NX bits, meaning the overflow directly controls the instruction pointer without needing to bypass modern mitigations.- Access to account management function (available to read-only users)
- Knowledge of target architecture (ARM/MIPS — deterministic from model number)
- No public PoC exists yet — attacker must develop the exploit independently
- Embedded firmware reversing requires ICS-specific skill set
account_name parameterAchieve code execution on gateway
- Successful stack smash from Step 3
- Exploit payload compatible with target firmware build
- Firmware varies across models and versions, requiring per-build exploit tuning
- No known weaponized tooling (no Metasploit module, no public exploit)
Manipulate industrial process data or pivot deeper
- Sustained code execution on gateway
- Knowledge of downstream Modbus device addressing and register maps
- Process-aware attacks require domain knowledge of the specific industrial process
- Safety instrumented systems (SIS) on separate loops may catch dangerous state changes
admin/moxa credentials immediately. Remove or disable any unused read-only accounts. Enforce unique, strong passwords for all remaining accounts. This directly degrades Step 2 — without valid credentials, the authenticated overflow is unreachable. Deploy within the noisgate mitigation SLA of 3 days. Document new credentials in your OT password vault (e.g., CyberArk PAM for OT).- Generic IT firewalls at the IT/OT boundary — if the attacker is already on the OT network (insider, compromised historian, VPN pivot), perimeter firewalls don't protect the MGate's management interface. Micro-segmentation within the OT zone is required.
- Web Application Firewalls (WAFs) — MGate's embedded HTTP server does not sit behind a WAF, and deploying a reverse proxy in front of an embedded OT device is impractical and unsupported.
- Disabling the account management feature — the web interface does not support selectively disabling account management endpoints; the entire web console must be disabled, which removes the primary management plane.
- Network-level IDS without OT protocol awareness — standard Snort/Suricata rules won't catch the malformed
account_namePOST without a custom signature, and won't detect post-exploitation Modbus manipulation without protocol-specific dissectors.
The supporting signals.
| In-the-Wild Exploitation | Not observed. Not listed in CISA KEV catalog. No campaigns or threat actor TTPs documented as of 2026-10-02. Moxa MGate devices have been targeted in prior ICS campaigns (cf. ICSA-21-357-01, ICSA-16-196-02), making this product family a known target of interest for ICS-focused adversaries. |
|---|---|
| Proof-of-Concept | No public PoC. Checked pocindex.io, GitHub (no repos named CVE-2026-86325), ExploitDB, and Metasploit — no exploit code available. The vulnerability is straightforward (missing length check on a stack buffer), lowering the skill barrier for independent development by researchers or threat actors with embedded reversing capability. |
| EPSS Score | Pending. FIRST.org EPSS evaluation is in progress; CVE was just disclosed 2026-10-02. Historical EPSS for comparable Moxa gateway CVEs (e.g., CVE-2021-4161, CVSS 9.8) ranged 0.05–0.15 (low exploitation probability), reflecting the niche ICS deployment context. |
| KEV Status | Not listed in CISA Known Exploited Vulnerabilities catalog as of 2026-10-02. |
| CVSS Vector | CVSS v4.0: AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H = 9.4 Critical (vendor-assigned in MPSA-269540). CVSS v3.1 NVD score: pending analysis. The v4.0 vector reflects network-accessible, low-complexity, low-privilege attack with high impact across all CIA dimensions including downstream systems (SC/SI/SA:H). |
| Affected Versions | MB3170/MB3270: ≤ v4.7 · MB3180: ≤ v2.7 · MB3280: ≤ v4.6 · MB3480: ≤ v4.5 · MB3660: ≤ v3.4 · 5217 Series: ≤ v1.5 · All 5000/5100 Series and W5108/W5208: all versions (contact Moxa) |
| Fixed Versions | MB3170/MB3270: v4.7.1 · MB3180: v2.7.1 · MB3280: v4.6.3 · MB3480: v4.5.1 · MB3660: v3.4.5 · 5217: v1.5.5 · 5000/5100/W5108/W5208: no fix available — contact Moxa support |
| Scanning / Exposure | Shodan indexes ~4,100 Moxa-branded devices globally (banner: MoxaHttp). MGate-specific subset is smaller but non-trivial. CISA ICS advisories for this product family explicitly warn that many deployments are internet-accessible or inadequately segmented. Censys/FOFA corroborate presence of Moxa web consoles on public internet, concentrated in APAC and European industrial regions. |
| Disclosure Timeline | Reserved: 2026-09-07 · Published: 2026-10-02 · Vendor advisory: MPSA-269540 (same day) · Patches released: same day for most models |
| Reporting Researcher | Not publicly attributed in Moxa advisory MPSA-269540. Coordinated disclosure through Moxa PSIRT. |
Sources.
- Moxa Security Advisory MPSA-269540
- CISA ICS Advisory ICSA-21-357-01 (Moxa MGate)
- SecurityOnline — Moxa MGate Vulnerabilities Analysis
- Strix.ai — CVE-2026-86325 Detail Page
- Threat Radar — CVE-2026-86325 Intelligence
- TheHackerWire — CVE-2026-86325 Analysis
- Moxa MGate MB3000 Security Hardening Guide
- CSO Online — Protocol Gateway Flaws in ICS
Why this verdict
- Canonical OT/ICS device — floor is CRITICAL. The Moxa MGate is a Modbus protocol gateway deployed *exclusively* in ICS/SCADA environments (energy, water, chemical, manufacturing). By definition, ≥95% of installed units sit in high-value OT roles. The blast radius on successful exploitation is process-control manipulation, credential theft from device memory, and operational disruption — this is operational-safety-impact territory, and the verdict floor per the deployment-role rule is CRITICAL.
- Role multiplier: OT protocol gateway. The MGate bridges serial field devices (PLCs, RTUs, sensors) to the Modbus TCP network. Compromising it gives MitM control over all process data transiting the gateway — register reads, coil writes, setpoints, alarms. In energy substations or water treatment plants, this translates to physical-process manipulation. The chain succeeds in this role (read-only auth + stack overflow), and the blast radius is safety/operational impact. This is not a workstation vulnerability being speculatively mapped to a high-value role — the *only* role this device occupies is the high-value one.
- Trivial authentication barrier. The attack requires only read-only user access — the lowest privilege tier. Default credentials (
admin/moxa) are well-documented and endemic in OT environments where credential rotation is rare. This reduces the PR:L requirement to near-zero friction in practice. - Embedded platform lacks modern mitigations. MGate firmware runs on ARM/MIPS embedded Linux or RTOS without ASLR, stack canaries, or NX in most firmware builds. A stack overflow on these platforms is a reliable code-execution primitive, not a probabilistic crash. The gap between overflow and shell is minimal.
- No PoC tempers immediacy, not severity. No public exploit code exists, and EPSS is pending. This means mass exploitation is not imminent today, but the vulnerability is straightforward (missing
strlencheck) and independently discoverable. ICS-focused threat actors (CHERNOVITE/PIPEDREAM, ELECTRUM, XENOTIME) have demonstrated capability against similar embedded OT devices. - Score set at 9.2 vs. vendor's 9.4. Slight reduction acknowledges the authentication requirement (even if weak) and absence of confirmed exploitation. The delta is narrow because the auth friction is marginal in real OT deployments.
Why not higher?
A 9.4+ or 10.0 score would require either unauthenticated remote access or confirmed active exploitation. The authentication requirement — even at the read-only level — is a real gate that prevents fully anonymous exploitation. Additionally, no public PoC or in-the-wild activity has been confirmed, which tempers the immediacy of the threat. The vendor's own v4.0 score of 9.4 is the ceiling; we sit just below it.
Why not lower?
Downgrading below CRITICAL would require ignoring the deployment reality: this device exists *only* in OT/ICS environments where compromise has operational and safety consequences. The authentication friction is near-zero given default credentials. The lack of modern exploit mitigations on the embedded platform makes the stack overflow highly reliable. Network segmentation *should* limit exposure, but CISA's own advisories for this product family repeatedly warn that segmentation is inadequate in practice. A HIGH rating would understate the blast radius of a compromised protocol gateway in a safety-critical process network.
Crowdsourced verification payload.
Run this script from any workstation with network access to the MGate device's web interface. Invoke with: python3 check_cve_2026_86325.py <device_ip> [--port 443] [--https]. No authentication required — the script checks the firmware version from the device's public-facing info page. Requires Python 3.6+ with the requests library (pip install requests).
#!/usr/bin/env python3
"""CVE-2026-86325 Checker — Moxa MGate Stack Buffer Overflow
Checks firmware version against known-vulnerable and fixed versions.
Usage: python3 check_cve_2026_86325.py <host> [--port PORT] [--https]
Exit codes: 1=VULNERABLE, 0=PATCHED, 2=UNKNOWN
"""
import sys
import re
import argparse
import warnings
try:
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
warnings.simplefilter('ignore', InsecureRequestWarning)
except ImportError:
print("UNKNOWN - 'requests' library not installed. Run: pip install requests")
sys.exit(2)
# Fixed firmware versions per model (from MPSA-269540)
FIXED_VERSIONS = {
'MB3170': '4.7.1',
'MB3270': '4.7.1',
'MB3180': '2.7.1',
'MB3280': '4.6.3',
'MB3480': '4.5.1',
'MB3660': '3.4.5',
'5217': '1.5.5',
}
# Models with NO fix available
NO_FIX_MODELS = ['5000', '5100', 'W5108', 'W5208']
def parse_version(v):
"""Parse version string into tuple of ints for comparison."""
parts = re.findall(r'\d+', v)
return tuple(int(p) for p in parts)
def check_device(host, port, use_https):
scheme = 'https' if use_https else 'http'
base_url = f"{scheme}://{host}:{port}"
# Try common Moxa MGate info endpoints
endpoints = [
'/main.htm',
'/index.htm',
'/Overview.htm',
'/api/v1/device/info',
'/',
]
model = None
fw_version = None
for ep in endpoints:
try:
resp = requests.get(
f"{base_url}{ep}",
timeout=10,
verify=False,
headers={'User-Agent': 'CVE-2026-86325-Checker/1.0'}
)
text = resp.text
# Extract model
m = re.search(r'(MB3170|MB3270|MB3180|MB3280|MB3480|MB3660|MGate\s*5217|W5108|W5208|5000|5100)', text, re.IGNORECASE)
if m:
model = m.group(1).strip().replace('MGate ', '').replace('MGate', '')
# Extract firmware version patterns
fw = re.search(r'[Ff]irmware\s*[Vv]ersion[:\s]+[Vv]?(\d+\.\d+(?:\.\d+)?)', text)
if not fw:
fw = re.search(r'[Vv]ersion[:\s]+[Vv]?(\d+\.\d+(?:\.\d+)?)', text)
if fw:
fw_version = fw.group(1)
# Also check Server header
server = resp.headers.get('Server', '')
if 'Moxa' in server or 'MoxaHttp' in server:
sm = re.search(r'[Vv]?(\d+\.\d+(?:\.\d+)?)', server)
if sm and not fw_version:
fw_version = sm.group(1)
if model and fw_version:
break
except requests.exceptions.RequestException:
continue
return model, fw_version
def main():
parser = argparse.ArgumentParser(description='Check for CVE-2026-86325 (Moxa MGate Stack Overflow)')
parser.add_argument('host', help='MGate device IP or hostname')
parser.add_argument('--port', type=int, default=80, help='Web interface port (default: 80)')
parser.add_argument('--https', action='store_true', help='Use HTTPS')
args = parser.parse_args()
if args.https and args.port == 80:
args.port = 443
print(f"[*] Checking {args.host}:{args.port} for CVE-2026-86325...")
model, fw_version = check_device(args.host, args.port, args.https)
if not model and not fw_version:
print("UNKNOWN - Could not identify device model or firmware version.")
print(" Verify the host is a Moxa MGate device and the web interface is reachable.")
sys.exit(2)
print(f"[*] Detected model: {model or 'Unknown'}")
print(f"[*] Detected firmware: {fw_version or 'Unknown'}")
if not fw_version:
print("UNKNOWN - Could not determine firmware version.")
sys.exit(2)
# Check for models with no fix
if model:
for nf in NO_FIX_MODELS:
if nf.lower() in model.lower():
print(f"VULNERABLE - {model} has NO patch available for CVE-2026-86325.")
print(" Contact Moxa support. Apply compensating controls immediately.")
sys.exit(1)
# Find the matching fixed version
fixed_ver = None
matched_model = None
if model:
for key, ver in FIXED_VERSIONS.items():
if key.lower() in model.lower():
fixed_ver = ver
matched_model = key
break
if not fixed_ver:
# Try all fixed versions and find closest match
print(f"UNKNOWN - Model '{model}' not in known affected list.")
print(" Manually verify against Moxa advisory MPSA-269540.")
sys.exit(2)
current = parse_version(fw_version)
fixed = parse_version(fixed_ver)
if current >= fixed:
print(f"PATCHED - Firmware {fw_version} >= fixed version {fixed_ver} for {matched_model}.")
sys.exit(0)
else:
print(f"VULNERABLE - Firmware {fw_version} < fixed version {fixed_ver} for {matched_model}.")
print(f" Update to firmware >= {fixed_ver} to remediate CVE-2026-86325.")
sys.exit(1)
if __name__ == '__main__':
main()