Skip to content

MASTG-TEST-0052: Teste de Armazenamento de Dados Locais

Visão Geral

Este caso de teste foca em identificar dados potencialmente sensíveis armazenados por um aplicativo e verificar se estão armazenados de forma segura. As seguintes verificações devem ser realizadas:

  • Analisar o armazenamento de dados no código-fonte.
  • Certificar-se de acionar todas as funcionalidades possíveis no aplicativo (por exemplo, clicando em todos os lugares possíveis) para garantir a geração de dados.
  • Verificar todos os arquivos gerados e modificados pelo aplicativo e assegurar que o método de armazenamento seja suficientemente seguro.
    • Isso inclui NSUserDefaults, bancos de dados, Keychain, Internal Storage, External Storage, etc.

NOTA: Para conformidade com MASVS L1, é suficiente armazenar dados não criptografados no diretório de armazenamento interno do aplicativo (sandbox). Para conformidade L2, é necessária criptografia adicional usando chaves criptográficas gerenciadas com segurança no iOS Keychain. Isso inclui o uso de envelope encryption (DEK+KEK) ou métodos equivalentes.

Análise Estática

Quando você tem acesso ao código-fonte de um aplicativo iOS, identifique dados sensíveis que são salvos e processados em todo o aplicativo. Isso inclui senhas, chaves secretas e informações pessoalmente identificáveis (PII), mas também pode incluir outros dados identificados como sensíveis por regulamentações do setor, leis e políticas da empresa. Procure por esses dados sendo salvos por meio de qualquer uma das APIs de armazenamento local listadas abaixo.

Certifique-se de que dados sensíveis nunca sejam armazenados sem proteção adequada. Por exemplo, tokens de autenticação não devem ser salvos em NSUserDefaults sem criptografia adicional. Também evite armazenar chaves de criptografia em arquivos .plist, codificadas como strings no código ou geradas usando uma função ofuscação previsível ou função de derivação de chave baseada em atributos estáveis.

Dados sensíveis devem ser armazenados usando a API Keychain (que os armazena dentro do Secure Enclave) ou armazenados criptografados usando envelope encryption. Envelope encryption, ou envelope encryption, é uma construção criptográfica que usa criptografia simétrica para encapsular material de chave. Chaves de criptografia de dados (DEK) podem ser criptografadas com chaves de criptografia de chaves (KEK), que devem ser armazenadas com segurança no Keychain. DEKs criptografadas podem ser armazenadas em NSUserDefaults ou escritas em arquivos. Quando necessário, o aplicativo lê a KEK e descriptografa a DEK. Consulte a OWASP Cryptographic Storage Cheat Sheet para saber mais sobre criptografia de chaves criptográficas.

Keychain

A criptografia deve ser implementada de forma que a chave secreta seja armazenada no Keychain com configurações seguras, idealmente kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. Isso garante o uso de mecanismos de armazenamento com suporte de hardware. Certifique-se de que os AccessControlFlags estejam definidos de acordo com a política de segurança das chaves no Keychain.

Exemplos genéricos de uso do Keychain para armazenar, atualizar e excluir dados podem ser encontrados na documentação oficial da Apple. A documentação oficial da Apple também inclui um exemplo de uso de chaves protegidas por Touch ID e senha.

Sistema de Arquivos

Usando o código-fonte, examine as diferentes APIs usadas para armazenar dados localmente. Certifique-se de que quaisquer dados sejam devidamente criptografados com base em sua sensibilidade.

Análise Dinâmica

Uma maneira de determinar se informações sensíveis (como credenciais e chaves) são armazenadas de forma insegura sem aproveitar as funções nativas do iOS é analisar o diretório de dados do aplicativo. Acionar todas as funcionalidades do aplicativo antes da análise dos dados é importante porque o aplicativo pode armazenar dados sensíveis apenas após funcionalidades específicas serem acionadas. Você pode então realizar análise estática do dump de dados de acordo com palavras-chave genéricas e dados específicos do aplicativo.

