Insights Blog Angriffsszenario: Umgehung der ...

Angriffsszenario: Umgehung der Zertifikatsvalidierung

Verfasst von Mahmoud Jadaan
Agenda

Serie: V2G Security Research

Agenda

Serie: V2G Security Research

Agenda

Serie: V2G Security Research

TLS ist nur so sicher wie die zugrunde liegende Zertifikatsprüfung. Die Verschlüsselung des Datenverkehrs bringt wenig, wenn ein Client gefälschte oder fehlerhafte Zertifikate akzeptiert. Jeder Angreifer, der dem EVCC ein manipuliertes Zertifikat präsentieren kann, übernimmt damit potenziell die Sitzung. In diesem Szenario untersuchen wir daher die entscheidende Frage: Welche Zertifikatsfehler erkennt das Josev-EVCC und bei welchen Mängeln würde die Verbindung dennoch akzeptiert?

Dafür führen wir drei Teil-Szenarien durch. In jedem Szenario präsentiert der SECC dem EVCC einen anderen Zertifikatstyp. Anschließend analysieren wir, wie sich dies auf den TLS-Handshake auswirkt.

Anforderung gemäß ISO 15118-2 (§ 8.7.4): Wird TLS eingesetzt, muss das EVCC das Zertifikat des SECC gegen eine vertrauenswürdige V2G-Root-CA validieren und die Sitzung abbrechen, falls die Validierung fehlschlägt. Die Spezifikation schreibt keine Hostnamenprüfung vor. Das Zertifikat muss jedoch auf eine vertrauenswürdige Root-CA zurückführbar sein und darf nicht abgelaufen sein. [05_Certifi...Validation | Word]

Testzertifikate erstellen

Für diesen Test benötigen wir zunächst drei verschiedene Zertifikatsvarianten. Geprüft werden die folgenden Szenarien:

Nr. Teil-Szenario Zertifikatsfehler des SECC Erwartetes Ergebnis
A valid Kein Fehler (von vertrauenswürdiger Research-Lab-CA signiert) TLS erfolgreich, Sitzung wird aufgebaut
B self_signed Selbstsigniert, keine Zertifikatskette zu einer CA TLS wird abgelehnt
C expired Abgelaufen (notAfter = 2. Januar 2020) TLS wird abgelehnt

Zur Vereinfachung der Zertifikatsgenerierung verwenden wir das Skript make-certs.sh. Dieses erstellt zunächst eine selbstsignierte Root-CA für das Forschungslabor und generiert anschließend drei Varianten des SECC-Zertifikats:

  • Ein gültiges Zertifikat
  • Ein abgelaufenes Zertifikat (auf Januar 2020 zurückdatiert)
  • Ein selbstsigniertes Zertifikat ohne CA-Kette
  • ca/ enthält die Root-CA
  • certs/cert_validation/valid/
  • certs/cert_validation/self_signed/
  • certs/cert_validation/expired/
  • evcc/ enthält den Truststore des EVCC
  • SECC_SCENARIO wird an beide Container übergeben und bestimmt die zu ladende Konfiguration.
  • EVCC_PKI_PATH verweist auf den Truststore des EVCC und stellt Josev die Root-CA des Forschungslabors zur Verfügung.

Zusätzlich wird ein EVCC-Truststore erzeugt, der ausschließlich die Root-CA des Forschungslabors enthält. Dadurch verwendet das EVCC in allen Tests dieselbe Vertrauensbasis.

Alle Dateien werden im Verzeichnis certs/ abgelegt.

bash make-certs.sh

Nach Abschluss der Ausführung liegt folgende Struktur vor:

Da im Truststore ausschließlich die Root-CA des Forschungslabors gespeichert ist, erfolgt die Validierung in allen Teil-Szenarien gegen dieselbe vertrauenswürdige Zertifizierungsstelle. [05_Certifi...Validation | Word]

Ausführung eines Teil-Szenarios

Jedes Szenario wird über zwei Umgebungsvariablen ausgewählt, bevor docker compose up ausgeführt wird:

In allen Konfigurationen ist SECC_ENFORCE_TLS=True gesetzt. TLS wird also immer angeboten. Der einzige Unterschied zwischen den Szenarien sind die jeweils präsentierten Zertifikate.

Für eine bessere Lesbarkeit der Wireshark-Mitschnitte verwenden alle Szenarien dieselben festen IPv6-Adressen:

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

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

Mitschnitte und Logs analysieren

Die PCAP-Dateien werden automatisch unter folgendem Pfad gespeichert:

./captures/cert_validation_<name>/

Für die Analyse des TLS-Handshakes in Wireshark empfiehlt sich folgender Filter:

tls || udp.port == 15118

Beginnen wir mit dem Positivtest: einem gültigen Zertifikat, das auf die vertrauenswürdige Root-CA zurückgeführt werden kann.

