
Über die App
Am 21.03.20 wurde die COVID-19 Patienten-App im App Store und Play Store veröffentlicht. Die offizielle Beschreibung lautet: “Über die COVID-19 Patienten-App werden Patienten, die auf COVID-19 untersucht wurden, in Echtzeit über ihr Testergebnis informiert. Sobald der Befund verfügbar ist, erhält der Patient eine Push-Notification und kann das Ergebnis in der App einsehen” (Quelle). Patienten erhalten von ihrer Arztpraxis einen QR-Code (bzw. ID-Nummer) und können so auf die Ergebnisse mithilfe der App zugreifen. Die App ist eine gemeinsame Entwicklung der Telekom und BS Software Development.
Überprüfung & Schwachstelle
Also startete ich das Remote Virtual Interface Tool, dass es erlaubt Datenpaket von iOS Geräten mitzuschneiden: sudo rvictl -s [Identifier meines iPhones]
.
Anschließend startete ich den Paketmitschnitt in Wireshark auf dem neu erstellten Interface rvi0
und gab eine fiktive ID-Nummer in der App ein. Etwas überrascht stellte
ich fest, dass für den Transport der Daten TLSv1 verwendet wurde. Gerade aufgrund der Tatsache, dass es sich um eine medizinische Anwendung handelt, wurde ich skeptisch.
Ein kurzer Blick auf den SSL Report von SSL Labs bestätige den Verdacht:
Das genutzte Zertifikat war selbstsigniert, abgelaufen und ohne Domaineintrag, der Server unterstützt ausschließlich TLS 1.0 sowie veraltete und als unsicher geltende Cipher Suites.
Also richtete ich Burp Suite als Proxy meines iPhones ein - ohne die Burp Suite Root CA zu installieren - und gab erneut eine ID-Nummer ein: Tatsächlich wurden die Daten anstandslos übertragen, obwohl der App gerade ein durch Burp Suite signiertes Zertifikat präsentiert wurde. Offensichtlich wurde das Zertifikat nicht gepinnt bzw. überprüft, sodass die App jedes beliebige für den Transport akzeptiert.
Dies ermöglicht das Ausspähen & Manipulieren des Datenverkehrs für einen Angreifer mit Zugriff auf den Datenverkehr.
Die eigentliche Prüfung, ob die eingegebene ID-Nummer gültig ist, fand per HTTP Basic Auth statt: Base64-codiert wurden ID-Nummer, Geräte-ID, das Standardpasswort COVID_STANDARDPASSWORT
und einige andere Daten über die vermeintlich sichere Verbindung an den Server versandt.
Nachfolgend der Mitschnitt einer Anfrage mit ID-Nummer 123456789
aus App Version 1.1.0:
GET /datasnap/rest/DSAdmin/GetPlatformName/ HTTP/1.1
Host: 80.158.3.72
Content-Type: text/plain;charset=UTF-8
Connection: close
Accept: application/JSON
If-Modified-Since: Mon, 1 Oct 1990 05:00:00 GMT
User-Agent: Embarcadero URI Client/1.0
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Authorization: Basic UUNfQ09NTVVOSUNBVE9SMTIzNDU2Nzg5OnsidXNlck5hbWUiOiIxMjM0NTY3ODkiLCJwYXNzd29yZCI6IkNPVklEX1NUQU5EQVJEUEFTU1dPUlQiLCJkZXZpY2VJRCI6IjZiMDkyZmEyZGI1MGMzNzNlOWRmYmVkMWJkYmU5N2Y2NDYyYWU1NjQzYWNlNDNiNmM5ZGUzMTUwOGMwMWI1YmMiLCJjbGllbnRWZXJzaW9uIjoiNDFfMjAwMzI3IiwibG9naW5SZXF1ZXN0IjoiTFJfTG9naW4iLCJkZXZpY2VDb21tZW50IjoiMTIzNDU2Nzg5IiwiYW53ZW5kdW5nR1VJRCI6InsxNzZENzIyMy05RTIwLTQ1RjQtQkNDMi0yRDk1QUE4QTcyRkR9IiwicmVnaXN0ZXJEZXZpY2VJZk5vdEtub3duIjp0cnVlLCJ1c2VUb3VjaCI6ZmFsc2UsInNhdmVMb2dpbiI6ZmFsc2UsImRldmljZUdVSURPbGQiOiI2YjA5MmZhMmRiNTBjMzczZTlkZmJlZDFiZGJlOTdmNjQ2MmFlNTY0M2FjZTQzYjZjOWRlMzE1MDhjMDFiNWJjIiwiYmV0cmllYnNzeXN0ZW0iOiJJT1MiLCJuZXdVc2VyTmFtZSI6IiIsIm5ld1VzZXJNYWlsIjoiIiwibmV3VXNlclBob25lIjoiIiwibmV3VXNlckZpZWxkMSI6IiIsIm5ld1VzZXJGaWVsZDIiOiIiLCJuZXdVc2VyRmllbGQzIjoiIiwib25lVGltZVRva2VuIjoiIiwiZUVOUiI6IiIsInB1c2hzQWxsb3dlZCI6ZmFsc2V9
Decodiert man den HTTP-Header Authorization
mit Base64 ist die eingegebene ID-Nummer mehrfach lesbar:
QC_COMMUNICATOR123456789:{"userName":"123456789","password":"COVID_STANDARDPASSWORT","deviceID":"6b092fa2db50c373e9dfbed1bdbe97f6462ae5643ace43b6c9de31508c01b5bc","clientVersion":"41_200327","loginRequest":"LR_Login","deviceComment":"123456789","anwendungGUID":"{176D7223-9E20-45F4-BCC2-2D95AA8A72FD}","registerDeviceIfNotKnown":true,"useTouch":false,"saveLogin":false,"deviceGUIDOld":"6b092fa2db50c373e9dfbed1bdbe97f6462ae5643ace43b6c9de31508c01b5bc","betriebssystem":"IOS","newUserName":"","newUserMail":"","newUserPhone":"","newUserField1":"","newUserField2":"","newUserField3":"","oneTimeToken":"","eENR":"","pushsAllowed":false}
In der Anwort wird dann, neben der Information, dass das System auf win32
läuft, eine Session-ID dssession
gesetzt, die bei weiteren Anfragen zur Authentifizierung dient.
HTTP/1.1 200 OK
Connection: close
Content-Type: application/json
Content-Length: 20
Date: Tue, 31 Mar 2020 22:29:38 GMT
Pragma: dssession=998046.584957.599223,dssessionexpires=49999969
Server: DatasnapHTTPService/2011
{"result":["win32"]}
Demonstration im Video
Update
Ab Version 1.1.1 wurde dann eine lokale Syntaxprüfung des Code implementiert. Mit den Demo-Codes konnte die Untersuchung allerdings fortgesetzt werden. Ferner wurden serverseitige Änderungen vorgenommen.
Beispiel mit Demo-Code 2203-7E12D1A5-B4C9-457E-A45B-AA4825CCB69B
am 01.04.2020:
QC_COMMUNICATOR2203-7E12D1A5-B4C9-457E-A45B-AA4825CCB69B:{"userName":"2203-7E12D1A5-B4C9-457E-A45B-AA4825CCB69B","password":"COVID_STANDARDPASSWORT","deviceID":"6b092fa2db50c373e9dfbed1bdbe97f6462ae5643ace43b6c9de31508c01b5bc","clientVersion":"41_200331","loginRequest":"LR_Login","deviceComment":"2203-7E12D1A5-B4C9-457E-A45B-AA4825CCB69B","anwendungGUID":"{176D7223-9E20-45F4-BCC2-2D95AA8A72FD}","registerDeviceIfNotKnown":true,"useTouch":false,"saveLogin":false,"deviceGUIDOld":"6b092fa2db50c373e9dfbed1bdbe97f6462ae5643ace43b6c9de31508c01b5bc","betriebssystem":"IOS","newUserName":"","newUserMail":"","newUserPhone":"","newUserField1":"","newUserField2":"","newUserField3":"","oneTimeToken":"","eENR":"","pushsAllowed":false,"privacyPolicyAccepted":true,"useRandomKey":true,"clientCert":""}
Außerdem fällt auf, dass einige neue Parameter - darunter useRandomKey
hinzugefügt wurde.
In der darauf folgenden Anfrage werden User-ID, eine weitere Session-ID sowie ein Schlüssel übertragen, der, nachdem deverlängert wurde. Außerdem scheint dieser nun zufällig generiert zu werden, während dieser vorher immer AVIFMNLC122R2X2HU202ULN0022THJDL
betrug.
GET /datasnap/rest/TFFunctionsDatabase/apiGetSessionInfo/DMasdNDjd$dasxFnndMDFASMS/ HTTP/1.1
Host: 80.158.3.72
Content-Type: text/plain;charset=UTF-8
Pragma: dssession=998046.584957.599223
Accept: application/JSON
If-Modified-Since: Mon, 1 Oct 1990 05:00:00 GMT
User-Agent: Embarcadero URI Client/1.0
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: close
[...]
{"result":[{"SESSIONID":"1789","KEY":"Y0RKVFFnTnJKc1haS1dHZThtQzA1ZEkvMVlpeW0yek5nSjkwOVlWUmhSYTh5WHp6bXRJbElmVWRp\r\nY3BXUE9rTHRKbjJiV2swMWk4blA3eUpRTGsvOVE9PQ==","RIGHTS":"{\"UserState\":\"3\",\"UserID\":\"1789\",\"DeviceState\":\"4\",\"DeviceID\":\"0\",\"UserQuestion\":\"\",\"UserAnswer\":\"\",\"Kategorie\":\"\",\"Anzeigename\":\"\",\"Benutzerrechte\":[],\"Geraeterechte\":[],\"Gruppen\":[]}"}]}
Werden mehrere Anfragen mit verschlüsseltem Inhalt an den Server gestellt, auf die der Server zum aktuellen Zeitpunkt aber nicht antwortet (500 Internal Server Error
).
GET /datasnap/rest/TFFunctionsDatabase/apiGetDB/communicator/tzb%2BjBBfdD%2Bn2eIQl1oyXKFw72Fm%2F8Dy8dTFpmDx1G3w%2BUqOusWbb0tFa6C0UQGmaXJiOvxuUDdeQYnqD80OQJnGCR2peOjs3aHUsKshkRlgjdYxtzMwjjJPL5bvUiv4oq8HOJZwh5D95Upz2NqqbLJp6fZgSC8%2Bh6KfIUjAjsQ=/ HTTP/1.1
Host: 80.158.3.72
Content-Type: text/plain;charset=UTF-8
Pragma: dssession=762704.758902.473935
Accept: application/JSON
If-Modified-Since: Mon, 1 Oct 1990 05:00:00 GMT
User-Agent: Embarcadero URI Client/1.0
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: close
Anfällig für diese Schwachstelle sind Versionen der COVID-19 App kleiner oder gleich Version 1.1.2 (iOS) vom 01.04.2020 bzw. 4120.3.30 (Android) vom 31.03.2020.
Lösung
Zum aktuellen Zeitpunkt (01.04.2020, 13:00 Uhr) ist kein Update für die App verfügbar. Der Hersteller habe aber bereits eine neue Version im App Store und Play Store eingereicht und betroffenen Versionen sei der Zugriff auf den Server entzogen worden (vgl. c’t deckt auf: Corona-App der Telekom ist katastrophal unsicher).
Update vom 01.04.2020
Im Google Play Store ist nun (~18:00 Uhr) Version 4120.3.32 erschienen, mit dem Zugriff auf den Server wieder möglich ist und die genannten Sicherheitsmängel behebt. Es ist nicht länger möglich, ein selbstsigniertes Zertifikat zu verwenden. Für iOS ist bisher noch kein Update verfügbar. Bei niedrigere Versionen erscheint eine Fehlermeldung, wobei die ID-Nummer trotzdem unsicher übertragen wird. Im App Store ist nun (~23:03 Uhr) Version 1.1.2 erschienen, in der weiterhin die Nutzung mit einem selbstsignierten Zertifikat möglich ist.
Update vom 02.04.2020
Ebenfalls konnte ich in einem netten Telefonat mit dem Geschäftsführer von BS Software Development Einblicke in den Entwicklungsprozess erhalten und mich davon überzeugen, dass mit Hochdruck an der Schließung der Lücke gearbeitet werde. Außerdem erschien gegen 18:30 Uhr ein Update für iOS (Version 1.1.3), mit dem nun die Gültigkeit des Zertifikats überprüft wird, sodass die App nun nicht mehr selbstsignierten Zertifikaten vertraut. Aufgrund des fehlenden Certificate- bzw. Public Key-Pinnings lassen sich weiterhin Zertifikate, die durch Burp Suites Root-CA ausgestellt wurden und vertraut werden, unter iOS einspielen.
Datum | Status |
---|---|
31.03.2020 12:24 Uhr | Meldung an Heise c’t |
31.03.2020 13:59 Uhr | Erster Kontakt durch Herrn Eikenberg |
31.03.2020 18:09 Uhr | Herr Eikenberg bestätigt, dass Telekom und Entwicklerfirma informiert sind |
31.03.2020 ~20:30 Uhr | Durch Let´s Encrypt ausgestelltes Zertifikat ist aktiv |
31.03.2020 ~22:00 Uhr | TLS 1.0 deaktiviert & TLS 1.2 aktiviert |
01.04.2020 ~18:00 Uhr | RC4 deaktiviert & Forward Secrecy aktiviert (siehe SSL Report), Update (4120.3.32) im Play Store verfügbar |
01.04.2020 ~23:30 Uhr | Update (1.1.2) im App Store verfügbar |
02.04.2020 ~18:30 Uhr | Update (1.1.3) im App Store verfügbar |
Vielen Dank an c’t, die die Kommunikation mit Deutsche Telekom und BS Software Development übernommen haben und einen Artikel hierzu veröffentlichten sowie das Geschehen in Ausgabe 09/2020 erläuterten! Außerdem lobe ich ausgesprochen schnelle Reaktion auf der Schwachstelle durch den Hersteller.