As seguintes etapas podem ser usadas para determinar como o aplicativo armazena dados localmente em um dispositivo iOS com jailbreak:

  1. Acione a funcionalidade que armazena dados potencialmente sensíveis.
  2. Conecte-se ao dispositivo iOS e navegue até seu diretório Bundle (isso se aplica a versões do iOS 8.0 e superiores): /var/mobile/Containers/Data/Application/$APP_ID/
  3. Execute grep com os dados que você armazenou, por exemplo: grep -iRn "USERID".
  4. Se os dados sensíveis estiverem armazenados em texto simples, o aplicativo falha neste teste.

Você pode analisar o diretório de dados do aplicativo em um dispositivo iOS sem jailbreak usando aplicativos de terceiros, como iMazing.

  1. Acione a funcionalidade que armazena dados potencialmente sensíveis.
  2. Conecte o dispositivo iOS ao seu computador host e inicie o iMazing.
  3. Selecione "Apps", clique com o botão direito no aplicativo iOS desejado e selecione "Extract App".
  4. Navegue até o diretório de saída e localize $APP_NAME.imazing. Renomeie para $APP_NAME.zip.
  5. Descompacte o arquivo ZIP. Você pode então analisar os dados do aplicativo.

Observe que ferramentas como o iMazing não copiam dados diretamente do dispositivo. Elas tentam extrair dados dos backups que criam. Portanto, é impossível obter todos os dados do aplicativo armazenados no dispositivo iOS: nem todas as pastas estão incluídas nos backups. Use um dispositivo com jailbreak ou reempacote o aplicativo com Frida e use uma ferramenta como objection para acessar todos os dados e arquivos.

Se você adicionou a biblioteca Frida ao aplicativo e o reempacotou conforme descrito em "Análise Dinâmica em Dispositivos sem Jailbreak" (do capítulo "Tampering and Reverse Engineering on iOS"), você pode usar objection para transferir arquivos diretamente do diretório de dados do aplicativo ou ler arquivos no objection conforme explicado em Transferência de Dados entre Host e Device.

O conteúdo do Keychain pode ser extraído durante a análise dinâmica usando diferentes ferramentas, consulte Despejo de Dados da KeyChain.

O caminho para o arquivo do Keychain é

/private/var/Keychains/keychain-2.db

Em um dispositivo sem jailbreak, você pode usar objection para extrair os itens do Keychain criados e armazenados pelo aplicativo.

Análise Dinâmica com Xcode e simulador iOS

Este teste está disponível apenas no macOS, pois são necessários o Xcode e o simulador iOS.

Para testar o armazenamento local e verificar quais dados estão armazenados nele, não é obrigatório ter um dispositivo iOS. Com acesso ao código-fonte e ao Xcode, o aplicativo pode ser construído e implantado no simulador iOS. O sistema de arquivos do dispositivo atual do simulador iOS está disponível em ~/Library/Developer/CoreSimulator/Devices.

Uma vez que o aplicativo esteja em execução no simulador iOS, você pode navegar até o diretório do simulador mais recente iniciado com o seguinte comando:

$ cd ~/Library/Developer/CoreSimulator/Devices/$(
ls -alht ~/Library/Developer/CoreSimulator/Devices | head -n 2 |
awk '{print $9}' | sed -n '1!p')/data/Containers/Data/Application

O comando acima encontrará automaticamente o UUID do simulador mais recente iniciado. Agora você ainda precisa pesquisar pelo nome do seu aplicativo ou uma palavra-chave em seu aplicativo. Isso mostrará o UUID do aplicativo.

grep -iRn keyword .

Então você pode monitorar e verificar as alterações no sistema de arquivos do aplicativo e investigar se alguma informação sensível é armazenada nos arquivos durante o uso do aplicativo.

Análise Dinâmica com Objection

Você pode usar o toolkit de exploração móvel em tempo de execução objection para encontrar vulnerabilidades causadas pelo mecanismo de armazenamento de dados do aplicativo. O Objection pode ser usado sem um dispositivo com jailbreak, mas exigirá aplicar um patch no aplicativo iOS.

Leitura do Keychain

Para usar o Objection para ler o Keychain, execute o seguinte comando:

