跳过内容

MASTG-TEST-0071: 测试 UIActivity 共享

此测试即将更新

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

请通过提交 PR 来帮助我们: MASTG v1->v2 MASTG-TEST-0071: 测试 UIActivity 共享 (ios)

发送反馈

概述

静态分析

发送项目

在测试 UIActivity 共享时,应特别注意

  • 正在共享的数据(项目),
  • 自定义活动,
  • 排除的活动类型。

通过 UIActivity 共享数据的工作原理是创建一个 UIActivityViewController,并将所需的项目(URL、文本、图片)传递给 init(activityItems: applicationActivities:)

正如我们之前提到的,可以通过控制器的 excludedActivityTypes 属性 排除一些共享机制。 强烈建议使用最新版本的 iOS 进行测试,因为可以排除的活动类型数量可能会增加。 开发人员必须意识到这一点,并**明确排除**不适合应用数据的活动类型。 某些活动类型甚至可能没有记录,例如“创建表盘”。

如果有源代码,您应该查看 UIActivityViewController

  • 检查传递给 init(activityItems:applicationActivities:) 方法的活动。
  • 检查它是否定义了自定义活动(也传递给前一个方法)。
  • 验证 excludedActivityTypes(如果有)。

如果您只有已编译/已安装的应用,请尝试搜索之前的方法和属性,例如使用 _ rabin2_

$ rabin2 -zq Telegram\ X.app/Telegram\ X | grep -i activityItems
0x1000df034 45 44 initWithActivityItems:applicationActivities:

接收项目

接收项目时,应检查

  • 应用是否通过查看导出的/导入的 UTI(Xcode 项目的“信息”选项卡)来声明_自定义文档类型_。 所有系统声明的 UTI(统一类型标识符)列表都可以在 已存档的 Apple 开发人员文档 中找到。
  • 应用是否通过查看文档类型(Xcode 项目的“信息”选项卡)来指定任何_它可以打开的文档类型_。 如果存在,它们由名称和一个或多个表示数据类型的 UTI 组成(例如,PNG 文件的“public.png”)。 iOS 使用此来确定应用是否有资格打开给定的文档(仅指定导出的/导入的 UTI 是不够的)。
  • 应用是否通过查看应用委托中 application:openURL:options:(或其已弃用的版本 UIApplicationDelegate application:openURL:sourceApplication:annotation:)的实现来正确_验证收到的数据_。

如果没有源代码,您仍然可以查看 Info.plist 文件并搜索

  • UTExportedTypeDeclarations/UTImportedTypeDeclarations 如果应用声明了导出的/导入的_自定义文档类型_。
  • CFBundleDocumentTypes 查看应用是否指定了任何_它可以打开的文档类型_。

有关这些键的使用的非常完整的说明可以在 Stackoverflow 上找到。

让我们看一个实际的例子。 我们将采用文件管理器应用并查看这些键。 我们在此处使用了 _ objection_ 来读取 Info.plist 文件。

objection --gadget SomeFileManager run ios plist cat Info.plist

请注意,这与如果我们从手机检索 IPA 或通过 SSH 访问 IPA / 应用沙盒中的相应文件夹相同。 但是,使用 objection,我们只需_一个命令_即可实现我们的目标,这仍然可以被认为是静态分析。

我们注意到的第一件事是该应用未声明任何导入的自定义文档类型,但我们可以找到几个导出的类型

UTExportedTypeDeclarations =     (
            {
        UTTypeConformsTo =             (
            "public.data"
        );
        UTTypeDescription = "SomeFileManager Files";
        UTTypeIdentifier = "com.some.filemanager.custom";
        UTTypeTagSpecification =             {
            "public.filename-extension" =                 (
                ipa,
                deb,
                zip,
                rar,
                tar,
                gz,
                ...
                key,
                pem,
                p12,
                cer
            );
        };
    }
);

该应用还声明了它打开的文档类型,因为我们可以找到键 CFBundleDocumentTypes

CFBundleDocumentTypes =     (
        {
        ...
        CFBundleTypeName = "SomeFileManager Files";
        LSItemContentTypes =             (
            "public.content",
            "public.data",
            "public.archive",
            "public.item",
            "public.database",
            "public.calendar-event",
            ...
        );
    }
);

