Skip to content

MASTG-TEST-0069: Testando Permissões de Aplicativo

Visão Geral

Análise Estática

Desde o iOS 10, estas são as principais áreas que você precisa inspecionar para permissões:

  • Strings de Finalidade no Arquivo Info.plist
  • Arquivo de Entitlements de Code Signing
  • Arquivo de Perfil de Provisionamento Incorporado
  • Entitlements Incorporados no Binário Compilado do App
  • Uso de Permissões no Código Fonte

Strings de Finalidade no Arquivo Info.plist

Se tiver o código-fonte original, você pode verificar as permissões incluídas no arquivo Info.plist:

  • Abra o projeto com o Xcode.
  • Localize e abra o arquivo Info.plist no editor padrão e procure pelas chaves que começam com "Privacy -".

Você pode alternar a visualização para exibir os valores brutos clicando com o botão direito e selecionando "Show Raw Keys/Values" (desta forma, por exemplo, "Privacy - Location When In Use Usage Description" se tornará NSLocationWhenInUseUsageDescription).

Se tiver apenas o IPA:

  • Descompacte o IPA.
  • O Info.plist está localizado em Payload/<appname>.app/Info.plist.
  • Converta-o se necessário (por exemplo, plutil -convert xml1 Info.plist), conforme explicado no capítulo "Teste Básico de Segurança iOS", seção "O Arquivo Info.plist".
  • Inspecione todas as chaves Info.plist de strings de finalidade, geralmente terminando com UsageDescription:

    <plist version="1.0">
    <dict>
        <key>NSLocationWhenInUseUsageDescription</key>
        <string>Sua localização é usada para fornecer direções passo a passo até seu destino.</string>
    

Para cada string de finalidade no arquivo Info.plist, verifique se a permissão faz sentido.

Por exemplo, imagine que as seguintes linhas foram extraídas de um arquivo Info.plist usado por um jogo de Paciência:

<key>NSHealthClinicalHealthRecordsShareUsageDescription</key>
<string>Compartilhe seus dados de saúde conosco!</string>
<key>NSCameraUsageDescription</key>
<string>Queremos acessar sua câmera</string>

Deve ser suspeito que um jogo comum de paciência solicite esse tipo de acesso a recursos, pois provavelmente não há necessidade de acessar a câmera nem aos registros de saúde do usuário.

Além de simplesmente verificar se as permissões fazem sentido, etapas de análise adicionais podem ser derivadas da análise das strings de finalidade, por exemplo, se estiverem relacionadas ao armazenamento de dados sensíveis. Por exemplo, NSPhotoLibraryUsageDescription pode ser considerada uma permissão de armazenamento que dá acesso a arquivos fora da sandbox do app e que também podem ser acessados por outros apps. Nesse caso, deve-se testar se nenhum dado sensível está sendo armazenado lá (fotos, neste caso). Para outras strings de finalidade como NSLocationAlwaysUsageDescription, também deve ser considerado se o app está armazenando esses dados com segurança. Consulte o capítulo "Teste de Armazenamento de Dados" para obter mais informações e práticas recomendadas sobre armazenamento seguro de dados sensíveis.

Arquivo de Perfil de Provisionamento Incorporado

Quando você não tem o código-fonte original, deve analisar o IPA e procurar internamente pelo perfil de provisionamento incorporado, que geralmente está localizado na pasta raiz do pacote do app (Payload/<appname>.app/) sob o nome embedded.mobileprovision.

Este arquivo não é um .plist, ele é codificado usando Cryptographic Message Syntax. No macOS, você pode inspecionar os entitlements de um perfil de provisionamento incorporado usando o seguinte comando:

security cms -D -i embedded.mobileprovision

e então procurar pela região da chave Entitlements (<key>Entitlements</key>).

Entitlements Incorporados no Binário Compilado do App

