跳过内容

MASTG-TEST-0075: 测试自定义 URL 方案

此测试即将更新

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

请提交 PR 帮助我们:MASTG v1->v2 MASTG-TEST-0075: 测试自定义 URL Scheme (ios)

发送反馈

概述

静态分析

我们可以使用静态分析来做一些事情。在接下来的章节中,我们将看到以下内容

  • 测试自定义 URL Scheme 注册
  • 测试应用查询 Scheme 注册
  • 测试 URL 处理和验证
  • 测试对其他应用的 URL 请求
  • 测试已弃用的方法

测试自定义 URL Scheme 注册

测试自定义 URL Scheme 的第一步是找出应用是否注册了任何协议处理程序。

如果您有原始源代码并想查看注册的协议处理程序,只需在 Xcode 中打开项目,转到 Info 选项卡并打开 URL Types 部分,如下面的屏幕截图所示

同样在 Xcode 中,您可以通过在应用的 Info.plist 文件中搜索 CFBundleURLTypes 键来找到它(来自 iGoat-Swift 的示例)

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>com.iGoat.myCompany</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>iGoat</string>
        </array>
    </dict>
</array>

在编译后的应用(或 IPA)中,注册的协议处理程序位于应用包根文件夹中的 Info.plist 文件中。打开它并搜索 CFBundleURLSchemes 键,如果存在,它应该包含一个字符串数组(来自 iGoat-Swift 的示例)

grep -A 5 -nri urlsch Info.plist
Info.plist:45:    <key>CFBundleURLSchemes</key>
Info.plist-46-    <array>
Info.plist-47-        <string>iGoat</string>
Info.plist-48-    </array>

注册 URL Scheme 后,其他应用可以通过创建格式正确的 URL 并使用 UIApplication openURL:options:completionHandler: 方法打开它们,从而打开注册该 Scheme 的应用并传递参数。

来自 iOS 应用编程指南 的注意事项

如果多个第三方应用注册以处理相同的 URL Scheme,则目前没有确定哪个应用将被赋予该 Scheme 的过程。