...itudehacks.DVIAswiftv2.develop on (iPhone: 13.2.3) [usb] # ios keychain dump
Note: You may be asked to authenticate using the devices passcode or TouchID
Save the output by adding `--json keychain.json` to this command
Dumping the iOS keychain...
Created                    Accessible                      ACL    Type      Account                    Service                                                        Data
-------------------------  ------------------------------  -----  --------  -------------------------  -------------------------------------------------------------  ------------------------------------
2020-02-11 13:26:52 +0000  WhenUnlocked                    None   Password  keychainValue              com.highaltitudehacks.DVIAswiftv2.develop                      mysecretpass123

Pesquisa por Cookies Binários

Aplicativos iOS frequentemente armazenam arquivos de cookies binários na sandbox do aplicativo. Cookies são arquivos binários contendo dados de cookie para WebViews do aplicativo. Você pode usar o objection para converter esses arquivos para formato JSON e inspecionar os dados.

...itudehacks.DVIAswiftv2.develop on (iPhone: 13.2.3) [usb] # ios cookies get --json
[
    {
        "domain": "highaltitudehacks.com",
        "expiresDate": "2051-09-15 07:46:43 +0000",
        "isHTTPOnly": "false",
        "isSecure": "false",
        "name": "username",
        "path": "/",
        "value": "admin123",
        "version": "0"
    }
]

Pesquisa por Arquivos de Lista de Propriedades

Aplicativos iOS frequentemente armazenam dados em arquivos de lista de propriedades (plist) que são armazenados tanto na sandbox do aplicativo quanto no pacote IPA. Às vezes, esses arquivos contêm informações sensíveis, como nomes de usuário e senhas; portanto, o conteúdo desses arquivos deve ser inspecionado durante avaliações de iOS. Use o comando ios plist cat plistFileName.plist para inspecionar o arquivo plist.

Para encontrar o arquivo userInfo.plist, use o comando env. Ele imprimirá os locais dos diretórios Library, Caches e Documents do aplicativo:

...itudehacks.DVIAswiftv2.develop on (iPhone: 13.2.3) [usb] # env
Name               Path
-----------------  -------------------------------------------------------------------------------------------
BundlePath         /private/var/containers/Bundle/Application/B2C8E457-1F0C-4DB1-8C39-04ACBFFEE7C8/DVIA-v2.app
CachesDirectory    /var/mobile/Containers/Data/Application/264C23B8-07B5-4B5D-8701-C020C301C151/Library/Caches
DocumentDirectory  /var/mobile/Containers/Data/Application/264C23B8-07B5-4B5D-8701-C020C301C151/Documents
LibraryDirectory   /var/mobile/Containers/Data/Application/264C23B8-07B5-4B5D-8701-C020C301C151/Library

Vá para o diretório Documents e liste todos os arquivos usando ls.

...itudehacks.DVIAswiftv2.develop on (iPhone: 13.2.3) [usb] # ls
NSFileType      Perms  NSFileProtection                      Read    Write    Owner         Group         Size      Creation                   Name
------------  -------  ------------------------------------  ------  -------  ------------  ------------  --------  -------------------------  ------------------------
Directory         493  n/a                                   True    True     mobile (501)  mobile (501)  192.0 B   2020-02-12 07:03:51 +0000  default.realm.management
Regular           420  CompleteUntilFirstUserAuthentication  True    True     mobile (501)  mobile (501)  16.0 KiB  2020-02-12 07:03:51 +0000  default.realm
Regular           420  CompleteUntilFirstUserAuthentication  True    True     mobile (501)  mobile (501)  1.2 KiB   2020-02-12 07:03:51 +0000  default.realm.lock
Regular           420  CompleteUntilFirstUserAuthentication  True    True     mobile (501)  mobile (501)  284.0 B   2020-05-29 18:15:23 +0000  userInfo.plist
Unknown           384  n/a                                   True    True     mobile (501)  mobile (501)  0.0 B     2020-02-12 07:03:51 +0000  default.realm.note

Readable: True  Writable: True

Execute o comando ios plist cat para inspecionar o conteúdo do arquivo userInfo.plist.