Se você tiver apenas o IPA do app ou simplesmente o app instalado em um dispositivo com jailbreak, normalmente não será capaz de encontrar arquivos .entitlements. Isso também pode ser o caso para o arquivo embedded.mobileprovision. Ainda assim, você deve ser capaz de extrair as listas de propriedades de entitlements do binário do app por conta própria (consulte Extraindo Entitlements de Binários MachO).

Uso de Permissões no Código Fonte

Após verificar o arquivo <appname>.entitlements e o arquivo Info.plist, é hora de verificar como as permissões solicitadas e as capacidades atribuídas são utilizadas. Para isso, uma revisão de código-fonte deve ser suficiente. No entanto, se você não tiver o código-fonte original, verificar o uso de permissões pode ser especialmente desafiador, pois pode ser necessário fazer engenharia reversa do app; consulte a "Análise Dinâmica" para obter mais detalhes sobre como proceder.

Ao fazer uma revisão de código-fonte, preste atenção em:

  • se as strings de finalidade no arquivo Info.plist correspondem às implementações programáticas.
  • se as capacidades registradas são usadas de forma que nenhuma informação confidencial vaze.

Os usuários podem conceder ou revogar autorização a qualquer momento através de "Configurações", portanto, os apps normalmente verificam o status de autorização de um recurso antes de acessá-lo. Isso pode ser feito usando APIs dedicadas disponíveis para muitas estruturas do sistema que fornecem acesso a recursos protegidos.

Você pode usar a Documentação do Desenvolvedor Apple como ponto de partida. Por exemplo:

  • Bluetooth: a propriedade state da classe CBCentralManager é usada para verificar o status de autorização do sistema para usar periféricos Bluetooth.
  • Localização: procure por métodos de CLLocationManager, por exemplo, locationServicesEnabled.

    func checkForLocationServices() {
        if CLLocationManager.locationServicesEnabled() {
            // Location services are available, so query the user’s location.
        } else {
            // Update your app’s UI to show that the location is unavailable.
        }
    }
    

    Consulte a Tabela1 em "Determining the Availability of Location Services" (Documentação do Desenvolvedor Apple) para uma lista completa.

Percorra o aplicativo procurando por usos dessas APIs e verifique o que acontece com os dados sensíveis que podem ser obtidos a partir delas. Por exemplo, eles podem ser armazenados ou transmitidos pela rede; se for esse o caso, a proteção adequada de dados e a segurança de transporte devem ser adicionalmente verificadas.

Análise Dinâmica

Com a ajuda da análise estática, você já deve ter uma lista das permissões incluídas e capacidades do app em uso. No entanto, como mencionado em "Inspeção de Código Fonte", identificar os dados sensíveis e as APIs relacionadas a essas permissões e capacidades do app pode ser uma tarefa desafiadora quando você não tem o código-fonte original. A análise dinâmica pode ajudar aqui, fornecendo entradas para iterar na análise estática.

Seguir uma abordagem como a apresentada abaixo deve ajudá-lo a identificar os dados sensíveis e APIs mencionados:

  1. Considere a lista de permissões/capacidades identificadas na análise estática (por exemplo, NSLocationWhenInUseUsageDescription).
  2. Mapeie-as para as APIs dedicadas disponíveis para as estruturas do sistema correspondentes (por exemplo, Core Location). Você pode usar a Documentação do Desenvolvedor Apple para isso.
  3. Rastreie classes ou métodos específicos dessas APIs (por exemplo, CLLocationManager), usando, por exemplo, frida-trace.
  4. Identifique quais métodos estão sendo realmente usados pelo app ao acessar o recurso relacionado (por exemplo, "Compartilhar sua localização").
  5. Obtenha um backtrace para esses métodos e tente construir um grafo de chamadas.

Uma vez que todos os métodos foram identificados, você pode usar esse conhecimento para fazer engenharia reversa do app e tentar descobrir como os dados estão sendo manipulados. Ao fazer isso, você pode identificar novos métodos envolvidos no processo, que pode novamente alimentar o passo 3 acima e continuar iterando entre análise estática e dinâmica.

