Swift容易受到代码注入的影响吗? [英] Is Swift vulnerable to code injection?

查看:144
本文介绍了Swift容易受到代码注入的影响吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I was reading about Cycript and Cydia Substrate and how they can be used for code injection attacks on an iOS app. Code like this should scare you if you are working in a high security environment. (Ignore the /etc/password part, just consider the ability to replace originalMessage with 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那么脆弱,因为它没有动态。然后,我还读到你可以在swift中方法调整,所以它不是'如果Swift对代码注入攻击提供任何保护,请告诉我。

I read one blog (which I didn't save) that said that Swift was not as vulnerable as Objective-C since it wasn't as dynamic. Then again, I've also read that you can do method swizzling in Swift so it isn't clear to me if Swift offers any protections against code injection attacks.

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

So, is Swift vulnerable to code injection attacks?

推荐答案

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

Ultimately, there is no way to prevent someone from hijacking your program if you let it run on their device. There are ways to make it harder, but there is no way to make it impossible.

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

I can think of these major ways of injecting code into an application:


  • 使用运行时调配Objective-C方法;

  • 通过解析可执行文件和计算来调配虚拟Swift方法正确的位改变;

  • 修改调用目标;

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

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

  • 替换程序链接的库。

  • swizzling Objective-C methods with the runtime;
  • swizzling virtual Swift methods by parsing out the executable and figuring the right bits to change;
  • modifying call targets;
  • swizzling imported symbols by changing symbol stub targets;
  • using dyld to force-load libraries or change which libraries your program loads;
  • replacing the libraries that your program links against.

在用户完全控制的环境中,没有100%有效的方法可以防止这些问题。您应根据您的威胁模型决定是否担心。

And there's no 100% effective way to prevent any of these in an environment that the user fully controls. You should decide whether to be worried or not depending on your threat model.

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

Method swizzling is a technique where you change the implementation of a method at runtime with arbitrary, different code (usually for a different purpose). Common use cases are bypassing checks or logging parameters.

在Objective-C中进行调查是一件大事,因为运行时需要元数据来识别每个方法和每个实例字段。我不知道任何其他编译为本机机器代码的语言,并且保留了这么多的元数据。如果您有类似 - [AccessControl validatePassword:] 的内容,那么您只是让坏人变得非常轻松。使用 method_setImplementation ,这只是乞求发生。

Swizzling in Objective-C was a huge thing because the runtime needs metadata that identifies every method and every instance field. I don't know any other language that compiles to native machine code and that keeps this much metadata around. If you have something like -[AccessControl validatePassword:], you're just making it really easy for the bad guys. With method_setImplementation, this is just begging to happen.

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

As Swift classes can inherit from Objective-C classes, this is still something to look for. However, new methods on classes that inherit from an Objective-C class are only exposed to the Objective-C runtime if they have the @objc attribute (or if the class itself has the @objc attribute), so this limits the attack surface compared to Objective-C.

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

Additionally, the Swift compiler may bypass the Objective-C runtime to call, devirtualize or inline Swift methods that were not marked dynamic, even if they were marked @objc. This means that in some cases, swizzling could be possible only for calls dispatched through Objective-C.

当然,如果您的类或方法未暴露,则完全不可能到Objective-C运行时。

And of course, it's entirely impossible if your class or method is not exposed to the Objective-C runtime.

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

However, you don't need the Objective-C runtime to swap method implementations. Swift still has virtual tables for its virtual methods, and as of February 2015, they are located in the __DATA segment of the executable. It is writable, so it should be possible to swizzle Swift virtual methods if you can figure out the right bits to change. There is no convenient API for this.

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

C++ classes can similarly be modified, but Swift methods being virtual by default, the attack surface is much larger. The compiler is allowed to devirtualize methods as an optimization if it finds no override, but relying on compiler optimizations as a security feature is not responsible.

默认情况下,部署的Swift可执行文件剥离 ped。非 public / open 符号的信息将被丢弃,这样可以识别出您想要更改的符号与Objective-C相比更难。 公共 / 打开符号不会被剥离,因为假设其他外部代码客户端可能需要它们。

By default, deployed Swift executables are stripped. Information for non-public/open symbols is discarded, and this makes identifying the symbols that you want to change this much harder compared to Objective-C. Public/open symbols are not stripped because it is assumed that other, external code clients may need them.

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

However, if someone figures out which function implementation they want to swap out, all they have to do is write the address of the new implementation in the correct virtual table slot. They will probably need to make their own Mach-O parser, but this is certainly not out of the range of the people who make things like Cycript.

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

Finally, final methods reduce this risk because the compiler doesn't need to call them through the vtable. Also, struct methods are never virtual.

如果所有其他方法都失败了,攻击者仍然可以浏览您的机器代码并更改 bl 调用指令操作数到他们想要更好的地方。这更加复杂,相当难/不可能通过自动化方法100%正确,特别是如果缺少符号,但有足够的人能够做到这一点。您决定是否有人最终会发现为您的应用程序执行此操作非常麻烦。

If all else fails, your attacker can still walk through your machine code and change the bl or call instruction operands to anywhere they'd like better. This is more involved and fairly hard/impossible to get 100% right with an automated method, especially if symbols are missing, but someone determined enough will be able to do it. You decide if someone will eventually find it worth the trouble to do it for your application.

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

This works for virtual and non-virtual methods. It is, however, extremely difficult to do when the compiler inlines calls.

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

Any imported symbol, regardless of the language it's been written with and the language it's being used from, is vulnerable to swizzling. This is because external symbols are bound at runtime. Whenever you use a function from an external library, the compiler generates an entry in a lookup table. This is an example of what a call to fopen could look like if you returned your executable to C code:

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示例。

The initial call to fopen_stub finds the actual fopen, and replaces the address pointed to by fopen_stub with it. That way, dyld doesn't need to resolve the thousands of external symbols used from your program and its libraries before it starts running at all. However, this means that an attacker can replace fopen_stub with the address of any function that they'd like to call instead. This is what your Cycript example does.

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

Short of writing your own linker and dynamic linker, your only protection against this kind of attack is to not use shared libraries or frameworks. This is not a viable solution in a modern development environment, so you will probably have to deal with it.

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

There could be ways to ensure that stubs go where you expect them to be, but it would be kind of flaky, and these checks can always be noped out by a determined attacker. Additionally, you wouldn't be able to insert these checks before shared libraries you have no control over call imported symbols. These checks would also be useless if the attacker decided to just replace the shared library with one they control.

另外,启动闭包允许dyld 3替换这些查找表预先绑定的信息。我不认为启动闭包目前是只读的,但看起来它们最终可能是。如果是,那么混合符号将变得更难。

As an aside, launch closures allow dyld 3 to replace these lookup tables with pre-bound information. I don't think that launch closures are currently read-only, but it looks like they could eventually be. If they are, then swizzling symbols will become harder.

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

Dyld supports force-loading libraries into your executable. This capability can be used to replace just about any imported symbol that your executable uses. Don't like the normal fopen? Write a dylib that redefines it!

如果可执行文件被标记为受限制,Dyld将不会配合此方法。有三种方式来实现此状态(查找 pruneEnvironmentVariables ):

Dyld will not cooperate with this method if the executable is marked as restricted. There are three ways to achieve this status (look for pruneEnvironmentVariables):


  • 启用setuid位或setgid咬你的可执行文件;

  • 进行代码签名并拥有受限制的OS X-only权利;

  • 有一个名为<$ c的部分$ c> __ restrict 在一个名为 __ RESTRICT 的细分中。

  • enable the setuid bit or the setgid bit on your executable;
  • be code-signed and have the "Restricted" OS X-only entitlement;
  • have a section called __restrict in a segment called __RESTRICT.

您可以使用以下其他链接标记创建 __ restrict 部分和 __ RESTRICT 部分:

You can create the __restrict section and the __RESTRICT segment using the following "Other Linker Flags":

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

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

Note that all of these are pretty easy to break. The setuid and setgid bits are trivial to clear when the user controls the execution environment, a code signature is easy to remove, and the section or segment just has to be renamed to get rid of the restricted status as well.

如果所有其他方法都失败,攻击者仍然可以替换您的可执行文件使用的共享库让它做任何他们喜欢的事。你无法控制它。

If all else fails, an attacker can still replace the shared libraries that your executable uses to make it do whatever they like. You have no control over that.

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

Injecting code in a Swift application is harder than it was for an Objective-C application, but it's still possible. Most of the methods that can be used to inject code are language-independent, meaning that no language will make you safer.

大多数情况下,没有什么是你的可以做些保护自己免受此事。只要用户控制执行环境,您的代码就会在他们的系统上作为访客运行,他们几乎可以随意使用它。

For the most part, there is nothing that you can do to protect yourself against this. As long as the user controls the execution environment, your code is running as a guest on their system, and they can do almost whatever they want with it.

这篇关于Swift容易受到代码注入的影响吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