Insights Blog Attack Scenario: TLS Downgrade (V2G ...

Attack Scenario: TLS Downgrade (V2G security research)

Written by Mahmoud Jadaan
Agenda

Series: V2G Security Research

Part 1 — EV Charging & the V2G Protocol Stack

Part 2 — Lab Environment: Docker-based V2G Security Lab 

Part 3 — TLS Downgrade you are here

Part 4 — SDP Response Spoofing

Part 5 — Certificate Validation

Part 6 — Protocol Namespace Downgrade 

This post is part of an ongoing series on V2G security research. The attack covered here builds directly on the lab setup from Part 2 — if you have not read it yet, it is worth skimming first.

Parts 1 and 2 of this series covered the V2G protocol stack and the Docker lab we use to run all scenarios. With the environment ready, we can now execute the first attack: forcing a V2G session to run over plaintext by stripping TLS from the SDP response.

Why TLS enforcement matters in ISO 15118-2

TLS is the backbone of security in ISO 15118-2. Every sensitive exchange — session IDs, vehicle identity, charging parameters — is supposed to travel inside an encrypted TLS tunnel. But what happens when the charging station refuses to use TLS? The standard is clear: the vehicle should walk away. In this scenario, we put that requirement to the test by setting up a rogue SECC that advertises no TLS in its SDP response, and we observe whether the EVCC holds its ground or silently caves.

Standard requirement (ISO 15118-2, §8.7.3): If the EVCC sets the Security field to 0x00 in the SDP Request, it is expressing a mandatory preference for TLS. A conformant EVCC must not establish a session with a station that responds with 0x10. Proceeding without TLS exposes the entire V2G session — session IDs, authorization data, and charging parameters — in plaintext on the network.

Steps

Step 1 - Configure the SECC to advertise no TLS in its SDP response

In our lab, the SECC represents the attacker-controlled side — either a rogue station or a misconfigured legitimate one. To simulate this scenario, we configure the SECC to advertise no TLS in its SDP response. The SECC’s behaviour is driven by an environment file (secc/config/attack2_no_tls.env) that is passed to the container at startup:

PROTOCOLS=ISO_15118_2
AUTH_MODES=EIM
SECC_ENFORCE_TLS=False # ← instructs Josev to advertise no-TLS in SDP response

One thing to keep in mind: we deliberately leave the EVCC configuration unchanged — useTls: true stays set. The EVCC has no way to tell whether it is talking to a legitimate charger or a rogue station. All it can do is check the SDP response and act on what it sees. That is exactly what we are here to test.

Step 2 — Start the SECC and EVCC containers

With the SECC configured, we bring both containers up. The EVCC has a built-in 2-second startup delay to ensure the SECC’s SDP listener is ready before the first multicast goes out:

export SECC_SCENARIO=attack2_no_tls
docker compose up secc evcc

Watch the compose logs as the containers start — you will see the SDP exchange and the full V2G message sequence scroll by in real time. The EVCC exits automatically once the session completes (SessionStop → TCP close).

Fixed addresses: since we set static MAC addresses in docker-compose.yml, the link-local addresses are always deterministic — no need to query them each run:

SECC → fe80::ff:fe00:1 (MAC 02:00:00:00:00:01)
EVCC → fe80::ff:fe00:2 (MAC 02:00:00:00:00:02)

Step 3 — Collect the traffic capture

Once both containers have exited, the PCAPs land in ./captures/attack_no_tls/. We have one file from the SECC and one from the EVCC — both recorded on the shared eth0 interface:

captures/

attack_no_tls/

secc_<timestamp>.pcap

evcc_<timestamp>.pcap

Open the SECC capture in Wireshark. To cut through the noise and focus on the V2G exchange, apply this display filter:

udp.port == 15118 || tcp.port == 15118

Traffic Analysis

Let us open the SECC capture in Wireshark and walk through what actually happened. The PCAP was recorded on the SECC container’s eth0 interface, and the two participants are:

Role IPv6 Link-Local MAC
EVCC (EV) fe80::ff:fe00:2 02:00:00:00:00:02
SECC (rogue station) fe80::ff:fe00:1 02:00:00:00:00:01

Frame 1 — SDP Request

The first thing we notice is the EVCC waking up and broadcasting its SDP request over UDP to ff02::1 (all-nodes multicast) on port 15118. Looking at the payload, the EVCC is doing exactly what the standard requires — it is explicitly asking for TLS:

Payload Type: 0x9000 (SDP request)
Security: 0x00 → "Secured with TLS"
Transport: 0x00 → TCP

V2G_Part3_Screenshot_1
SDP Request — EVCC explicitly requests TLS (Security: 0x00)

Frame 2 — SDP Response

Milliseconds later, the rogue SECC replies. This is where things get interesting. Expanding the response payload in Wireshark, we can see the SECC has answered back with Security: 0x10 — silently stripping TLS from the response:

Payload Type: 0x9001 (SDP response)
SECC IPv6: fe80::ff:fe00:1
Security: 0x10 → "No transport layer security" ← downgrade
Transport: 0x00 → TCP

V2G_Part3_Screenshot_2
SDP Response — rogue SECC downgrades security to 0x10 (no TLS)

This is the critical decision point. The EVCC just asked for TLS and received a flat “no” in return. A conformant implementation should detect this mismatch and abort the session immediately. Let us scroll down and see what it actually does.

Frame 3 — TCP Three-Way Handshake

The very next frames in Wireshark tell the story:

EVCC → SECC: SYN
SECC → EVCC: SYN-ACK
EVCC → SECC: ACK

V2G_Part3_Screenshot_3
TCP handshake completes with no TLS ClientHello following

The EVCC opened a plain TCP connection to the rogue SECC — no hesitation, no error, no retry. Crucially, scrolling past the ACK, there is no TLS ClientHello frame anywhere. The EVCC did not even attempt to negotiate TLS. It simply accepted the downgrade and moved on.

Frames 4+ — Full ISO 15118-2 session in cleartext

From this point the session runs to completion. Wireshark shows frame after frame of raw, unencrypted V2G application data — every message in the sequence is fully readable without any decryption key:

Message Sensitive data exposed
supportedAppProtocolReq / Res Protocol capabilities
SessionSetupReq / Res Session ID assigned by SECC
ServiceDiscoveryReq / Res Available services, pricing flags
PaymentServiceSelectionReq / Res Selected payment service
AuthorizationReq / Res EIM identity data
ChargeParameterDiscoveryReq / Res AC charging parameters
PowerDeliveryReq / Res Power delivery start
ChargingStatusReq / Res (loop) Meter values, energy delivered

Nothing here is encrypted. An attacker on the same PLC segment — or anyone who has captured the HomePlug GreenPHY traffic off the cable — can read every field, including the vehicle’s EIM identity and the energy metering data, with no cryptographic effort whatsoever.

V2G_Part3_Screenshot_4
Full ISO 15118-2 session visible in plaintext in Wireshark

Expected vs. Observed Behavior

Before drawing conclusions, let us lay the expected and observed outcomes side by side:

  Expected (conformant EVCC) Observed
EVCC requests TLS Security: 0x00 ✓ Security: 0x00 ✓
Station refuses TLS EVCC aborts session EVCC silently accepts
TCP connection Not established     Established on plain TCP
TLS ClientHello N/A Never sent
Session outcome No charging session Full session completed in cleartext

The EVCC played its part correctly — it sent Security: 0x00 as required. The failure is entirely in what happened next.

Finding

The EVCC does not enforce its own TLS preference. After sending Security: 0x00 in the SDP Request, it accepted a Security: 0x10 response and completed a full charging session over unencrypted TCP. This is a direct violation of ISO 15118-2 §8.7.3.

The practical risk is significant: any rogue station on the charging network — or a legitimate station that has been misconfigured — can silently strip TLS from the session. The driver and vehicle observe nothing unusual; the session completes normally while every ISO 15118-2 message, including authorization data and charging parameters, is exposed in plaintext to anyone on the same link. Combined with the SDP spoofing technique described in the next scenario, this gives an attacker full visibility into the session without needing to break any cryptography.

 

FAQ

What is a TLS downgrade attack in ISO 15118-2?

A TLS downgrade attack occurs when a charging station responds to an EVCC's TLS request with a "no TLS" advertisement in the SDP response. In this scenario, the rogue SECC returns Security: 0x10 despite the EVCC explicitly requesting Security: 0x00 ("Secured with TLS").

What does ISO 15118-2 require when a charging station refuses TLS?

According to ISO 15118-2 §8.7.3, an EVCC that requests TLS must not establish a session with a station that responds without TLS support. The expected behavior is to abort the session immediately rather than continue communication over plaintext TCP. 

How was the TLS downgrade tested in the V2G security lab?

The SECC was configured with SECC_ENFORCE_TLS=False, causing it to advertise no TLS support in its SDP response. The EVCC configuration remained unchanged and continued requesting TLS, allowing the researchers to observe how it handled the mismatch. 

What happened when the EVCC received a no-TLS SDP response?

Instead of terminating the connection, the EVCC established a standard TCP connection to the SECC and proceeded with the charging session. No TLS ClientHello was sent, and the session continued without encryption. 

Which ISO 15118-2 messages were exposed in plaintext?

Because TLS was not used, the full V2G session was visible in Wireshark. Exposed information included protocol capabilities, session IDs, available services, payment-service selections, authorization data, charging parameters, and charging-status information. 

Why is this TLS downgrade finding significant?

The researchers found that the EVCC accepted a TLS downgrade and completed the charging session over unencrypted TCP, despite having requested TLS. As a result, any party with access to the communication link could read the exchanged ISO 15118-2 data without breaking cryptographic protections.