Teil-Szenario A: Gültiges Zertifikat (Referenzfall)

Bevor mögliche Angriffe getestet werden, müssen wir sicherstellen, dass die Laborumgebung mit einem gültigen Zertifikat korrekt funktioniert.

Wenn das EVCC bereits ein korrektes Zertifikat ablehnt, liegt ein Konfigurationsfehler und kein Sicherheitsbefund vor.

Die Konfiguration befindet sich in:

secc/config/cert_validation/valid.env
PROTOCOLS=ISO_15118_2
AUTH_MODES=EIM
SECC_ENFORCE_TLS=True
FREE_CHARGING_SERVICE=False
LOG_LEVEL=DEBUG
PKI_PATH=/certs/cert_validation/valid/

Vor dem Test kann das Zertifikat mit OpenSSL überprüft werden:

openssl x509 -noout -subject -issuer -dates \
  -in certs/cert_validation/valid/iso15118_2/certs/cpoCertChain.pem

Anschließend wird die Laborumgebung gestartet:

# Cleanup: stop and remove any containers and volumes from a previous run
docker compose down -v
docker rm -f attacker 2>/dev/null || true

export SECC_SCENARIO=cert_validation_valid
export EVCC_PKI_PATH=/certs/evcc
docker compose up secc evcc

Traffic-Analyse

SDP-Anfrage

Wie bereits im vorherigen Beitrag beschrieben, sendet das EVCC zunächst eine SDP-Anfrage und fordert dabei eine TLS-gesicherte Verbindung an.

SDP-Antwort

Der SECC antwortet mit seiner IP-Adresse, dem TCP-Port sowie Security = 0x00. Damit bestätigt er, dass TLS verwendet wird.

TCP Three-Way Handshake

Mit den erhaltenen Verbindungsinformationen baut das EVCC eine TCP-Verbindung auf.

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

TLS ClientHello

Direkt nach Aufbau der TCP-Verbindung sendet das EVCC seine TLS-ClientHello-Nachricht.

TLS ServerHello und Zertifikatsübermittlung

Der SECC präsentiert nun sein Zertifikat. Weitere Details finden wir im Zertifikatsrahmen in Wireshark:

TLSv1.2 Server Hello, Certificate, Server Key Exchange, Server Hello Done
Selected cipher: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Certificate: CN=secc.v2g.research (signed by V2G Root CA)

TLS ClientKeyExchange

Das EVCC validiert die Zertifikatskette erfolgreich und sendet seine Schlüsselinformationen.

Danach wird der TLS-Handshake erfolgreich abgeschlossen und alle weiteren V2G-Nachrichten werden verschlüsselt übertragen.

Auch die EVCC-Logs bestätigen den erfolgreichen Sitzungsaufbau.

Der Referenzfall ist damit bestätigt: Das EVCC akzeptiert ein korrekt ausgestelltes Zertifikat einer vertrauenswürdigen Zertifizierungsstelle. [05_Certifi...Validation | Word]

Teil-Szenario B: Selbstsigniertes Zertifikat

Kommen wir nun zum nächsten Teil-Szenario. Was passiert, wenn der SECC ein Zertifikat präsentiert, das er selbst signiert hat und das keine Zertifikatskette zu einer vertrauenswürdigen Zertifizierungsstelle besitzt?

Für diesen Test setzen wir SECC_SCENARIO=cert_validation_self_signed, sodass der SECC die Konfiguration aus secc/config/cert_validation/self_signed.env verwendet:

PROTOCOLS=ISO_15118_2
AUTH_MODES=EIM
SECC_ENFORCE_TLS=True
PKI_PATH=./certs/cert_validation/self_signed/

Am EVCC wird keine Änderung vorgenommen. Der Trust Store bleibt unter EVCC_PKI_PATH=/certs/evcc konfiguriert, sodass weiterhin ausschließlich die Root-CA des Forschungslabors als vertrauenswürdig hinterlegt ist.

Der SECC lädt nun ein Zertifikat, das mit seinem eigenen Schlüssel signiert wurde. Es existiert keinerlei CA-Kette. Wie zuvor können wir das Zertifikat vor dem Test mit OpenSSL inspizieren:

openssl x509 -noout -subject -issuer -dates \
  -in certs/cert_validation/self_signed/iso15118_2/certs/cpoCertChain.pem

Auffällig ist, dass Subject und Issuer identisch sind. Das Zertifikat wurde also nicht von einer Zertifizierungsstelle signiert, sondern signiert sich selbst.

Wir sind bereit und können die Container starten:

export SECC_SCENARIO=cert_validation_self_signed
export EVCC_PKI_PATH=/certs/evcc
docker compose up secc evcc --abort-on-container-exit

