Swift 是不是容易受到代码注入的影响?

Posted

技术标签:

【中文标题】Swift 是不是容易受到代码注入的影响?【英文标题】:Is Swift vulnerable to code injection?Swift 是否容易受到代码注入的影响? 【发布时间】:2015-04-17 08:47:53 【问题描述】:

我正在阅读有关 Cycript 和 Cydia Substrate 以及它们如何用于对 ios 应用程序进行代码注入攻击的信息。如果您在高安全性环境中工作,这样的代码应该会吓到您。 (忽略 /etc/password 部分,只考虑将 originalMessage 替换为 crackedMessage 的能力。)

cy# MS.hookFunction(fopen, function(path, mode) 
cy>     if (path == "/etc/passwd")
cy>         path = "/var/passwd-fake";
cy>     var file = (*oldf)(path, mode);
cy>     log.push([path, mode, file]);
cy>     return file;
cy> , oldf)

我读过一篇博客(我没有保存)说 Swift 不像 Objective-C 那样容易受到攻击,因为它不是动态的。再说一次,我还读到您可以使用 method swizzling in Swift,所以我不清楚 Swift 是否提供任何针对代码注入攻击的保护。

那么,Swift 是否容易受到代码注入攻击?

【问题讨论】:

【参考方案1】:

最终,如果您让程序在他们的设备上运行,就无法阻止他人劫持您的程序。有办法让它变得更难,但没有办法让它变得不可能。

我可以想到将代码注入应用程序的这些主要方法:

在运行时调配 Objective-C 方法; 通过解析可执行文件并确定要更改的正确位来调整虚拟 Swift 方法; 修改调用目标; 通过更改符号存根目标来混合导入的符号; 使用 dyld 强制加载库或更改程序加载的库; 替换您的程序链接的库。

在用户完全控制的环境中,没有 100% 有效的方法来防止这些情况发生。您应该根据您的威胁模型来决定是否担心。

使用运行时调整 Objective-C 方法

方法调配是一种技术,您可以在运行时使用任意不同的代码(通常用于不同的目的)更改方法的实现。常见用例是绕过检查或记录参数。

在 Objective-C 中调配是一件大事,因为运行时需要元数据来识别每个方法和每个实例字段。我不知道有任何其他语言可以编译为本机代码并保留这么多元数据。如果你有类似-[AccessControl validatePassword:] 的东西,你只是让坏人很容易。对于method_setImplementation,这只是在乞求发生。

由于 Swift 类可以从 Objective-C 类继承,这仍然是需要寻找的东西。但是,从 Objective-C 类继承的类上的新方法只有在具有 @objc 属性(或者如果类本身具有 @objc 属性)时才会暴露给 Objective-C 运行时,因此这限制了攻击表面与 Objective-C 相比。

此外,Swift 编译器可能会绕过 Objective-C 运行时来调用、去虚拟化或内联未标记为 dynamic 的 Swift 方法,即使它们被标记为 @objc。这意味着在某些情况下,只能对通过 Objective-C 调度的调用进行调配。

当然,如果你的类或方法没有暴露给 Objective-C 运行时,那是完全不可能的。

通过解析可执行文件并确定要更改的正确位来调整虚拟 Swift 方法

但是,您不需要 Objective-C 运行时来交换方法实现。 Swift 仍然为其虚拟方法提供虚拟表,截至 2015 年 2 月,它们位于可执行文件的 __DATA 段中。它是可写的,所以如果你能找出要改变的正确位,应该可以混合 Swift 虚拟方法。没有方便的 API。

C++ 类也可以类似地进行修改,但 Swift 方法默认是虚拟的,攻击面要大得多。如果编译器没有发现覆盖,则允许编译器将方法去虚拟化作为优化,但依赖编译器优化作为安全功能是不负责任的。

默认情况下,部署的 Swift 可执行文件是stripped。非public/open 符号的信息被丢弃,与Objective-C 相比,这使得识别您想要更改的符号变得更加困难。 Public/open 符号不会被剥离,因为假设其他外部代码客户端可能需要它们。

但是,如果有人弄清楚他们想要换出哪个函数实现,他们所要做的就是将新实现的地址写入正确的虚拟表槽中。他们可能需要制作自己的 Mach-O 解析器,但这肯定不会超出制作 Cycript 之类的东西的人的范围。

最后,final 方法降低了这种风险,因为编译器不需要通过 vtable 调用它们。此外,struct 方法永远不是虚拟的。

修改调用目标