...itudehacks.DVIAswiftv2.develop on (iPhone: 13.2.3) [usb] # ios plist cat userInfo.plist
{
        password = password123;
        username = userName;
}

Pesquisa por Bancos de Dados SQLite

Aplicativos iOS normalmente usam bancos de dados SQLite para armazenar dados necessários ao aplicativo. Testadores devem verificar os valores de proteção de dados desses arquivos e seu conteúdo em busca de dados sensíveis. O Objection contém um módulo para interagir com bancos de dados SQLite. Ele permite despejar o schema, suas tabelas e consultar os registros.

...itudehacks.DVIAswiftv2.develop on (iPhone: 13.2.3) [usb] # sqlite connect Model.sqlite
Caching local copy of database file...
Downloading /var/mobile/Containers/Data/Application/264C23B8-07B5-4B5D-8701-C020C301C151/Library/Application Support/Model.sqlite to /var/folders/4m/dsg0mq_17g39g473z0996r7m0000gq/T/tmpdr_7rvxi.sqlite
Streaming file from device...
Writing bytes to destination...
Successfully downloaded /var/mobile/Containers/Data/Application/264C23B8-07B5-4B5D-8701-C020C301C151/Library/Application Support/Model.sqlite to /var/folders/4m/dsg0mq_17g39g473z0996r7m0000gq/T/tmpdr_7rvxi.sqlite
Validating SQLite database format
Connected to SQLite database at: Model.sqlite

SQLite @ Model.sqlite > .tables
+--------------+
| name         |
+--------------+
| ZUSER        |
| Z_METADATA   |
| Z_MODELCACHE |
| Z_PRIMARYKEY |
+--------------+
Time: 0.013s

SQLite @ Model.sqlite > select * from Z_PRIMARYKEY
+-------+--------+---------+-------+
| Z_ENT | Z_NAME | Z_SUPER | Z_MAX |
+-------+--------+---------+-------+
| 1     | User   | 0       | 0     |
+-------+--------+---------+-------+
1 row in set
Time: 0.013s

Pesquisa por Bancos de Dados de Cache

Por padrão, o NSURLSession armazena dados, como solicitações e respostas HTTP, no banco de dados Cache.db. Este banco de dados pode conter dados sensíveis, se tokens, nomes de usuário ou qualquer outra informação sensível tiver sido armazenada em cache. Para encontrar as informações em cache, abra o diretório de dados do aplicativo (/var/mobile/Containers/Data/Application/<UUID>) e vá para /Library/Caches/<Bundle Identifier>. O cache do WebKit também é armazenado no arquivo Cache.db. O Objection pode abrir e interagir com o banco de dados com o comando sqlite connect Cache.db, pois é um banco de dados SQLite normal.

É recomendável desabilitar o armazenamento em cache desses dados, pois eles podem conter informações sensíveis na solicitação ou resposta. A lista abaixo mostra diferentes maneiras de alcançar isso:

  1. É recomendável remover respostas em cache após o logout. Isso pode ser feito com o método fornecido pela Apple chamado removeAllCachedResponses Você pode chamar este método da seguinte forma:

URLCache.shared.removeAllCachedResponses()

Este método removerá todas as solicitações e respostas em cache do arquivo Cache.db.

  1. Se você não precisa usar a vantagem dos cookies, seria recomendável usar apenas a propriedade de configuração .ephemeral do URLSession, que desabilitará o salvamento de cookies e caches.

Documentação da Apple:

Um objeto de configuração de sessão efêmera é semelhante a uma configuração de sessão padrão (consulte default), exceto que o objeto de sessão correspondente não armazena caches, armazenamentos de credenciais ou quaisquer dados relacionados à sessão em disco. Em vez disso, os dados relacionados à sessão são armazenados na RAM. A única vez que uma sessão efêmera grava dados em disco é quando você diz a ela para gravar o conteúdo de um URL em um arquivo.

  1. O cache também pode ser desabilitado definindo a Política de Cache para .notAllowed. Isso desabilitará o armazenamento de cache de qualquer forma, seja na memória ou em disco.