Trust Me if You Can - Part 2

Risiken bei der Authentifizierung mit X.509v3 Zertifikaten am Beispiel eines Angriffsszenarios auf eine Webanwendung auf der Basis von Java und Tomcat.

Im ersten Teil dieses Blog Beitrags haben wir Grundlagen von X.509 Zertifikaten und hierarchische Vertrauensmodelle kritisch beleuchtet. Dieser zweite Teil von „Trust me if you can“ untersucht die Prüfung von Zertifikaten in der Praxis und zeigt einen möglichen Angriff auf einer Webanwendung, in der Java JSSE für die Authentifizierung eingesetzt wird. Im Rahmen eines Responsible Disclosure haben wir diese Angriffsmöglichkeit mit Oracle besprochen. Oracle hat daraufhin die Logik zur Zertifikatsprüfung angepasst und im Rahmen des Critical Patch Update in Januar 2020 veröffentlicht. Hier wird die Cologne Intelligence als Contributer genannt.

Versuchsaufbau Nr. 1: Sichere Kommunikation über TLS

Mit dem Versuchsaufbau klären wir, wie Zertifikatsprüfungen in Java-Komponenten ausgeführt werden und welche Prüfschritte ausgeführt werden. Um die Test-Möglichkeiten zu erweitern, erstellen wir CA-Zertifikat und signieren mit diesem zwei EE-Zertifikate, eines für Alice und eines für Bob. Wir nutzen OpenSSL, Keytool und KeystoreExplorer, um die in Tabelle 1 gelisteten Artefakte zu erzeugen.

Tabelle 1: Artefakte

File

Beschreibung

ca.crt

Ein Root-CA-Zertifikat „Test-CA“

ee.crt

Ein EE-Zertifikat „Alice“, signiert von der Test-CA

ee2.crt

Ein EE-Zertifikat „Bob“, signiert von der Test-CA

myKeystore.p12

PKCS12 Keystore, beinhaltet EE-Zertifikat „Alice“ plus private Key

myKeystore2.p12

PKCS12 Keystore, beinhaltet EE-Zertifikat „Bob“ plus private Key

myKeystore_chain.p12

PKCS12 Keystore, beinhaltet EE-Zertifikat „Alice“ plus private Key sowie das Root-CA-Zertifikat

myKeystore_compromised.p12

PKCS12 Keystore, beinhaltet EE-Zertifikat „Alice“ plus private Key, der Common Name des Zertifikates wurde manipuliert.

myTruststore.jks

Java Keystore, beinhaltet Root-CA-Zertifikat, EE-Zertifikat „Alice“, EE-Zertifikat „Bob“ (wird als Truststore verwendet)

myTruststore_ca_only.jks

Java Keystore, beinhaltet Root-CA-Zertifikat, EE-Zertifikat „Alice“, EE-Zertifikat „Bob“ (wird als Truststore verwendet)

myTruststore_ee_only.jks

Java Keystore, beinhaltet EE-Zertifikat „Alice“ (wird als Truststore verwendet)

myTruststore_ee2_only.jks

Java Keystore, beinhaltet EE-Zertifikat „Bob“ (wird als Truststore verwendet)

myTruststore_ee_all.jks

Java Keystore, beinhaltet EE-Zertifikat „Bob“ und EE-Zertifikat „Alice“ (wird als Truststore verwendet)

Um die Zertifikatsprüfung in einem realistischen und weit verbreiteten Szenario zu testen, etablieren wir die Kommunikation zwischen zwei TLS Endpunkten. Im Rahmen des TLS Handshakes stellt der Server dem Client eine Certificate Chain zur Verfügung und weist durch Signatur den Besitz des privaten Schlüssels des verwendeten EE Zertifikates nach. Es wird also ein Java-Programm als TLSServer gestartet und eines als TLSClient. Der TLS Kanal wird über die Klassen SSLServerSocket und SSLSocket aus dem Paket javax.net.ssl aufgebaut. Tabelle 2 zeigt die durchgeführten Versuche und deren Ergebnisse.

Tabelle 2: Versuche

Server Keystore

Client Truststore

Special scenario

Status

myKeystore.p12

myTruststore_ee_only.jks

 

Connected

myKeystore.p12

myTruststore_ee2_only.jks

 

Rejected

myKeystore2.p12

myTruststore_ee_only.jks

 

Connected

myKeystore2.p12

myTruststore_ee2_only.jks

 

Rejected

myKeystore.p12

myTruststore_ca_only.jks

 

Connected

myKeystore2.p12

myTruststore_ca_only.jks

 

Connected

myKeystore.p12

myTruststore.jks

 

Connected

myKeystore2.p12