如果所有其他方法都失败了,您的攻击者仍然可以遍历您的机器代码并将blcall 指令操作数更改为他们想要更好的任何位置。这更复杂,而且很难/不可能通过自动化方法获得 100% 正确,尤其是在缺少符号的情况下,但有足够决心的人将能够做到这一点。您决定是否有人最终会发现为您的应用程序这样做是值得的。

这适用于虚拟和非虚拟方法。但是,当编译器内联调用时,这是非常困难的。

通过更改符号存根目标来调整导入的符号

任何导入的符号,无论其编写语言和使用语言如何,都容易受到混淆。这是因为外部符号是在运行时绑定的。每当您使用来自外部库的函数时,编译器都会在查找表中生成一个条目。这是一个示例,说明如果您将可执行文件返回到 C 代码,调用 fopen 可能会是什么样子:

FILE* locate_fopen(const char* a, const char* b) 
    fopen_stub = dyld->locate("fopen"); // find actual fopen and replace stub pointer to it
    return fopen_stub(a, b);


FILE* (*fopen_stub)(const char*, const char*) = &locate_fopen;

int main() 
    FILE* x = fopen_stub("hello.txt", "r");

fopen_stub 的初始调用找到实际的fopen,并用它替换fopen_stub 指向的地址。这样,dyld 在开始运行之前就不需要解析程序及其库中使用的数千个外部符号。但是,这意味着攻击者可以将fopen_stub 替换为他们想要调用的任何函数的地址。这就是您的 Cycript 示例所做的。

除了编写您自己的链接器和动态链接器之外,您对这种攻击的唯一保护就是不使用共享库或框架。在现代开发环境中,这不是一个可行的解决方案,因此您可能不得不处理它。

可能有一些方法可以确保存根到达您期望的位置,但它会有点不稳定,而且这些检查总是可以被坚定的攻击者nop 删除。此外,您无法在无法控制调用导入符号的共享库之前插入这些检查。如果攻击者决定用他们控制的共享库替换共享库,这些检查也将毫无用处。

顺便说一句,启动关闭允许 dyld 3 用预先绑定的信息替换这些查找表。我不认为发射关闭目前是只读的,但看起来它们最终可能是。如果是,那么 swizzling 符号将变得更难。

使用 dyld 强制加载库或更改程序加载的库

Dyld supports 将库强制加载到您的可执行文件中。此功能可用于替换可执行文件使用的几乎任何导入符号。不喜欢普通的fopen?写一个 dylib 重新定义它!

如果可执行文件被标记为受限,Dyld 将不会与此方法合作。有three ways可以达到这个状态(找pruneEnvironmentVariables):

在您的可执行文件上启用 setuid 位或 setgid 位; 经过代码签名并具有“受限”OS X 专用权利; 在名为 __RESTRICT 的段中有一个名为 __restrict 的部分。

您可以使用以下“其他链接器标志”创建__restrict 部分和__RESTRICT 段:

-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null

请注意,所有这些都很容易破解。当用户控制执行环境时,setuid 和 setgid 位很容易清除,代码签名很容易删除,只需重命名节或段即可摆脱受限状态。

替换程序链接的库

如果所有其他方法都失败了,攻击者仍然可以替换您的可执行文件使用的共享库,以使其为所欲为。你无法控制它。

tl;博士

在 Swift 应用程序中注入代码比在 Objective-C 应用程序中更难,但它仍然是可能的。大多数可用于注入代码的方法都与语言无关,这意味着没有任何语言可以让您更安全。

在大多数情况下,您无法保护自己免受这种情况的影响。只要用户控制执行环境,您的代码就会在他们的系统上作为访客运行,他们几乎可以用它做任何他们想做的事情。

【讨论】:

非常棒地更新了您的原始答案!非常感谢。【参考方案2】:

您说的是越狱 iOS 设备上的代码注入。很简单:用户已经删除了他们的操作系统保护,所以现在一切正常。没有安全感。如果用户没有自愿取消这种保护,那么进入应用程序的地址空间是不可能的。

【讨论】:

以上是关于Swift 是不是容易受到代码注入的影响?的主要内容,如果未能解决你的问题,请参考以下文章

这个 Python 代码是不是容易受到 SQL 注入的影响? (SQLite3)

如何更改此代码以使其容易受到 CRLF 注入的影响?

参数化的 java 持久性查询是不是容易受到 sql 注入的影响?

SQL Server 全文搜索 CONTAINS() 是不是容易受到 SQL 注入的攻击?

这个 Rails 3 搜索是不是容易受到 SQL 注入的攻击?

如何防止sql注入oracle apex