Overview
该技巧来源是我偶然间被推送的一条小蓝鸟:
当在ContentProvider
的openFile
方法内使用CallingUid或是CallingPid进行校验,是一种不合规范且不安全的权限校验方式,有被绕过的可能性。攻击者可以获取到一个只读的文件对象,造成文件泄漏。
复现 & 利用限制
复现方式比较简单,利用ActivityMangerService
中的openContentUri
接口可以拿到一个文件对象:
1 | // pls check it in android.app.IActivityManager |
利用条件是:
需要目标ContentProvider是一个导出的组件
在目标
ContentProvider
的openFile
接口内利用 uid/pid 或其关联的permission来鉴权;
利用效果:
- 调用者可以使用
system_server
(shareUid=1000), 来访问openFile
接口;
利用原理
以下所有代码片段均可在https://cs.android.com/ 中找到对应的内容;
poc中拿到与AMS通信用的binder对象后,调用了openContentUri
即会转交到AMS的对应方法中做处理:
1 | // frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java |
首先获取到目标Provider的ContentProviderHolder
将调用者的信息全部整理到AttributionSource
后调用provider的openFile:
1 | // frameworks/base/core/java/android/content/ContentProvider.java |
值得注意的是,该openFile
方法并不是开发者经常Override的接口,开发者常用的接口在后续的中.
1 | // frameworks/base/core/java/android/content/ContentProvider.java |
综上,当此时调用Binder.getCallingUid
的时候,拿到的就会是system_server的uid而非实际调用者的uid。
告警与反思
从Android对于ContentProvider的openFile接口的设计角度,使用Binder.getCallingUid
的方式来获取调用者的uid是不准确的。让我们回顾下,Android系统是如何做鉴权的:
1 | // frameworks/base/core/java/android/content/ContentProvider.java |
由于本次的访问是经过system_server进行了一次转发,因此,完整的callingUids应当是Binder.getCallingUid
+ AttributionSource
中所包含的uid信息;
对于一个Android开发者,建议尽量依赖系统本身提供的机制来完成访问控制,如组件的导出(exported)以及grantUriPermission机制。
REFERENCE
感谢@炜唯提供的帮助与指导~