myTruststore.jks

 

Connected

myKeystore.p12

myTruststore.jks

All Certs expired (in Truststore and KeyStore)

Connected

myKeystore.p12

myTruststore_ee_only.jks

All Certs expired (in Truststore and KeyStore)

Connected

myKeystore.p12

myTruststore_ca_only.jks

All Certs expired (in Truststore and KeyStore)

Rejected

myKeystore_compromised.p12

myTruststore_ee_only.jks

CN tampered with

Rejected

myKeystore_compromised.p12

myTruststore_ca_only.jks

CN tampered with

Rejected

myKeystore_compromised.p12

myTruststore.jks

CN tampered with

Rejected

myKeystore.p12

myTruststore.jks

Weak crypto
(Curl rejects)

Connected

Vorsicht bei der Konfiguration des Truststores

Grundsätzlich verhält sich der Verbindungsaufbau wie erwartet. Ist ein EE-Zertifikat im Truststore enthalten, wird der TLS-Kanal erfolgreich aufgebaut. Ist es nicht zu finden, wird ein SSL Alert erzeugt. Auch Manipulation an den Zertifikaten wird zuverlässig erkannt. Was aber auffällt, ist die unterschiedliche Reaktion des Clients je nachdem ob in dessen Truststore das EE-Zertifikat oder das ausstellende CA Zertifikat hinterlegt ist. Ein ungültiges (weil abgelaufenes) Zertifikat wurde akzeptiert, wenn dieses Zertifikat direkt im Truststore des Clients enthalten ist. In diesem Fall wird die Verbindung auch dann aufgebaut, wenn Zertifikate mit schwacher Kryptografie eingesetzt werden, die z.B. von Curl abgelehnt werden. Es empfiehlt sich also in jedem Fall, entweder bei der Zusammenstellung des Truststore ein besonderes Augenmerk auf die Algorithmen der vertrauenswürdigen Zertifikate zu legen, oder eine entsprechende Abfrage in der Prüfroutine zu ergänzen. Um dieses Verhalten genauer zu untersuchen, wird der Versuchsaufbau etwas abgewandelt.

Versuchsaufbau Nr 2: Client-Autorisierung über Zertifikate

Unser zweiter Testaufbau bildet ein fiktives Szenario ab, in dem ausschließlich authentifizierte und autorisierte Clients auf eine Webapplikation zugreifen dürfen. Als Server verwenden wir einen Tomcat 8, der einen TLS Endpunkt auf Port 8443 mit der folgenden Konfiguration bereitstellt:

<Connector
            protocol="org.apache.coyote.http11.Http11Nio2Protocol"
            sslImplementationName="org.apache.tomcat.util.net.jsse.JSSEImplementation"
            Port="8443"
            maxThreads="150"
            scheme="https"
            secure="true"
            SSLEnabled="true"
            keystoreFile="conf/myKeystore.p12"
            keystoreType="PKCS12"
            keystorePass="***"
            clientAuth="true"
            truststoreFile="conf/myTruststore_ee_all.jks"
            truststorePass="***"
            truststoretype="JKS"
            sslProtocol="TLS">
    </Connector>

In dieser Konfiguration kommt für die TLS-Terminierung und die Zertifikatsprüfung Java JSSE zum Einsatz. Installiert ist openjdk 11.0.5. auf Ubuntu. Der auf diese Weise erstellte Endpunkt akzeptiert nur Clients, die sich im Rahmen des TLS Handshakes mit einem Zertifikat ausweisen können, das im Truststore des Servers vorliegt . Zusätzlich müssen die Clients den Nachweis über den Besitz des privaten Schlüssels erbringen. Wird kein Zertifikat vorgelegt oder eines, das nicht auf den Truststore zurückgeführt werden kann, so wird der Request abgewiesen. Soll der Zugriff entzogen werden, wird das EE-Zertifikat aus dem Truststore gelöscht. Auf dem Tomcat installieren wir ein Servlet, welches die Common Names aus den Zertifikaten auswertet und den Zugriffen hierüber eine Rolle zuordnet. In unserem Beispiel vergeben wir für Bob die Rolle „Admin“ und für Alice die Rolle „User“.

Privelege Escalation Attack „Son of Alice“

Alices Zertifikat ist korrekt als EE Zertifikat konfiguriert, wir haben es ist mit den folgenden kritischen Extensions versehen:

•    Certificate Authority: No
•    Max Path Length: 0

