跳过内容

MASTG-TEST-0077: 测试 WebView 协议处理程序

此测试即将更新

此测试目前可使用,但将作为新的 OWASP MASTG v2 指南 的一部分进行全面修订。

请通过提交 PR 来帮助我们:MASTG v1->v2 MASTG-TEST-0077: 测试 WebView 协议处理程序 (ios)

发送反馈

概述

静态分析

  • 测试 WebView 如何加载内容
  • 测试 WebView 文件访问
  • 检查电话号码检测

测试 WebView 如何加载内容

如果 WebView 从应用数据目录加载内容,用户不应能够更改从中加载文件的文件名或路径,也不应能够编辑加载的文件。

如果 `UIWebView` 通过已弃用的方法 `loadHTMLString:baseURL:``loadData:MIMEType:textEncodingName:baseURL:` 加载不受信任的内容,并将 `baseURL` 参数设置为 `nil` 或 `file:` 或 `applewebdata:` URL 方案,则会出现此问题。在这种情况下,为了防止未经授权访问本地文件,最佳选择是将其设置为 `about:blank`。但是,建议避免使用 `UIWebView`,而改用 `WKWebView`。

这是来自 "Where's My Browser?" 的一个易受攻击的 `UIWebView` 示例:

let scenario2HtmlPath = Bundle.main.url(forResource: "web/UIWebView/scenario2.html", withExtension: nil)
do {
    let scenario2Html = try String(contentsOf: scenario2HtmlPath!, encoding: .utf8)
    uiWebView.loadHTMLString(scenario2Html, baseURL: nil)
} catch {}

该页面使用 HTTP 从互联网加载资源,使潜在的 MITM 能够泄露本地文件中包含的秘密,例如,共享首选项中。

在使用 `WKWebView` 时,Apple 建议使用 `loadHTMLString:baseURL:``loadData:MIMEType:textEncodingName:baseURL:` 加载本地 HTML 文件,并使用 `loadRequest:` 加载 Web 内容。通常,本地文件与包括 `pathForResource:ofType:``URLForResource:withExtension:``init(contentsOf:encoding:)` 等方法组合加载。

在源代码中搜索提到的方法并检查其参数。

Objective-C 中的示例

- (void)viewDidLoad
{
    [super viewDidLoad];
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];

    self.webView = [[WKWebView alloc] initWithFrame:CGRectMake(10, 20,
        CGRectGetWidth([UIScreen mainScreen].bounds) - 20,
        CGRectGetHeight([UIScreen mainScreen].bounds) - 84) configuration:configuration];
    self.webView.navigationDelegate = self;
    [self.view addSubview:self.webView];

    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"example_file" ofType:@"html"];
    NSString *html = [NSString stringWithContentsOfFile:filePath
                                encoding:NSUTF8StringEncoding error:nil];
    [self.webView loadHTMLString:html baseURL:[NSBundle mainBundle].resourceURL];
}

来自 "Where's My Browser?" 的 Swift 示例

let scenario2HtmlPath = Bundle.main.url(forResource: "web/WKWebView/scenario2.html", withExtension: nil)
do {
    let scenario2Html = try String(contentsOf: scenario2HtmlPath!, encoding: .utf8)
    wkWebView.loadHTMLString(scenario2Html, baseURL: nil)
} catch {}

如果只有已编译的二进制文件,您还可以使用 _ rabin2_ 搜索这些方法

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

在这种情况下,建议执行动态分析以确保实际上正在使用此方法,以及从哪种 WebView 使用。此处的 `baseURL` 参数不会出现问题,因为它将被设置为“null”,但如果在使用 `UIWebView` 时未正确设置,则可能会出现问题。有关此方面的示例,请参见“检查 WebView 的加载方式”。

此外,您还应该验证应用是否正在使用方法 `loadFileURL: allowingReadAccessToURL:`。它的第一个参数是 `URL`,包含要在 WebView 中加载的 URL,它的第二个参数 `allowingReadAccessToURL` 可能包含单个文件或目录。如果包含单个文件,则该文件将可供 WebView 使用。但是,如果它包含一个目录,则该目录中的所有文件都将可供 WebView 使用。因此,值得检查一下,如果它是一个目录,则应验证其中是否找不到任何敏感数据。

来自 "Where's My Browser?" 的 Swift 示例

var scenario1Url = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0]
scenario1Url = scenario1Url.appendingPathComponent("WKWebView/scenario1.html")
wkWebView.loadFileURL(scenario1Url, allowingReadAccessTo: scenario1Url)

在这种情况下,参数 `allowingReadAccessToURL` 包含单个文件“WKWebView/scenario1.html”,这意味着 WebView 只能访问该文件。

在编译后的二进制文件中,您可以使用 _ rabin2_

$ rabin2 -zz ./WheresMyBrowser | grep -i "loadFileURL"
237 0x0002dff1 37 (4.__TEXT.__objc_methname) ascii loadFileURL:allowingReadAccessToURL:

测试 WebView 文件访问

如果您发现正在使用 `UIWebView`,则以下内容适用

  • `file://` 方案始终启用。
  • 始终启用来自 `file://` URL 的文件访问。
  • 始终启用来自 `file://` URL 的通用访问。

关于 `WKWebView`

  • `file://` 方案也始终启用,并且**无法禁用**。
  • 默认情况下,它禁用来自 `file://` URL 的文件访问,但可以启用。