我们可以看到,此文件管理器将尝试打开任何符合 LSItemContentTypes 中列出的任何 UTI 的文件,并且可以打开扩展名在 UTTypeTagSpecification/"public.filename-extension" 中列出的文件。 请注意这一点,因为如果您在执行动态分析时想要搜索处理不同类型的文件时的漏洞,这将很有用。

动态分析

发送项目

通过执行动态检测,您可以轻松检查三个主要事项

  • activityItems:正在共享的项目数组。 它们可能是不同的类型,例如,要通过消息传递应用共享的一个字符串和一个图片。
  • applicationActivities:表示应用的自定义服务的 UIActivity 对象数组。
  • excludedActivityTypes:不支持的活动类型数组,例如 postToFacebook

要实现这一点,您可以做两件事

让我们看一个使用 Telegram 共享图片和文本文件的示例。 首先准备挂钩,我们将使用 Frida REPL 并为此编写一个脚本

Interceptor.attach(
ObjC.classes.
    UIActivityViewController['- initWithActivityItems:applicationActivities:'].implementation, {
  onEnter: function (args) {

    printHeader(args)

    this.initWithActivityItems = ObjC.Object(args[2]);
    this.applicationActivities = ObjC.Object(args[3]);

    console.log("initWithActivityItems: " + this.initWithActivityItems);
    console.log("applicationActivities: " + this.applicationActivities);

  },
  onLeave: function (retval) {
    printRet(retval);
  }
});

Interceptor.attach(
ObjC.classes.UIActivityViewController['- excludedActivityTypes'].implementation, {
  onEnter: function (args) {
    printHeader(args)
  },
  onLeave: function (retval) {
    printRet(retval);
  }
});

function printHeader(args) {
  console.log(Memory.readUtf8String(args[1]) + " @ " + args[1])
};

function printRet(retval) {
  console.log('RET @ ' + retval + ': ' );
  try {
    console.log(new ObjC.Object(retval).toString());
  } catch (e) {
    console.log(retval.toString());
  }
};

您可以将其存储为 JavaScript 文件,例如 inspect_send_activity_data.js,并像这样加载它

frida -U Telegram -l inspect_send_activity_data.js

现在,当您首先共享图片时,请观察输出

[*] initWithActivityItems:applicationActivities: @ 0x18c130c07
initWithActivityItems: (
    "<UIImage: 0x1c4aa0b40> size {571, 264} orientation 0 scale 1.000000"
)
applicationActivities: nil
RET @ 0x13cb2b800:
<UIActivityViewController: 0x13cb2b800>

[*] excludedActivityTypes @ 0x18c0f8429
RET @ 0x0:
nil

然后是文本文件

[*] initWithActivityItems:applicationActivities: @ 0x18c130c07
initWithActivityItems: (
    "<QLActivityItemProvider: 0x1c4a30140>",
    "<UIPrintInfo: 0x1c0699a50>"
)
applicationActivities: (
)
RET @ 0x13c4bdc00:
<_UIDICActivityViewController: 0x13c4bdc00>

[*] excludedActivityTypes @ 0x18c0f8429
RET @ 0x1c001b1d0:
(
    "com.apple.UIKit.activity.MarkupAsPDF"
)

您可以看到

  • 对于图片,活动项目是一个 UIImage,并且没有排除的活动。
  • 对于文本文件,有两个不同的活动项目,并且排除了 com.apple.UIKit.activity.MarkupAsPDF

在前面的示例中,没有自定义 applicationActivities,并且只有一个排除的活动。 但是,为了更好地说明您可以从其他应用中获得什么,我们使用另一个应用共享了一张图片,在这里您可以看到一堆应用程序活动和排除的活动(输出已编辑以隐藏发起应用的名称)

[*] initWithActivityItems:applicationActivities: @ 0x18c130c07
initWithActivityItems: (
    "<SomeActivityItemProvider: 0x1c04bd580>"
)
applicationActivities: (
    "<SomeActionItemActivityAdapter: 0x141de83b0>",
    "<SomeActionItemActivityAdapter: 0x147971cf0>",
    "<SomeOpenInSafariActivity: 0x1479f0030>",
    "<SomeOpenInChromeActivity: 0x1c0c8a500>"
)
RET @ 0x142138a00:
<SomeActivityViewController: 0x142138a00>

