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
![]()
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
![]()
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
![]()
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.
![]()
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.