以下 WebView 属性可用于配置文件访问

  • `allowFileAccessFromFileURLs`(`WKPreferences`,默认情况下为 `false`):它使在 `file://` 方案 URL 的上下文中运行的 JavaScript 能够访问来自其他 `file://` 方案 URL 的内容。
  • `allowUniversalAccessFromFileURLs`(`WKWebViewConfiguration`,默认情况下为 `false`):它使在 `file://` 方案 URL 的上下文中运行的 JavaScript 能够访问来自任何来源的内容。

例如,可以通过以下方式设置 **未记录的属性** `allowFileAccessFromFileURLs`

Objective-C

[webView.configuration.preferences setValue:@YES forKey:@"allowFileAccessFromFileURLs"];

Swift

webView.configuration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")

如果激活了一个或多个上述属性,则应确定它们是否对于应用的正常运行确实是必要的。

检查电话号码检测

在 iOS 版 Safari 中,默认情况下启用电话号码检测。但是,如果您的 HTML 页面包含可以解释为电话号码但不是电话号码的数字,或者为了防止 DOM 文档在被浏览器解析时被修改,您可能需要关闭它。要在 iOS 版 Safari 中关闭电话号码检测,请使用格式检测元标记(``)。可以在 Apple 开发人员文档 中找到这方面的示例。然后应该使用电话链接(例如,`1-408-555-5555`)来显式创建链接。

动态分析

如果可以通过 WebView 加载本地文件,则应用可能容易受到目录遍历攻击。这将允许访问沙盒中的所有文件,甚至可以逃脱沙盒,完全访问文件系统(如果设备已越狱)。因此,应验证用户是否可以更改从中加载文件的文件名或路径,以及他们是否可以编辑加载的文件。

要模拟攻击,您可以使用拦截代理将自己的 JavaScript 注入到 WebView 中,或者仅使用动态检测。尝试访问本地存储以及可能暴露于 JavaScript 上下文的任何本机方法和属性。

在实际场景中,JavaScript 只能通过永久后端跨站点脚本漏洞或 MITM 攻击注入。有关更多信息,请参见 OWASP XSS 预防速查表 和“iOS 网络通信”章节。

对于本节,我们将了解

  • 测试 WebView 如何加载内容
  • 确定 WebView 文件访问

测试 WebView 如何加载内容

如果加载了 "Where's My Browser?" 应用的 `WKWebView` 的“场景 2”,则该应用将通过调用 `URLForResource:withExtension:` 和 `loadHTMLString:baseURL` 来实现。

要快速检查这一点,您可以使用 frida-trace 并跟踪所有 `loadHTMLString` 和 `URLForResource:withExtension:` 方法。

$ frida-trace -U "Where's My Browser?"
    -m "*[WKWebView *loadHTMLString*]" -m "*[* URLForResource:withExtension:]"

 14131 ms  -[NSBundle URLForResource:0x1c0255390 withExtension:0x0]
 14131 ms  URLForResource: web/WKWebView/scenario2.html
 14131 ms  withExtension: 0x0
 14190 ms  -[WKWebView loadHTMLString:0x1c0255390 baseURL:0x0]
 14190 ms   HTMLString: <!DOCTYPE html>
    <html>
        ...
        </html>

 14190 ms  baseURL: nil

在这种情况下,`baseURL` 设置为 `nil`,这意味着有效来源为“null”。您可以通过从页面的 JavaScript 运行 `window.origin` 来获取有效来源(此应用具有允许编写和运行 JavaScript 的利用辅助程序,但您也可以实现 MITM 或仅使用 Frida 注入 JavaScript,例如通过 `WKWebView` 的 `evaluateJavaScript:completionHandler`)。

关于 `UIWebView` 的额外说明,如果您从 `UIWebView` 中检索有效来源,其中 `baseURL` 也设置为 `nil`,您将看到它未设置为“null”,而是会获得类似于以下内容:

applewebdata://5361016c-f4a0-4305-816b-65411fc1d780

此来源“applewebdata://”类似于“file://”来源,因为它不实现同源策略,并且允许访问本地文件和任何 Web 资源。在这种情况下,最好将 `baseURL` 设置为“about:blank”,这样,同源策略将阻止跨域访问。但是,此处的建议是完全避免使用 `UIWebView`,而改用 `WKWebView`。

确定 WebView 文件访问

即使没有原始源代码,您也可以快速确定应用的 WebView 是否允许文件访问以及哪种类型的文件访问。为此,只需导航到应用中的目标 WebView 并检查其所有实例,对于每个实例,获取静态分析中提到的值,即 `allowFileAccessFromFileURLs` 和 `allowUniversalAccessFromFileURLs`。这仅适用于 `WKWebView`(`UIWebVIew` 始终允许文件访问)。

我们继续使用 "Where's My Browser?" 应用和 Frida REPL 作为示例,使用以下内容扩展脚本

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

`allowFileAccessFromFileURLs` 和 `allowUniversalAccessFromFileURLs` 都设置为“0”,这意味着它们已禁用。在此应用中,我们可以转到 WebView 配置并启用 `allowFileAccessFromFileURLs`。如果我们这样做并重新运行脚本,我们将看到这次它设置为“1”

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

allowFileAccessFromFileURLs:  1