Als Security-Testfall leiten wir nun dennoch von Alices EE-Zertifikat ein weiteres Zertifikat ab. Wir vergeben als Common Name CN=‘Bob‘ ab. Mit diesem Zertifikat als Client-Cert schicken wir einen Request gegen die wie oben beschrieben gesicherte Webanwendung. Ergebnis: Der Request wird akzeptiert und ihm wird die Rolle „Admin“ zugewiesen. Alice hat damit durch die Erzeugung eines neuen Zertifikates ihre Rechte auf „Admin“ erweitert, obwohl ihr Zertifikat als EE Zertifikat konfiguriert ist.

Dieser Angriff funktioniert, wenn der Connector für das NIO 2 Protokoll konfiguriert wird, also Java JSSE zum Einsatz kommt. Er funktioniert nicht bei Nutzung von OpenSSL durch Konfiguration des ARP Protokolls wie folgt:

   <Connector
            protocol="org.apache.coyote.http11.Http11AprProtocol"
            port="8443"
            maxThreads="150"
            scheme="https"
            secure="true"
            SSLEnabled="true"
            SSLCertificateFile="/home/ben/Certs/ee3.crt"
            SSLCertificateKeyFile="/home/ben/Certs/ee3_prk.pem"
            SSLPassword="verymuchsecurepwd"
            ClientAuth="true"
            SSLCACertificateFile="/home/ben/Certs/ee.crt" >
    </Connector>

Der Angriff funktioniert unabhängig davon, ob Alices eingesetztes EE Zertifikat wie oben beschrieben von einer CA abgeleitet wird oder selbst-signiert (self-signed) ist. Es war im vorliegenden Szenario offensichtlich keine gute Idee, den Zugriff durch die Platzierung der EE-Zertifikate im Truststore zu gewähren. In diesem Szenario konnte Alices unerlaubter Zugriff vermieden werden, wenn statt der EE-Zertifikate die CA im Truststore enthalten ist.

Die Recherche unterstreicht diese Erkenntnis. Apache warnt in der Dokumentation zu Tomcat 8 vor dem Einsatz von self-signed Zertifikaten: „While self-signed certificates can be useful for some testing scenarios, they are not suitable for any form of production use.“ (https://tomcat.apache.org/tomcat-8.0-doc/ssl-howto.html). Auch OWASP positioniert sich eindeutig: „The use of self signed certificates is never acceptable.“ (https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.md)

Lessons Learned

Grundsätzlich zeigt sich in den Versuchen die Komplexität, die mit dem Einsatz von Zertifikaten verbunden ist. Es ist daher besonders wichtig, für die Zertifikatsnutzung die eigene Zielsetzung zu definieren, ein Lösungskonzept zu entwickeln und das fertige System zu testen. Im Detail werden die folgenden „Lessons Learned“ aus den Versuchen abgeleitet:

1.    Das Team muss bei der Architekturplanung eine bewusste entscheiden, ob ein öffentlicher Truststore oder ein individueller Truststore genutzt wird.
2.    Vor dem Einsatz von self-signed-EE-Zertifikaten muss gewarnt werden. Die Versuche zeigen, dass es ein Unterschied ist, ob ein EE-Zertifikat oder dessen CA in einem Truststore hinterlegt ist:
a.    Abgelaufene EE-Zertifikate werden akzeptiert, wenn sie im Truststore hinterlegt sind. Ist nur die CA im Truststore, wird das abgelaufene EE-Zertifikat nicht akzeptiert.
b.    Unter Umständen werden von EE-Zertifikaten abgeleitete Zertifikate akzeptiert.
Auch Apache und OWASP warnen vor dem Einsatz von Self-Signed-Zertifikaten.
3.    Die Entwickler müssen wissen, welchen Truststore Java nutzt und wie der verwendete Truststore konfiguriert ist. Java fällt u.A. auf den öffentlichen Default-Truststore zurück, wenn kein individueller Truststore definiert wird oder dieser nicht gefunden wird. Es greifen weitere Automatismen die in der Java Specification definiert sind.
4.    Eigene Zertifikate zu erzeugen und sicher einzusetzen ist nicht trivial. Die Auswirkung der Attributierung sowie die Verarbeitung durch die eingesetzten Werkzeuge müssen verstanden werden, um ein sicheres System zu bauen. Sicherheitstests der entwickelten Lösung sind absolut notwendig, sie müssen in agilen Entwicklungsteams als Regression Tests umgesetzt werden

So weit, so einleuchtend. In der Realität sieht es aber häufig anders aus: Die Entwickler müssen in kurzen Zyklen fuktionale Releases erzeugen und haben meist keine Zeit für eine detaillierte Analyse von IT-Security-Fragen. Cologne Intelligence bietet Entwicklungsteams daher gezielte Unterstützung in Form des Security Champions an. Hier gibt es mehr Informationen zum Security Champion