这可能会导致 URL Scheme 劫持攻击(参见 [#thiel2] 中的第 136 页)。

测试应用查询 Scheme 注册

在调用 openURL:options:completionHandler: 方法之前,应用可以调用 canOpenURL: 来验证目标应用是否可用。但是,由于恶意应用使用此方法来枚举已安装的应用,从 iOS 9.0 开始,传递给它的 URL Scheme 也必须通过LSApplicationQueriesSchemes 键添加到应用的 Info.plist 文件和一个最多包含 50 个 URL Scheme 的数组来声明。

<key>LSApplicationQueriesSchemes</key>
    <array>
        <string>url_scheme1</string>
        <string>url_scheme2</string>
    </array>

canOpenURL 将始终为未声明的 Scheme 返回 NO,无论是否安装了相应的应用。但是,此限制仅适用于 canOpenURL

openURL:options:completionHandler: 方法仍然会打开任何 URL Scheme,即使声明了 LSApplicationQueriesSchemes 数组,并根据结果返回 YES / NO

例如,Telegram 在其 Info.plist 中声明了这些查询 Scheme(除其他外)

    <key>LSApplicationQueriesSchemes</key>
    <array>
        <string>dbapi-3</string>
        <string>instagram</string>
        <string>googledrive</string>
        <string>comgooglemaps-x-callback</string>
        <string>foursquare</string>
        <string>here-location</string>
        <string>yandexmaps</string>
        <string>yandexnavi</string>
        <string>comgooglemaps</string>
        <string>youtube</string>
        <string>twitter</string>
        ...

测试 URL 处理和验证

为了确定如何构建和验证 URL 路径,如果您有原始源代码,则可以搜索以下方法

  • application:didFinishLaunchingWithOptions: 方法或 application:will-FinishLaunchingWithOptions::验证如何做出决定以及如何检索有关 URL 的信息。
  • application:openURL:options::验证资源的打开方式,即数据的解析方式,验证 选项,尤其是在调用应用 (sourceApplication) 访问时应允许还是拒绝。使用自定义 URL Scheme 时,应用可能还需要用户权限。

在 Telegram 中,您将 找到四个正在使用的不同方法

func application(_ application: UIApplication, open url: URL, sourceApplication: String?) -> Bool {
    self.openUrl(url: url)
    return true
}

func application(_ application: UIApplication, open url: URL, sourceApplication: String?,
annotation: Any) -> Bool {
    self.openUrl(url: url)
    return true
}

func application(_ app: UIApplication, open url: URL,
options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
    self.openUrl(url: url)
    return true
}

func application(_ application: UIApplication, handleOpen url: URL) -> Bool {
    self.openUrl(url: url)
    return true
}

我们可以在这里观察到一些事情

测试对其他应用的 URL 请求

方法 openURL:options:completionHandler:UIApplication已弃用的 openURL: 方法 负责打开 URL(即向其他应用发送请求/进行查询),这些 URL 可能是当前应用的本地 URL,也可能是必须由不同应用提供的 URL。如果您有原始源代码,则可以直接搜索这些方法的使用情况。

此外,如果您有兴趣了解应用是否正在查询特定服务或应用,并且如果该应用是众所周知的,您还可以在网上搜索常见的 URL Scheme 并将其包含在您的 greps 中。例如,一个 快速 Google 搜索显示

Apple Music - music:// or musics:// or audio-player-event://
Calendar - calshow:// or x-apple-calevent://
Contacts - contacts://
Diagnostics - diagnostics:// or diags://
GarageBand - garageband://
iBooks - ibooks:// or itms-books:// or itms-bookss://
Mail - message:// or mailto://emailaddress
Messages - sms://phonenumber
Notes - mobilenotes://
...

我们在 Telegram 源代码中搜索此方法,这次不使用 Xcode,而只使用 egrep

$ egrep -nr "open.*options.*completionHandler" ./Telegram-iOS/

./AppDelegate.swift:552: return UIApplication.shared.open(parsedUrl,
    options: [UIApplicationOpenURLOptionUniversalLinksOnly: true as NSNumber],
    completionHandler: { value in
./AppDelegate.swift:556: return UIApplication.shared.open(parsedUrl,
    options: [UIApplicationOpenURLOptionUniversalLinksOnly: true as NSNumber],
    completionHandler: { value in

如果我们检查结果,我们将看到 openURL:options:completionHandler: 实际上正在用于通用链接,因此我们必须继续搜索。例如,我们可以搜索 openURL(

$ egrep -nr "openURL\(" ./Telegram-iOS/

./ApplicationContext.swift:763:  UIApplication.shared.openURL(parsedUrl)
./ApplicationContext.swift:792:  UIApplication.shared.openURL(URL(
                                        string: "https://telegram.org/deactivate?phone=\(phone)")!
                                 )
./AppDelegate.swift:423:         UIApplication.shared.openURL(url)
./AppDelegate.swift:538:         UIApplication.shared.openURL(parsedUrl)
...

如果我们检查这些行,我们将看到此方法也正在用于打开“设置”或打开“App Store 页面”。

当只搜索 :// 时,我们看到

if documentUri.hasPrefix("file://"), let path = URL(string: documentUri)?.path {
if !url.hasPrefix("mt-encrypted-file://?") {
guard let dict = TGStringUtils.argumentDictionary(inUrlString: String(url[url.index(url.startIndex,
    offsetBy: "mt-encrypted-file://?".count)...])) else {
parsedUrl = URL(string: "https://\(url)")
if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(appStoreId)") {
} else if let url = url as? String, url.lowercased().hasPrefix("tg://") {
[[WKExtension sharedExtension] openSystemURL:[NSURL URLWithString:[NSString
    stringWithFormat:@"tel://%@", userHandle.data]]];

在结合两次搜索的结果并仔细检查源代码后,我们找到了以下代码

openUrl: { url in
            var parsedUrl = URL(string: url)
            if let parsed = parsedUrl {
                if parsed.scheme == nil || parsed.scheme!.isEmpty {
                    parsedUrl = URL(string: "https://\(url)")
                }
                if parsed.scheme == "tg" {
                    return
                }
            }

            if let parsedUrl = parsedUrl {
                UIApplication.shared.openURL(parsedUrl)

在打开 URL 之前,将验证该 Scheme,必要时将添加“https”,并且不会打开任何具有“tg”Scheme 的 URL。准备好后,它将使用已弃用的 openURL 方法。

如果只有已编译的应用 (IPA),您仍然可以尝试识别哪些 URL Scheme 正在用于查询其他应用

  • 检查是否声明了 LSApplicationQueriesSchemes 或搜索常见的 URL Scheme。
  • 还要使用字符串 :// 或构建正则表达式来匹配 URL,因为应用可能未声明某些 Scheme。

您可以通过首先验证应用二进制文件是否包含这些字符串来做到这一点,例如使用 unix strings 命令

strings <yourapp> | grep "someURLscheme://"

甚至更好的是,使用 radare2 的 iz/izz 命令或 rafind2,两者都会找到 unix strings 命令找不到的字符串。来自 iGoat-Swift 的示例

$ r2 -qc izz~iGoat:// iGoat-Swift
37436 0x001ee610 0x001ee610  23  24 (4.__TEXT.__cstring) ascii iGoat://?contactNumber=

测试已弃用的方法

搜索已弃用的方法,例如

例如,使用 rabin2 我们找到了这三个

$ rabin2 -zzq Telegram\ X.app/Telegram\ X | grep -i "openurl"

0x1000d9e90 31 30 UIApplicationOpenURLOptionsKey
0x1000dee3f 50 49 application:openURL:sourceApplication:annotation:
0x1000dee71 29 28 application:openURL:options:
0x1000dee8e 27 26 application:handleOpenURL:
0x1000df2c9 9 8 openURL:
0x1000df766 12 11 canOpenURL:
0x1000df772 35 34 openURL:options:completionHandler:
...

动态分析

一旦确定了应用已注册的自定义 URL Scheme,就可以使用多种方法来测试它们

  • 执行 URL 请求
  • 识别并 Hook URL 处理方法
  • 测试 URL Scheme 来源验证
  • 模糊测试 URL Scheme

执行 URL 请求

使用 Safari

要快速测试一个 URL Scheme,您可以在 Safari 上打开 URL 并观察应用的表现。例如,如果您在 Safari 的地址栏中写入 tel://123456789,则会出现一个弹出窗口,其中包含电话号码以及“取消”和“呼叫”选项。如果您按下“呼叫”,它将打开电话应用并直接拨打电话。

您可能还已经知道触发自定义 URL Scheme 的页面,您可以像平常一样导航到这些页面,Safari 会在找到自定义 URL Scheme 时自动询问。

使用备忘录应用

如“触发通用链接”中已经看到的,您可以使用备忘录应用并长按您编写的链接,以便测试自定义 URL Scheme。请记住退出编辑模式才能打开它们。请注意,只有在安装了应用后,您才能单击或长按包含自定义 URL Scheme 的链接,如果没有安装应用,它们将不会突出显示为可单击链接

使用 Frida

如果您只是希望应用打开 URL Scheme,则可以使用 Frida 来做到这一点。使用 iGoat-Swift 的示例

$ frida -U iGoat-Swift

[iPhone::iGoat-Swift]-> function openURL(url) {
                            var UIApplication = ObjC.classes.UIApplication.sharedApplication();
                            var toOpen = ObjC.classes.NSURL.URLWithString_(url);
                            return UIApplication.openURL_(toOpen);
                        }
[iPhone::iGoat-Swift]-> openURL("tel://234234234")
true

在这个来自 Frida CodeShare 的示例中,作者使用非公共 API LSApplication Workspace.openSensitiveURL:withOptions: 打开 URL(来自 SpringBoard 应用)

function openURL(url) {
    var w = ObjC.classes.LSApplicationWorkspace.defaultWorkspace();
    var toOpen = ObjC.classes.NSURL.URLWithString_(url);
    return w.openSensitiveURL_withOptions_(toOpen, null);
}

请注意,App Store 上不允许使用非公共 API,这就是我们甚至不测试它们的原因,但我们允许将它们用于我们的动态分析。

识别并 Hook URL 处理方法

如果您无法查看原始源代码,则必须自己找出应用使用哪种方法来处理其收到的 URL Scheme 请求。您无法知道它是 Objective-C 方法还是 Swift 方法,甚至无法知道应用是否正在使用已弃用的方法。

为此,我们将使用来自 Frida CodeShare 的 ObjC 方法观察器,这是一个非常方便的脚本,只需提供一个简单的模式,即可快速观察任何方法或类的集合。

在这种情况下,我们对来自包含“openURL”的 iGoat-Swift 应用的所有方法感兴趣,因此我们的模式将是 *[* *openURL*]

  • 第一个星号将匹配所有实例 - 和类 + 方法。
  • 第二个星号将匹配所有 Objective-C 类。
  • 第三个和第四个星号允许匹配包含字符串 openURL 的任何方法。
$ frida -U iGoat-Swift --codeshare mrmacete/objc-method-observer

[iPhone::iGoat-Swift]-> observeSomething("*[* *openURL*]");
Observing  -[_UIDICActivityItemProvider activityViewController:openURLAnnotationForActivityType:]
Observing  -[CNQuickActionsManager _openURL:]
Observing  -[SUClientController openURL:]
Observing  -[SUClientController openURL:inClientWithIdentifier:]
Observing  -[FBSSystemService openURL:application:options:clientPort:withResult:]
Observing  -[iGoat_Swift.AppDelegate application:openURL:options:]
Observing  -[PrefsUILinkLabel openURL:]
Observing  -[UIApplication openURL:]
Observing  -[UIApplication _openURL:]
Observing  -[UIApplication openURL:options:completionHandler:]
Observing  -[UIApplication openURL:withCompletionHandler:]
Observing  -[UIApplication _openURL:originatingView:completionHandler:]
Observing  -[SUApplication application:openURL:sourceApplication:annotation:]
...

该列表非常长,其中包括我们已经提到的方法。如果我们现在触发一个 URL Scheme,例如来自 Safari 的 "igoat://",并接受在应用中打开它,我们将看到以下内容

[iPhone::iGoat-Swift]-> (0x1c4038280)  -[iGoat_Swift.AppDelegate application:openURL:options:]
application: <UIApplication: 0x101d0fad0>
openURL: igoat://
options: {
    UIApplicationOpenURLOptionsOpenInPlaceKey = 0;
    UIApplicationOpenURLOptionsSourceApplicationKey = "com.apple.mobilesafari";
}
0x18b5030d8 UIKit!__58-[UIApplication _applicationOpenURLAction:payload:origin:]_block_invoke
0x18b502a94 UIKit!-[UIApplication _applicationOpenURLAction:payload:origin:]
...
0x1817e1048 libdispatch.dylib!_dispatch_client_callout
0x1817e86c8 libdispatch.dylib!_dispatch_block_invoke_direct$VARIANT$mp
0x18453d9f4 FrontBoardServices!__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__
0x18453d698 FrontBoardServices!-[FBSSerialQueue _performNext]
RET: 0x1

现在我们知道

  • 调用了 -[iGoat_Swift.AppDelegate application:openURL:options:] 方法。正如我们之前所看到的,这是推荐的方法,并且没有被弃用。
  • 它接收我们的 URL 作为参数:igoat://
  • 我们还可以验证源应用:com.apple.mobilesafari
  • 我们还可以从 -[UIApplication _applicationOpenURLAction:payload:origin:] 中知道它是从哪里调用的。
  • 该方法返回 0x1,表示 YES委托成功处理了请求)。

调用成功,我们现在看到 iGoat-Swift 应用已打开

请注意,如果我们查看屏幕截图的左上角,我们还可以看到调用者(源应用)是 Safari。

查看在此过程中调用的其他方法也很有趣。为了稍微改变结果,我们将从 iGoat-Swift 应用本身调用相同的 URL Scheme。我们将再次使用 ObjC 方法观察器和 Frida REPL

$ frida -U iGoat-Swift --codeshare mrmacete/objc-method-observer

[iPhone::iGoat-Swift]-> function openURL(url) {
                            var UIApplication = ObjC.classes.UIApplication.sharedApplication();
                            var toOpen = ObjC.classes.NSURL.URLWithString_(url);
                            return UIApplication.openURL_(toOpen);
                        }

[iPhone::iGoat-Swift]-> observeSomething("*[* *openURL*]");
[iPhone::iGoat-Swift]-> openURL("iGoat://?contactNumber=123456789&message=hola")

(0x1c409e460)  -[__NSXPCInterfaceProxy__LSDOpenProtocol openURL:options:completionHandler:]
openURL: iGoat://?contactNumber=123456789&message=hola
options: nil
completionHandler: <__NSStackBlock__: 0x16fc89c38>
0x183befbec MobileCoreServices!-[LSApplicationWorkspace openURL:withOptions:error:]
0x10ba6400c
...
RET: nil

...

(0x101d0fad0)  -[UIApplication openURL:]
openURL: iGoat://?contactNumber=123456789&message=hola
0x10a610044
...
RET: 0x1

true
(0x1c4038280)  -[iGoat_Swift.AppDelegate application:openURL:options:]
application: <UIApplication: 0x101d0fad0>
openURL: iGoat://?contactNumber=123456789&message=hola
options: {
    UIApplicationOpenURLOptionsOpenInPlaceKey = 0;
    UIApplicationOpenURLOptionsSourceApplicationKey = "OWASP.iGoat-Swift";
}
0x18b5030d8 UIKit!__58-[UIApplication _applicationOpenURLAction:payload:origin:]_block_invoke
0x18b502a94 UIKit!-[UIApplication _applicationOpenURLAction:payload:origin:]
...
RET: 0x1

为了更好地可读性,输出了被截断。这次您会看到 UIApplicationOpenURLOptionsSourceApplicationKey 已更改为 OWASP.iGoat-Swift,这是有道理的。此外,还调用了很长的 openURL-like 方法列表。考虑到此信息对于某些场景可能非常有用,因为它将帮助您决定下一步要做什么,例如,下一步将 Hook 或篡改哪种方法。

您现在可以测试单击页面上包含的链接时的相同情况。Safari 将识别并处理 URL Scheme 并选择要执行的操作。打开此链接“https://telegram.me/fridadotre”将触发此行为。

首先,我们让 frida-trace 为我们生成桩代码

$ frida-trace -U Telegram -m "*[* *restorationHandler*]" -i "*open*Url*"
    -m "*[* *application*URL*]" -m "*[* openURL]"

...
7310 ms  -[UIApplication _applicationOpenURLAction: 0x1c44ff900 payload: 0x10c5ee4c0 origin: 0x0]
7311 ms     | -[AppDelegate application: 0x105a59980 openURL: 0x1c46ebb80 options: 0x1c0e222c0]
7312 ms     | $S10TelegramUI15openExternalUrl7account7context3url05forceD016presentationData
            18applicationContext20navigationController12dismissInputy0A4Core7AccountC_AA14Open
            URLContextOSSSbAA012PresentationK0CAA0a11ApplicationM0C7Display010NavigationO0CSgyyctF()

现在我们可以简单地手动修改我们感兴趣的桩代码

  • Objective-C 方法 application:openURL:options:

    // __handlers__/__AppDelegate_application_openUR_3679fadc.js
    
    onEnter: function (log, args, state) {
        log("-[AppDelegate application: " + args[2] +
                    " openURL: " + args[3] + " options: " + args[4] + "]");
        log("\tapplication :" + ObjC.Object(args[2]).toString());
        log("\topenURL :" + ObjC.Object(args[3]).toString());
        log("\toptions :" + ObjC.Object(args[4]).toString());
    },
    
  • Swift 方法 $S10TelegramUI15openExternalUrl...

    // __handlers__/TelegramUI/_S10TelegramUI15openExternalUrl7_b1a3234e.js
    
    onEnter: function (log, args, state) {
    
        log("TelegramUI.openExternalUrl(account, url, presentationData," +
                    "applicationContext, navigationController, dismissInput)");
        log("\taccount: " + ObjC.Object(args[1]).toString());
        log("\turl: " + ObjC.Object(args[2]).toString());
        log("\tpresentationData: " + args[3]);
        log("\tapplicationContext: " + ObjC.Object(args[4]).toString());
        log("\tnavigationController: " + ObjC.Object(args[5]).toString());
    },
    

下次我们运行它时,我们会看到以下输出

$ frida-trace -U Telegram -m "*[* *restorationHandler*]" -i "*open*Url*"
    -m "*[* *application*URL*]" -m "*[* openURL]"

  8144 ms  -[UIApplication _applicationOpenURLAction: 0x1c44ff900 payload: 0x10c5ee4c0 origin: 0x0]
  8145 ms     | -[AppDelegate application: 0x105a59980 openURL: 0x1c46ebb80 options: 0x1c0e222c0]
  8145 ms     |     application: <Application: 0x105a59980>
  8145 ms     |     openURL: tg://resolve?domain=fridadotre
  8145 ms     |     options :{
                        UIApplicationOpenURLOptionsOpenInPlaceKey = 0;
                        UIApplicationOpenURLOptionsSourceApplicationKey = "com.apple.mobilesafari";
                    }
  8269 ms     |    | TelegramUI.openExternalUrl(account, url, presentationData,
                                        applicationContext, navigationController, dismissInput)
  8269 ms     |    |    account: nil
  8269 ms     |    |    url: tg://resolve?domain=fridadotre
  8269 ms     |    |    presentationData: 0x1c4c51741
  8269 ms     |    |    applicationContext: nil
  8269 ms     |    |    navigationController: TelegramUI.PresentationData
  8274 ms     | -[UIApplication applicationOpenURL:0x1c46ebb80]

您可以在其中观察到以下内容

  • 它按照预期从应用委托中调用 application:openURL:options:
  • 源应用是 Safari ("com.apple.mobilesafari")。
  • application:openURL:options: 处理该 URL 但不打开它,它为此调用 TelegramUI.openExternalUrl
  • 正在打开的 URL 是 tg://resolve?domain=fridadotre
  • 它使用 Telegram 的 tg:// 自定义 URL Scheme。

有趣的是,如果您再次导航到“https://telegram.me/fridadotre”,单击取消,然后单击页面本身提供的链接(“在 Telegram 应用中打开”),它将通过通用链接打开,而不是通过自定义 URL Scheme 打开。

您可以在跟踪这两种方法的同时尝试此操作

$ frida-trace -U Telegram -m "*[* *restorationHandler*]" -m "*[* *application*openURL*options*]"

// After clicking "Open" on the pop-up

 16374 ms  -[AppDelegate application :0x10556b3c0 openURL :0x1c4ae0080 options :0x1c7a28400]
 16374 ms   application :<Application: 0x10556b3c0>
 16374 ms   openURL :tg://resolve?domain=fridadotre
 16374 ms   options :{
    UIApplicationOpenURLOptionsOpenInPlaceKey = 0;
    UIApplicationOpenURLOptionsSourceApplicationKey = "com.apple.mobilesafari";
}

// After clicking "Cancel" on the pop-up and "OPEN" in the page

406575 ms  -[AppDelegate application:0x10556b3c0 continueUserActivity:0x1c063d0c0
                restorationHandler:0x16f27a898]
406575 ms  application:<Application: 0x10556b3c0>
406575 ms  continueUserActivity:<NSUserActivity: 0x1c063d0c0>
406575 ms       webpageURL:https://telegram.me/fridadotre
406575 ms       activityType:NSUserActivityTypeBrowsingWeb
406575 ms       userInfo:{
}
406575 ms  restorationHandler:<__NSStackBlock__: 0x16f27a898>

测试已弃用的方法

搜索已弃用的方法,例如

您可以简单地使用 frida-trace 来查看是否正在使用任何这些方法。

测试 URL Scheme 来源验证

一种放弃或确认验证的方法是通过 Hook 可能用于此目的的典型方法。例如 isEqualToString:

// - (BOOL)isEqualToString:(NSString *)aString;

var isEqualToString = ObjC.classes.NSString["- isEqualToString:"];

Interceptor.attach(isEqualToString.implementation, {
  onEnter: function(args) {
    var message = ObjC.Object(args[2]);
    console.log(message)
  }
});

如果我们应用此 Hook 并再次调用 URL Scheme

$ frida -U iGoat-Swift

[iPhone::iGoat-Swift]-> var isEqualToString = ObjC.classes.NSString["- isEqualToString:"];

                    Interceptor.attach(isEqualToString.implementation, {
                      onEnter: function(args) {
                        var message = ObjC.Object(args[2]);
                        console.log(message)
                      }
                    });
{}
[iPhone::iGoat-Swift]-> openURL("iGoat://?contactNumber=123456789&message=hola")
true
nil

什么也没发生。这已经告诉我们,此方法没有用于此目的,因为我们在 Hook 和 tweet 文本之间找不到任何类似 OWASP.iGoat-Swiftcom.apple.mobilesafari应用包外观字符串。但是,请考虑到我们只是探测一种方法,应用可能正在使用其他方法进行比较。

模糊测试 URL Scheme

如果应用解析 URL 的一部分,您还可以执行输入模糊测试以检测内存损坏错误。

上面我们学到的内容现在可以用于以您选择的语言构建您自己的模糊器,例如在 Python 中,并使用 Frida 的 RPC 调用 openURL。该模糊器应执行以下操作

  • 生成有效负载。
  • 对于每个有效负载,调用 openURL
  • 检查应用是否在 /private/var/mobile/Library/Logs/CrashReporter 中生成崩溃报告 (.ips)。

FuzzDB 项目提供了模糊测试字典,您可以将其用作 payload。

使用 Frida

正如这篇博客文章中所解释的那样,使用 Frida 进行模糊测试非常容易,可以查看一个模糊测试 iGoat-Swift 应用的例子(在 iOS 11.1.2 上工作)。

在运行 fuzzer 之前,我们需要 URL scheme 作为输入。从静态分析中我们知道 iGoat-Swift 应用支持以下 URL scheme 和参数:iGoat://?contactNumber={0}&message={0}

$ frida -U SpringBoard -l ios-url-scheme-fuzzing.js
[iPhone::SpringBoard]-> fuzz("iGoat", "iGoat://?contactNumber={0}&message={0}")
Watching for crashes from iGoat...
No logs were moved.
Opened URL: iGoat://?contactNumber=0&message=0
OK!
Opened URL: iGoat://?contactNumber=1&message=1
OK!
Opened URL: iGoat://?contactNumber=-1&message=-1
OK!
Opened URL: iGoat://?contactNumber=null&message=null
OK!
Opened URL: iGoat://?contactNumber=nil&message=nil
OK!
Opened URL: iGoat://?contactNumber=99999999999999999999999999999999999
&message=99999999999999999999999999999999999
OK!
Opened URL: iGoat://?contactNumber=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
...
&message=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
...
OK!
Opened URL: iGoat://?contactNumber=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
...
&message=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
...
OK!
Opened URL: iGoat://?contactNumber='&message='
OK!
Opened URL: iGoat://?contactNumber=%20d&message=%20d
OK!
Opened URL: iGoat://?contactNumber=%20n&message=%20n
OK!
Opened URL: iGoat://?contactNumber=%20x&message=%20x
OK!
Opened URL: iGoat://?contactNumber=%20s&message=%20s
OK!

该脚本会检测是否发生了崩溃。这次运行没有检测到任何崩溃,但对于其他应用来说可能会发生崩溃。我们可以检查 /private/var/mobile/Library/Logs/CrashReporter/tmp 中的崩溃报告(如果脚本将其移动到此处)。