Einführung
Mitte April veröffentlichten wir
Nachrichten über den Trojaner
Android.InfectionAds.1 , der mehrere kritische Sicherheitslücken im Android-Betriebssystem ausnutzte. Einer von ihnen - CVE-2017-13156 (auch als
Janus bekannt ) - ermöglicht es einem Schadprogramm, APK-Dateien zu infizieren, ohne ihre digitale Signatur zu beschädigen.
Der andere ist CVE-2017-13315. Es gewährt dem Trojaner erweiterte Berechtigungen und kann Anwendungen unabhängig installieren und deinstallieren. Eine detaillierte Analyse von
Android.InfectionAds.1 finden Sie in unserer
Virenbibliothek. Sie finden sie
hier . Wir werden uns eingehender mit der Sicherheitsanfälligkeit CVE-2017-13315 befassen und sehen, wie sie ist.
CVE-2017-13315 gehört zu der Gruppe von Sicherheitslücken, die den allgemeinen Namen EvilParcel erhalten haben. Sie befinden sich in verschiedenen Systemklassen des Android-Betriebssystems. Aufgrund von Fehlern in letzterem beim Datenaustausch zwischen Anwendungen und dem System ist es möglich, diese Daten zu ersetzen. Schädliche Programme, die EvilParcel-Schwachstellen ausnutzen, erhalten höhere Berechtigungen und können mit ihrer Hilfe Folgendes tun:
- Installieren und Deinstallieren von Anwendungen mit Berechtigungen ohne Benutzerbestätigung;
- Infizieren Sie in Verbindung mit anderen Sicherheitslücken installierte Programme auf dem Gerät und ersetzen Sie „saubere“ Originale durch infizierte Kopien.
- Setzen Sie den Bildschirmsperrcode für das Android-Gerät zurück
- Setzen Sie die PIN des Android-Gerätesperrbildschirms zurück.
Derzeit sind 7 Sicherheitslücken dieses Typs bekannt:
- CVE-2017-0806 (Fehler in der GateKeeperResponse-Klasse), veröffentlicht im Oktober 2017;
- CVE-2017-13286 (Fehler in der Klasse OutputConfiguration, veröffentlicht im April 2018;
- CVE-2017-13287 (Fehler in der VerifyCredentialResponse-Klasse), veröffentlicht im April 2018;
- CVE-2017-13288 (Fehler in der PeriodicAdvertizingReport-Klasse), veröffentlicht im April 2018;
- CVE-2017-13289 (Fehler in der ParcelableRttResults-Klasse), veröffentlicht im April 2018;
- CVE-2017-13311 (Fehler in der SparseMappingTable-Klasse), veröffentlicht im Mai 2018;
- CVE-2017-13315 (Fehler in der DcParamObject-Klasse), veröffentlicht im Mai 2018.
Alle von ihnen bedrohen Geräte mit Android OS-Versionen 5.0 - 8.1, auf denen die Sicherheitsupdates vom Mai 2018 und höher nicht installiert sind.
Voraussetzungen für EvilParcel-Sicherheitslücken
Mal sehen, wie EvilParcel-Schwachstellen entstehen. Zunächst werden wir uns einige Funktionen von Android-Anwendungen ansehen. Unter Android OS interagieren alle Programme miteinander und mit dem Betriebssystem selbst, indem sie Objekte vom Typ Intent senden und empfangen. Diese Objekte können eine beliebige Anzahl von Schlüssel-Wert-Paaren in einem Objekt vom Typ Bundle enthalten.
Beim Übertragen von Intent wird das Bundle-Objekt in ein in Parcel eingeschlossenes Byte-Array konvertiert (serialisiert), und beim Lesen von Schlüsseln und Werten aus einem serialisierten Bundle wird es automatisch deserialisiert.
Im Bundle ist die Zeichenfolge der Schlüssel, und der Wert kann fast alles sein. Zum Beispiel ein primitiver Typ, eine Zeichenfolge oder ein Container, der primitive Typen oder Zeichenfolgen enthält. Darüber hinaus kann es sich um ein Objekt vom Typ Parcelable handeln.
Auf diese Weise können Sie im Bundle ein Objekt eines beliebigen Typs platzieren, der die Parcelable-Schnittstelle implementiert. Dazu müssen Sie die Methoden writeToParcel () und createFromParcel () zum Serialisieren und Deserialisieren des Objekts implementieren.
Als gutes Beispiel erstellen wir ein einfaches serialisiertes Bundle. Schreiben wir einen Code, der drei Schlüssel-Wert-Paare in das Bundle einfügt und es serialisiert:
Bundle-Demo = neues Bundle ();
demo.putString ("String", "Hallo Welt!");
demo.putInt ("Integer", 42);
demo.putByteArray ("ByteArray", neues Byte [] {1, 2, 3, 4, 5, 6, 7, 8});
Paket Paket = Paket.obtain ();
parcel.writeBundle (Demo);
Nach dem Ausführen dieses Codes erhalten wir ein Bundle des folgenden Formulars:
Abbildung 1. Struktur eines serialisierten Bundle-Objekts.
Beachten Sie die folgenden Funktionen der Bundle-Serialisierung:
- Alle Schlüssel-Wert-Paare werden nacheinander geschrieben.
- vor jedem Wert wird sein Typ angegeben (13 für ein Byte-Array, 1 für eine Ganzzahl, 0 für eine Zeichenfolge usw.);
- vor Daten variabler Länge wird ihre Größe angegeben (Länge für die Zeichenfolge, Anzahl der Bytes für das Array);
- Alle Werte werden mit einer Ausrichtung von 4 Bytes geschrieben.
Aufgrund der Tatsache, dass alle Schlüssel und Werte im Bundle beim Zugriff auf einen Schlüssel oder den Wert eines serialisierten Bundle-Objekts nacheinander geschrieben werden, wird letzteres vollständig deserialisiert, einschließlich der Initialisierung aller darin enthaltenen Parcelable-Objekte.
Es scheint, was könnte das Problem sein? In einigen Systemklassen, die Parcelable implementieren, können die Methoden createFromParcel () und writeToParcel () auf Fehler stoßen. In diesen Klassen unterscheidet sich die Anzahl der in der Methode createFromParcel () gelesenen Bytes von der Anzahl der in der Methode writeToParcel () geschriebenen Bytes. Wenn Sie ein Objekt einer solchen Klasse im Bundle platzieren, ändern sich die Grenzen des Objekts im Bundle nach der erneuten Serialisierung. Hier werden die Bedingungen für die Ausnutzung der EvilParcel-Sicherheitsanfälligkeit geschaffen.
Hier ist ein Beispiel für eine Klasse mit einem ähnlichen Fehler:
class Demo implements Parcelable { byte[] data; public Demo() { this.data = new byte[0]; } protected Demo(Parcel in) { int length = in.readInt(); data = new byte[length]; if (length > 0) { in.readByteArray(data); } } public static final Creator<Demo> CREATOR = new Creator<Demo>() { @Override public Demo createFromParcel(Parcel in) { return new Demo(in); } }; @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeInt(data.length); parcel.writeByteArray(data); } }
Wenn die Größe des Datenarrays 0 ist, wird beim Erstellen eines Objekts in createFromParcel () ein int (4 Byte) gelesen und zwei int (8 Byte) in writeToParcel () geschrieben. Das erste int wird in einem expliziten Aufruf von writeInt geschrieben. Das zweite int wird geschrieben, wenn writeByteArray () aufgerufen wird, da die Länge des Arrays immer davor in Parcel geschrieben wird (siehe Abbildung 1).
Situationen, in denen die Größe des Datenarrays 0 beträgt, sind selten. Aber selbst wenn dies geschieht, funktioniert das Programm weiterhin, wenn jeweils nur ein Objekt in serialisierter Form übertragen wird (in unserem Beispiel das Demo-Objekt). Daher bleiben solche Fehler in der Regel unbemerkt.
Versuchen wir nun, ein Demo-Objekt mit einer Array-Länge von Null im Bundle zu platzieren:
Abbildung 2. Das Ergebnis des Hinzufügens eines Demo-Objekts mit einer Array-Länge von Null zum Bundle.
Wir serialisieren das Objekt:
Abbildung 3. Objekt nach der Serialisierung bündeln.
Versuchen wir es zu deserialisieren:
Abbildung 4. Nach dem Deserialisieren des Bundle-Objekts.
Was ist das Ergebnis? Betrachten Sie einen Paketausschnitt:
Abbildung 5. Paketstruktur nach Deserialisierung des Bündels.
Aus den Abbildungen 4 und 5 geht hervor, dass während der Deserialisierung ein int in der Methode createFromParcel anstelle von zwei zuvor geschriebenen gelesen wurde. Daher wurden alle nachfolgenden Werte aus dem Bundle nicht korrekt gelesen. Der Wert 0x0 an der Adresse 0x60 wurde als Länge des nächsten Schlüssels gelesen. Und der Wert 0x1 an der Adresse 0x64 wurde als Schlüssel gelesen. In diesem Fall wurde der Wert 0x31 an der Adresse 0x68 als Werttyp gelesen. In Parcel gibt es keine Werte, deren Typ 0x31 ist. ReadFromParcel () hat daher einen Fehler (Ausnahme) getreu gemeldet.
Wie kann dies in der Praxis genutzt werden und zu einer Sicherheitslücke werden? Mal sehen! Mit dem oben in den Parcelable-Systemklassen beschriebenen Fehler können Sie ein Bundle erstellen, das sich während der ersten und wiederholten Deserialisierung unterscheiden kann. Ändern Sie das vorherige Beispiel, um dies zu demonstrieren:
Parcel data = Parcel.obtain(); data.writeInt(3);
Dieser Code erstellt ein serialisiertes Bundle, das eine anfällige Klasse enthält. Schauen wir uns das Ergebnis der Ausführung dieses Codes an:
Abbildung 6. Erstellen eines Bundles mit einer anfälligen Klasse
Nach der ersten Deserialisierung enthält dieses Bundle die folgenden Schlüssel:
Abbildung 7. Ergebnis der Deserialisierung eines Bundles mit einer anfälligen Klasse.
Serialisieren Sie nun das resultierende Bundle erneut, deserialisieren Sie es erneut und sehen Sie sich die Liste der Schlüssel an:
Abbildung 8. Ergebnis der Re-Serialisierung und Deserialisierung eines Bundles mit einer anfälligen Klasse.
Was sehen wir? Der versteckte Schlüssel (mit dem Zeichenfolgenwert "Hi there!") Wurde im Bundle angezeigt, das vorher nicht vorhanden war. Betrachten Sie den Paketausschnitt dieses Bundles, um zu verstehen, warum dies passiert ist:
Abbildung 9. Paketstruktur des Bundle-Objekts mit der anfälligen Klasse nach zwei Serialisierungs-Deserialisierungs-Zyklen.
Hier wird das Wesen der EvilParcel-Schwachstellen klarer. Es ist möglich, ein speziell geformtes Bundle zu erstellen, das eine anfällige Klasse enthält. Durch Ändern der Grenzen dieser Klasse können Sie jedes Objekt in diesem Bundle platzieren, z. B. Intent, das erst nach der zweiten Deserialisierung im Bundle angezeigt wird. Dadurch können Sie Intent vor den Schutzmechanismen des Betriebssystems verbergen.
Operation EvilParcel
Android.InfectionAds.1 mit CVE-2017-13315 installierte und deinstallierte Programme selbstständig, ohne dass der Besitzer des infizierten Geräts eingreifen musste. Aber wie geht das?
Im Jahr 2013 wurde auch der Fehler
7699048 entdeckt, der auch als Launch AnyWhere bezeichnet wird. Es ermöglichte einer Drittanbieteranwendung, beliebige Aktivitäten im Namen des privilegierteren Benutzersystems auszuführen. Das folgende Diagramm zeigt den Wirkmechanismus:
Abbildung 10. Fehlerschema 7699048.
Mit dieser Sicherheitsanfälligkeit kann eine Exploit-Anwendung den AccountAuthenticator-Dienst implementieren, mit dem dem Betriebssystem neue Konten hinzugefügt werden sollen. Dank des Fehlers 7699048 kann der Exploit Aktivitäten ausführen, um Anwendungen zu installieren, zu deinstallieren, zu ersetzen, die PIN oder die Mustersperre zurückzusetzen und andere unangenehme Dinge zu tun.
Google hat diese Lücke geschlossen, indem es den Start beliebiger Aktivitäten über den AccountManager untersagte. Jetzt ermöglicht der AccountManager nur das Starten von Aktivitäten, die von derselben Anwendung stammen. Zu diesem Zweck wird die digitale Signatur des Programms, das den Start der Aktivität initiiert hat, überprüft und mit der Signatur der Anwendung verglichen, in der sich die gestartete Aktivität befindet. Es sieht so aus:
if (result != null && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { int authenticatorUid = Binder.getCallingUid(); long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId); int targetUid = resolveInfo.activityInfo.applicationInfo.uid; if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authenticatorUid, targetUid)) { throw new SecurityException( "Activity to be started with KEY_INTENT must " + "share Authenticator's signatures"); } } finally { Binder.restoreCallingIdentity(bid); } }
Es scheint, dass das Problem gelöst wurde, aber nicht alles hier ist so glatt. Es stellte sich heraus, dass dieses Update mithilfe der bekannten Sicherheitsanfälligkeit EvilParcel CVE-2017-13315 umgangen werden kann! Wie wir bereits wissen, überprüft das System nach dem Fixieren von Launch AnyWhere die digitale Signatur der Anwendung. Wenn diese Prüfung erfolgreich ist, wird das Bundle an IAccountManagerResponse.onResult () übergeben. Gleichzeitig wird onResult () über den IPC-Mechanismus aufgerufen, sodass das Bundle erneut serialisiert wird. In der Implementierung von onResult () geschieht Folgendes:
private class Response extends IAccountManagerResponse.Stub { public void onResult(Bundle bundle) { Intent intent = bundle.getParcelable(KEY_INTENT); if (intent != null && mActivity != null) {
Als nächstes extrahiert das Bundle den Intent-Schlüssel und die Aktivität wird ohne Prüfung gestartet. Um eine beliebige Aktivität mit Systemrechten zu starten, reicht es daher aus, das Bundle so zu erstellen, dass das Absichtsfeld bei der ersten Deserialisierung ausgeblendet wird und bei der zweiten Deserialisierung angezeigt wird. Und wie wir bereits gesehen haben, erfüllen EvilParcel-Schwachstellen genau diese Aufgabe.
Derzeit werden alle bekannten Schwachstellen dieses Typs durch Korrekturen in den anfälligen Parcelable-Klassen selbst behoben. Das Wiederauftreten gefährdeter Klassen in der Zukunft kann jedoch nicht ausgeschlossen werden. Die Implementierung des Bundles und der Mechanismus zum Hinzufügen neuer Konten sind weiterhin dieselben wie zuvor. Mit ihnen können Sie immer noch genau denselben Exploit erstellen, wenn Sie anfällige Parcelable-Klassen entdecken (oder neue). Darüber hinaus erfolgt die Implementierung dieser Klassen immer noch manuell, und der Programmierer muss die konstante Länge des serialisierten Parcelable-Objekts im Auge behalten. Und das ist ein menschlicher Faktor mit allen Konsequenzen. Wir hoffen jedoch, dass solche Fehler so gering wie möglich sind und die Schwachstellen von EvilParcel die Benutzer von Android-Geräten nicht stören.
Sie können Ihr Mobilgerät mithilfe unseres
Dr.Web Security Space- Antivirus auf EvilParcel-Schwachstellen überprüfen. Der integrierte „Sicherheitsprüfer“ berichtet über die erkannten Probleme und gibt Empfehlungen zu deren Lösung.