Der TLS-Handshake wird nicht erfolgreich abgeschlossen. Im EVCC-Log ist zu erkennen, dass die Ablehnung unmittelbar nach der Präsentation des Zertifikats durch den SECC erfolgt.

Auch im PCAP-Mitschnitt ist das Verhalten gut sichtbar. Nach dem TCP-Handshake sendet der SECC sein ServerHello samt Zertifikat. Das EVCC antwortet jedoch nicht mit ClientKeyExchange, sondern bestätigt zunächst den Empfang mit ACK und beendet anschließend die Verbindung per FIN/ACK.

Damit wird bestätigt, dass keinerlei ISO-15118-2-Anwendungsdaten ausgetauscht werden.

Teil-Szenario C: Abgelaufenes Zertifikat

Als Nächstes betrachten wir ein Zertifikat, das ursprünglich von unserer vertrauenswürdigen Root-CA ausgestellt wurde, dessen Gültigkeitszeitraum jedoch bereits vor sechs Jahren abgelaufen ist. Mit diesem Test prüfen wir, ob das EVCC das Feld notAfter korrekt validiert.

Die SECC-Konfiguration für dieses Szenario befindet sich in:

PROTOCOLS=ISO_15118_2
AUTH_MODES=EIM
SECC_ENFORCE_TLS=True
PKI_PATH=./certs/cert_validation/expired/

Vor dem Start prüfen wir erneut das Zertifikat:

openssl x509 -noout -subject -issuer -dates \
  -in certs/cert_validation/expired/iso15118_2/certs/cpoCertChain.pem

Im Gegensatz zum selbstsignierten Zertifikat verweist der Issuer hier auf die Root-CA des Forschungslabors. Die Zertifikatskette ist also kryptografisch gültig. Das einzige Problem ist der Gültigkeitszeitraum: Das Feld notAfter ist auf den 2. Januar 2020 gesetzt und liegt damit mehr als sechs Jahre in der Vergangenheit.

Nun setzen wir SECC_SCENARIO=cert_validation_expired, damit der SECC die entsprechende Konfiguration lädt, und konfigurieren weiterhin EVCC_PKI_PATH=/certs/evcc, sodass das EVCC dieselbe Root-CA zur Validierung verwendet. Anschließend starten wir beide Container:

export SECC_SCENARIO=cert_validation_expired
export EVCC_PKI_PATH=/certs/evcc
docker compose up secc evcc --abort-on-container-exit

Dieses Szenario ist etwas subtiler als der vorherige Fall. Das Zertifikat besitzt eine gültige Signatur unserer vertrauenswürdigen CA, sodass die Prüfung der Zertifikatskette erfolgreich verläuft. Das EVCC muss jedoch einen weiteren Schritt durchführen und die Gültigkeit des Zertifikats überprüfen. Genau an dieser Stelle schlägt der Handshake fehl. Da das Feld notAfter mehr als sechs Jahre in der Vergangenheit liegt, lehnt das EVCC die Verbindung ab.

Das bedeutet: Die Zertifikatskette ist gültig, doch das EVCC erkennt den abgelaufenen Zeitstempel korrekt.

Das Verhalten auf Netzwerkebene entspricht exakt dem aus Teil-Szenario B. Nachdem der SECC sein ServerHello und das abgelaufene Zertifikat übermittelt hat, bestätigt das EVCC den Empfang zunächst mit ACK und beendet anschließend die Verbindung unmittelbar per FIN/ACK. Es erfolgt weder ein ClientKeyExchange noch ein Austausch von Anwendungsdaten oder der Aufbau einer Sitzung.

Am Ende dieses Teil-Szenarios können wir festhalten, dass das EVCC sowohl selbstsignierte als auch abgelaufene Zertifikate korrekt zurückweist, während gültige Zertifikate einer vertrauenswürdigen Zertifizierungsstelle akzeptiert werden. Damit bestätigt sich, dass das Josev-EVCC die von ISO 15118-2 § 8.7.4 geforderte Zertifikatsvalidierung ordnungsgemäß umsetzt.

Damit endet dieser Beitrag. Im nächsten Artikel untersuchen wir den Angriff auf den Protokoll-Namespace und analysieren, ob sich V2G-Kommunikation durch ein Downgrade unterstützter Protokollversionen beeinflussen lässt.

Weiterlesen: Zur V2G-Security Research

Teil 1: EV-Laden und der V2G-Protokollstack – Bedrohungsmodell und Protokollübersicht
Teil 2: Laborumgebung – Docker-basierte V2G-Sicherheitsumgebung
Teil 3: TLS-Downgrade
Teil 4: SDP Response Spoofing
Teil 5: Zertifikatsvalidierung ← Sie befinden sich hier
Teil 6: Downgrade des Protokoll-Namespace