[*] excludedActivityTypes @ 0x18c0f8429
RET @ 0x14797c3e0:
(
    "com.apple.UIKit.activity.Print",
    "com.apple.UIKit.activity.AssignToContact",
    "com.apple.UIKit.activity.SaveToCameraRoll",
    "com.apple.UIKit.activity.CopyToPasteboard",
)

接收项目

在执行静态分析之后,您将知道_应用可以打开的文档类型_以及_它是否声明了任何自定义文档类型_以及(部分)涉及的方法。 您现在可以使用它来测试接收部分

  • 从另一个应用_共享_一个文件到应用,或者通过 AirDrop 或电子邮件发送。 选择文件,以便它将触发“打开方式...”对话框(也就是说,没有默认应用会打开文件,例如 PDF)。
  • 挂钩 application:openURL:options: 和在先前的静态分析中标识的任何其他方法。
  • 观察应用行为。
  • 此外,您可以发送特定的格式错误的文件和/或使用模糊测试技术。

为了用示例说明这一点,我们从静态分析部分选择了相同的真实文件管理器应用,并遵循了以下步骤

  1. 通过 Airdrop 从另一个 Apple 设备(例如 MacBook)发送 PDF 文件。
  2. 等待 AirDrop 弹出窗口出现,然后单击接受
  3. 由于没有默认的应用会打开该文件,因此它会切换到 打开方式... 弹出窗口。 在那里,我们可以选择将打开我们文件的应用。 下一个屏幕截图显示了这一点(我们已使用 Frida 修改了显示名称以隐藏该应用的真实名称)

  4. 选择 SomeFileManager 后,我们可以看到以下内容

    (0x1c4077000)  -[AppDelegate application:openURL:options:]
    application: <UIApplication: 0x101c00950>
    openURL: file:///var/mobile/Library/Application%20Support
                        /Containers/com.some.filemanager/Documents/Inbox/OWASP_MASVS.pdf
    options: {
        UIApplicationOpenURLOptionsAnnotationKey =     {
            LSMoveDocumentOnOpen = 1;
        };
        UIApplicationOpenURLOptionsOpenInPlaceKey = 0;
        UIApplicationOpenURLOptionsSourceApplicationKey = "com.apple.sharingd";
        "_UIApplicationOpenURLOptionsSourceProcessHandleKey" = "<FBSProcessHandle: 0x1c3a63140;
                                                                    sharingd:605; valid: YES>";
    }
    0x18c7930d8 UIKit!__58-[UIApplication _applicationOpenURLAction:payload:origin:]_block_invoke
    ...
    0x1857cdc34 FrontBoardServices!-[FBSSerialQueue _performNextFromRunLoopSource]
    RET: 0x1
    

如您所见,发送应用程序是 com.apple.sharingd,URL 的方案是 file://。 请注意,一旦我们选择了应打开该文件的应用,系统就已经将该文件移动到了相应的目标位置,即该应用的收件箱。 然后,应用负责删除其收件箱中的文件。 例如,此应用将文件移动到 /var/mobile/Documents/ 并将其从收件箱中删除。

(0x1c002c760)  -[XXFileManager moveItemAtPath:toPath:error:]
moveItemAtPath: /var/mobile/Library/Application Support/Containers
                            /com.some.filemanager/Documents/Inbox/OWASP_MASVS.pdf
toPath: /var/mobile/Documents/OWASP_MASVS (1).pdf
error: 0x16f095bf8
0x100f24e90 SomeFileManager!-[AppDelegate __handleOpenURL:]
0x100f25198 SomeFileManager!-[AppDelegate application:openURL:options:]
0x18c7930d8 UIKit!__58-[UIApplication _applicationOpenURLAction:payload:origin:]_block_invoke
...
0x1857cd9f4 FrontBoardServices!__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__
RET: 0x1

如果查看堆栈跟踪,您可以看到 application:openURL:options: 如何调用 __handleOpenURL:,后者调用 moveItemAtPath:toPath:error:。 请注意,我们现在在没有目标应用的源代码的情况下获得了此信息。 我们要做的第一件事是明确:挂钩 application:openURL:options:。 关于其余部分,我们必须仔细考虑并提出可以开始跟踪且与文件管理器相关的方法,例如,所有包含字符串“copy”、“move”、“remove”等的方法,直到我们发现被调用的方法是 moveItemAtPath:toPath:error:

最后值得注意的是,这种处理传入文件的方式与自定义 URL 方案相同。 请参阅 _ 测试自定义 URL 方案_ 以获取更多信息。