OWASP 分类: MASVS-CODE: 代码质量
概览
自定义权限相关的风险出现在自定义权限定义缺失或拼写错误时,或者当相应的 android:protectionLevel
属性在 Manifest 中被误用时。
例如,可以通过恶意应用创建同名但定义了不同保护级别的自定义权限来利用这些风险。
自定义权限旨在实现与其他应用共享资源和功能。自定义权限的合法用途示例如下:
- 控制两个或多个应用之间的进程间通信 (IPC)
- 访问第三方服务
- 限制对应用共享数据的访问
影响
利用此漏洞的影响是恶意应用可以访问原本受保护的资源。该漏洞的影响取决于受保护的资源以及原始应用服务相关的权限。
风险:自定义权限拼写错误
自定义权限可能已在 Manifest 中声明,但由于拼写错误,使用了不同的自定义权限来保护导出的 Android 组件。恶意应用可以通过以下方式利用拼写错误的权限的应用:
- 首先注册该权限
- 预测后续应用中的拼写
这可能导致应用未经授权访问资源或控制受害应用。
例如,一个易受攻击的应用想要通过使用权限 READ_CONTACTS
来保护组件,但却不小心将权限拼写成了 READ_CONACTS
。恶意应用可以声明 READ_CONACTS
,因为它不归任何应用(或系统)所有,从而获得对受保护组件的访问权限。此漏洞的另一个常见变种是 android:permission=True
。诸如 true
和 false
之类的值(无论大小写)对于权限声明来说都是无效输入,并且会像其他自定义权限声明的拼写错误一样处理。要解决此问题,应将 android:permission
属性的值更改为有效的权限字符串。例如,如果应用需要访问用户的联系人,则 android:permission
属性的值应为 android.permission.READ_CONTACTS
。
缓解措施
Android Lint 检查
在声明自定义权限时,使用 Android lint 检查来帮助查找代码中的拼写错误和其他潜在错误。
命名约定
使用一致的命名约定使拼写错误更容易被发现。仔细检查应用 Manifest 中的自定义权限声明是否存在拼写错误。
风险:孤立权限
权限用于守护应用资源。应用可以在两个不同的位置声明访问资源所需的权限:
- AndroidManifest.xml:在 AndroidManifest.xml 文件中预定义(如果未指定,则使用
<application>
权限),例如:提供者权限、接收者权限、活动权限、服务权限; - 代码:在运行时代码中注册,例如
registerReceiver()
。
然而,有时这些权限在设备上的 APK 的 Manifest 中没有通过相应的 <permission>
标签定义。在这种情况下,它们被称为孤立权限。这种情况可能由于多种原因发生,例如:
- Manifest 中的更新与包含权限检查的代码之间可能存在不同步
- 包含权限的 APK 可能未包含在构建中,或者包含了错误的版本
- 检查或 Manifest 中的权限名称可能拼写错误
恶意应用可以定义并获取孤立权限。如果发生这种情况,那么信任孤立权限来保护组件的特权应用可能会受到损害。
在特权应用使用权限来保护或限制任何组件的情况下,这可能会授予恶意应用访问该组件的权限。例如,启动受权限保护的活动、访问内容提供者或向受孤立权限保护的广播接收器广播。
这也可能导致特权应用被欺骗,误认为恶意应用是合法应用,从而加载文件或内容。
缓解措施
确保你的应用用于保护组件的所有自定义权限也在 Manifest 中定义。
应用使用自定义权限 my.app.provider.READ
和 my.app.provider.WRITE
来保护对内容提供者的访问
Xml
<provider android:name="my.app.database.CommonContentProvider" android:readPermission="my.app.provider.READ" android:writePermission="my.app.provider.WRITE" android:exported="true" android:process=":myappservice" android:authorities="my.app.database.contentprovider"/>
应用也定义并使用了这些自定义权限,从而阻止其他恶意应用这样做
Xml
<permission android:name="my.app.provider.READ"/>
<permission android:name="my.app.provider.WRITE"/>
<uses-permission android:name="my.app.provider.READ" />
<uses-permission android:name="my.app.provider.WRITE" />
风险:误用 android:protectionLevel
此属性描述了权限中潜在的风险级别,并指明了系统在决定是否授予权限时应遵循的程序。
缓解措施
避免使用 Normal 或 Dangerous 保护级别
在你的权限上使用 normal 或 dangerous protectionLevel
意味着大多数应用可以请求并获取该权限
- “normal” 只需声明即可
- “dangerous” 会得到许多用户的批准
因此,这些 protectionLevels
提供的安全性很低。
使用签名权限 (Android >= 10)
尽可能使用签名保护级别。使用此功能可确保只有使用与创建权限的应用相同的证书签名的其他应用才能访问这些受保护的功能。确保你使用的是专用(未重复使用)签名证书,并将其安全地存储在 keystore 中。
在 Manifest 中按如下方式定义自定义权限
Xml
<permission
android:name="my.custom.permission.MY_PERMISSION"
android:protectionLevel="signature"/>
按如下方式限制对(例如)活动的访问,仅限已授予此自定义权限的应用
Xml
<activity android:name=".MyActivity" android:permission="my.custom.permission.MY_PERMISSION"/>
使用与声明此自定义权限的应用相同的证书签名的任何其他应用都将获得对 .MyActivity
活动的访问权限,并且需要在其 Manifest 中按如下方式声明:
Xml
<uses-permission android:name="my.custom.permission.MY_PERMISSION" />
注意签名自定义权限 (Android < 10)
如果你的应用目标 API 级别低于 Android 10,那么当你的应用的自定义权限因卸载或更新而被移除时,恶意应用可能仍能使用这些自定义权限,从而绕过检查。这是由于 Android 10 中 修复了一个权限提升漏洞 (CVE-2019-2200
)。
这是推荐使用签名检查而非自定义权限的原因之一(以及竞争条件的风险)。
风险:竞争条件
如果合法应用 A
定义了一个签名自定义权限,该权限被其他 X
应用使用,但 A
随后被卸载,则恶意应用 B
可以定义具有不同 protectionLevel
(例如 normal)的相同自定义权限。这样,B
就可以访问 X
应用中受该自定义权限保护的所有组件,而无需使用与应用 A
相同的证书签名。
如果 B
在 A
之前安装,也会发生同样的情况。
缓解措施
如果你想让组件仅对与提供应用使用相同签名签名的应用可用,则可以避免定义自定义权限来限制对该组件的访问。在这种情况下,你可以使用签名检查。当你的一个应用向你的另一个应用发出请求时,第二个应用可以在遵守请求之前验证两个应用是否使用相同的证书签名。
资源
- 最大限度地减少你的权限请求
- 权限概览
- 保护级别说明
- CustomPermissionTypo Android Lint
- 如何使用 Android Lint
- 一篇深入解释 Android 权限并提供有趣的模糊测试发现的研究论文