No exemplo a seguir, usamos o Telegram para abrir a caixa de diálogo de compartilhamento de um chat e o frida-trace para identificar quais métodos estão sendo chamados.

Primeiro, iniciamos o Telegram e iniciamos um rastreamento para todos os métodos que correspondem à string "authorizationStatus" (esta é uma abordagem geral porque mais classes além do CLLocationManager implementam este método):

frida-trace -U "Telegram" -m "*[* *authorizationStatus*]"

-U conecta ao dispositivo USB. -m inclui um método Objective-C nos rastreamentos. Você pode usar um padrão glob (por exemplo, com o curinga "*", -m "*[* *authorizationStatus*]" significa "incluir qualquer método Objective-C de qualquer classe contendo 'authorizationStatus'"). Digite frida-trace -h para obter mais informações.

Agora abrimos a caixa de diálogo de compartilhamento:

Os seguintes métodos são exibidos:

  1942 ms  +[PHPhotoLibrary authorizationStatus]
  1959 ms  +[TGMediaAssetsLibrary authorizationStatusSignal]
  1959 ms     | +[TGMediaAssetsModernLibrary authorizationStatusSignal]

Se clicarmos em Localização, outro método será rastreado:

 11186 ms  +[CLLocationManager authorizationStatus]
 11186 ms     | +[CLLocationManager _authorizationStatus]
 11186 ms     |    | +[CLLocationManager _authorizationStatusForBundleIdentifier:0x0 bundle:0x0]

Use os stubs auto-gerados do frida-trace para obter mais informações, como os valores de retorno e um backtrace. Faça as seguintes modificações no arquivo JavaScript abaixo (o caminho é relativo ao diretório atual):

// __handlers__/__CLLocationManager_authorizationStatus_.js

  onEnter: function (log, args, state) {
    log("+[CLLocationManager authorizationStatus]");
    log("Called from:\n" +
        Thread.backtrace(this.context, Backtracer.ACCURATE)
        .map(DebugSymbol.fromAddress).join("\n\t") + "\n");
  },
  onLeave: function (log, retval, state) {
    console.log('RET :' + retval.toString());
  }

Clicar novamente em "Localização" revela mais informações:

  3630 ms  -[CLLocationManager init]
  3630 ms     | -[CLLocationManager initWithEffectiveBundleIdentifier:0x0 bundle:0x0]
  3634 ms  -[CLLocationManager setDelegate:0x14c9ab000]
  3641 ms  +[CLLocationManager authorizationStatus]
RET: 0x4
  3641 ms  Called from:
0x1031aa158 TelegramUI!+[TGLocationUtils requestWhenInUserLocationAuthorizationWithLocationManager:]
    0x10337e2c0 TelegramUI!-[TGLocationPickerController initWithContext:intent:]
    0x101ee93ac TelegramUI!0x1013ac

Vemos que +[CLLocationManager authorizationStatus] retornou 0x4 (CLAuthorizationStatus.authorizedWhenInUse) e foi chamado por +[TGLocationUtils requestWhenInUserLocationAuthorizationWithLocationManager:]. Como antecipamos antes, você pode usar esse tipo de informação como ponto de entrada ao fazer engenharia reversa do app e, a partir daí, obter entradas (por exemplo, nomes de classes ou métodos) para continuar alimentando a análise dinâmica.

Em seguida, há uma maneira visual de inspecionar o status de algumas permissões do app ao usar o iPhone/iPad, abrindo "Configurações" e rolando para baixo até encontrar o app desejado. Ao clicar nele, será aberta a tela "PERMITIR QUE APP_NAME ACESSE". No entanto, nem todas as permissões podem ser exibidas ainda. Você terá que acioná-las para que sejam listadas nessa tela.

Por exemplo, no exemplo anterior, a entrada "Localização" não estava sendo listada até acionarmos a caixa de diálogo de permissão pela primeira vez. Uma vez que fizemos isso, não importa se permitimos o acesso ou não, a entrada "Localização" será exibida.