Android 活页夹安全性
Android 中 Binder 提供的进程间通信是否能够免受中间人攻击?有没有提供此信息的文档?
Is the interprocess communication provided by Binder in Android protected against man in the middle attacks? Is there any documentation that provides this info?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Binder 使用基于功能的安全模型。每个活页夹对象代表一种能力;将该对象交给另一个进程即可授予该进程访问该功能的权限。从这个角度来看,您可以通过不将重要的活页夹对象交给中间人来防止中间人攻击。如果进程没有获得活页夹对象,则它无法以任何方式访问它。
关于论文中讨论的“跨绑定器引用伪造”问题,如果我理解他们正在谈论的具体场景,我认为他们关于用户空间的附录比我同意的要弱一些。我认为他们犯了一个错误,那就是查看为 ServiceManager 编写的特殊 C 代码。正式地,我将 C++ 用户空间代码(特别是 Parcel 组成)视为 Binder 架构的一部分。当您调用 readBinder() 和相关方法时,此代码特别确保处理此类欺骗尝试。
我不同意这样的说法:内核没有完全确保数据的完整性是一个缺陷。我能想到的唯一方法是为活页夹事务定义标准类型数据结构,以便它可以读取和验证包裹的内容。在我看来,这在内核中引入了太多的知识,并且没有任何实际的好处。无论您放多少,用户空间都需要对传入交易进行某种验证,以确保其符合预期。如今,这是在验证 Parcel 上的原始数据读取操作(readBinder()、readString16()、readInt() 等)是否已写入以避免攻击的级别上完成的。将更多验证推送到内核仍然需要在用户空间中验证数据类型是否正确,现在您实际上已经将一些攻击机会(由于此代码中的错误)从用户空间转移到了内核。
关于 Binder 安全性的最后一件事是,重要的是要认识到在平台级别上还有一个在 Binder 基础设施之上实现的重要安全模型。这是基于权限/uid 的系统,服务可以检查传入呼叫的 uid,以根据允许的权限进行验证。
该安全模型还有另一个必须处理的欺骗漏洞。一个典型的场景是应用程序接收 Activity Manager 服务的 IBinder(因为每个人都可以获得它)。 Activity Manager Service 的 API 深深地基于检查传入调用的 uid 来确定允许什么——例如,如果调用了 bindService(),它将检查该 uid 是否有权绑定到给予的服务。恶意应用程序可能会尝试通过将活动管理器 IBinder 交给另一个系统服务来玩游戏,例如将 IWindow 交给窗口管理器。如果它知道第二个系统服务将进行的事务,它可以进行一些设置,以便它进行一个它认为是 resizeWindow() 的调用,但实际上当它放入活动中时最终会变成对 bindService() 的调用manager(如果这两个调用映射到相同的事务 ID)。
该漏洞的存在是因为活页夹系统没有以任何方式键入,“方法”调用只是使用整数事务代码和数据缓冲区发送的事务。
为了防止这种情况,由aidl生成的用户空间类型化接口总是将它们要调用的接口的名称放在其事务缓冲区的开头,并且事务的接收代码检查缓冲区前面的接口名称以确保它与自己的界面相匹配。这样,当活动管理器发现有一个接口用于窗口的传入调用时,上述场景中的欺骗者就会被捕获。
无论如何,对于 Android 开发人员的大多数实际使用来说,基于 uid 的安全性与核心 Binder 功能模型一样相关。在某些情况下,您将通过限制哪些进程可以访问活页夹来增强安全性。一个例子是如何有一个代表每个活动的 IBinder,只有系统进程和运行该活动的进程共享该 IBinder。
在其他情况下,IBinder 对象将与任何感兴趣的进程共享,但会根据 uid 在调用点强制执行安全性。如果您使用aidl 提供此类接口的标准实现,则您自己的此类安全性实现可以根据您想要应用于uid 的含义来完成。例如,您可以使用标准工具将权限与 uid 相关联,并询问包管理器传入的 uid 是否具有权限。
Binder uses a capability-based security model. Each binder object represents a capability; handing that object to another process grants the process access to that capability. From that perspective, you prevent man in the middle attacks by not handing an important binder object to the man in the middle. If a process doesn't get handed a binder object, it can not access it in any way.
Regarding the "cross-binder reference forgery" issue discussed in the paper, if I am understanding the specific scenario they are talking about, I think their addendum about user space is a little weaker than I would agree with. They make the mistake I think of looking at the special C code written for the ServiceManager. Formally, I would consider the C++ user space code (consisting in particular of Parcel) as being part of the Binder architecture. This code in particular makes sure to deal with such attempts at spoofing when you call its readBinder() and related methods.
I don't agree with the statement that it is a flaw that the kernel is not completely ensuring integrity of the data. The only way I could imagine for it to do that would be to defined a standard typed data structure for binder transactions, so that it could read and verify the contents of the parcel. This puts too much knowledge in the kernel in my opinion, and for no real benefit. No matter how much you put there, user space will need to do some kind of validation of the incoming transaction to ensure it matches its expectations. Today this is done at the level of validating that primitive data read operations on Parcel (readBinder(), readString16(), readInt(), etc) are written to avoid attacks. Pushing more validation to the kernel will still require validation in user space that data types are correct, and now you have actually moved some opportunities for attacks (due to bugs in this code) from user space to the kernel.
One final thing about binder security is that it is important to realize that at the platform level there is another important security model implemented on top of the binder infrastructure. This is the permission/uid-based system, where services can check the uid of incoming calls to verify them against their allowed permissions.
This security model has another spoofing vulnerability that it must deal with. A typical scenario would be an application receiving the IBinder for the Activity Manager Service (since everyone can get that). The API of the Activity Manager Service is deeply based on checking the uid of incoming calls to determine what is allowed -- for example if a call to bindService() is made, it will check to see if that uid has permission to bind to the given service. A malicious app could try to play games here by handing the activity manager IBinder to another system service, for example as an IWindow to the window manager. If it knows the transactions that the second system service will make, it could set things up so that it makes a call that it thinks is say resizeWindow() but actually ends up becoming a call to bindService() when it comes put in the activity manager (if those two calls are mapped to the same transaction ID).
This vulnerability exists because the binder system is not typed in any way, "method" calls are simply transactions being sent with an integer transaction code and data buffer.
To protect against this, the user space typed interfaces generated by aidl always put at the start of their transaction buffer the name of the interface they are intending to call, and the receiving code of the transaction checks the interface name at the front of the buffer to ensure that it matches its own interace. This way the spoofer in the scenario above will be caught when the activity manager sees that it has an incoming call whose interface was for a window.
Anyway, for most practical use by Android developers, uid-based security is just as relevant as the core binder capability model. In some cases you will enforce security by restricting which processes get access to a binder. An example for this would be how there is an IBinder representing each activity, which only the system process and the process running the activity share.
In other cases and IBinder object will be shared with any interested processes, but enforce security at point of call based on uid. If you are using aidl to supply your standard implementation of such interfaces, your own implementation of such security can be done based on what meaning you want to apply to uids. For example, you could use the standard facility to associated permissions with uids and ask the package manager if the incoming uid has a permission.
Binder 存在一个已识别的安全漏洞,可能使其容易受到 MITM 攻击:http://crypto. hyperlink.cz/files/xbinder.pdf 。引用 TFA 的话:
作者接着说,Android 中有针对这种攻击的防御措施,但是
的印象是,对于精心编写的代码来说,Binder 似乎相对安全,但也存在一些风险。
The Binder has one identified security flaw which may make it susceptible to a MITM attack: http://crypto.hyperlink.cz/files/xbinder.pdf . To quote TFA:
The author goes on to say that there are defenses in Android against this attack, but that
One gets the impression that Binder seems to be relatively secure for carefully-written code, but there is some risk.