OWASP 类别:MASVS-CODE:代码质量
概览
当自定义权限定义缺失或拼写错误,或者在清单中误用相应的 android:protectionLevel 属性时,就会出现与自定义权限相关的风险。
例如,恶意应用可以创建名称相同但由其定义且应用了不同保护级别的自定义权限,从而利用这些风险。
自定义权限旨在与其他应用共享资源和功能。以下是正当使用自定义权限的示例:
- 控制两个或更多应用之间的进程间通信 (IPC)
- 访问第三方服务
- 限制对应用的共享数据的访问权限
影响
利用此漏洞的影响是,恶意应用可能会获得对原本应受到保护的资源的访问权限。漏洞的影响取决于受保护的资源和原始应用服务关联的权限。
风险:自定义权限拼写错误
自定义权限可能在清单中声明,但由于拼写错误,系统会使用不同的自定义权限来保护导出的 Android 组件。恶意应用可以通过以下任一方式利用拼错权限的应用:
- 先注册该权限
- 预测后续应用中的拼写
这可能会导致应用未经授权访问资源或控制受害应用。
例如,某个存在漏洞的应用想要使用权限 READ_CONTACTS 保护某个组件,但不小心将该权限拼写为 READ_CONACTS。恶意应用可以声明 READ_CONACTS,因为该权限不归任何应用(或系统)所有,并获得对受保护组件的访问权限。此漏洞的另一个常见变体是 android:permission=True。true 和 false 等值(无论大小写)都是权限声明的无效输入,其处理方式与其他自定义权限声明的拼写错误类似。如需修正此问题,应将 android:permission 属性的值更改为有效的权限字符串。例如,如果应用需要访问用户的联系人,则 android:permission 属性的值应为 android.permission.READ_CONTACTS。
缓解措施
Android Lint 检查
声明自定义权限时,请使用 Android lint 检查来帮助您查找代码中的拼写错误和其他潜在错误。
命名惯例
使用一致的命名惯例,以便更轻松地发现错别字。仔细检查应用清单中的自定义权限声明,看看是否有拼写错误。
风险:孤立的权限
权限用于保护应用的资源。应用可以在两个不同的位置声明访问资源所需的权限:
- AndroidManifest.xml:在 AndroidManifest.xml 文件中预定义(如果未指定,则使用
<application>权限),例如,提供方权限、接收器权限、Activity 权限、服务权限; - 代码:在运行时代码中注册,例如
registerReceiver()。
不过,有时设备上 APK 的清单中没有通过相应的 <permission> 标记定义这些权限。在这种情况下,它们被称为孤立权限。出现此情况的原因有很多,例如:
- 清单上的更新与包含权限检查的代码之间可能存在不同步的情况
- 包含相应权限的 APK 可能未包含在 build 中,或者包含的版本可能不正确
- 检查或清单中的权限名称可能拼写错误
恶意应用可以定义孤立的权限并获取该权限。如果发生这种情况,信任孤立权限来保护组件的特权应用可能会受到损害。
如果特权应用使用该权限来保护或限制任何组件,则可能会授予恶意应用对该组件的访问权限。例如,启动受权限保护的 activity、访问 content provider 或向受孤立权限保护的广播接收器广播。
这还可能导致特权应用被欺骗,误认为恶意应用是合法应用,从而加载文件或内容。
缓解措施
确保您的应用用于保护组件的所有自定义权限也已在清单中定义。
应用使用自定义权限 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
此属性用于说明权限中隐含的潜在风险级别,并指示系统在决定是否授予权限时应遵循的流程。
缓解措施
避免使用“正常”或“危险”保护级别
在权限中使用常规或危险 protectionLevel 意味着大多数应用都可以请求并获取该权限:
- “normal”只需声明即可
- “危险”权限会被许多用户批准
因此,这些 protectionLevels 提供的安全性较低。
使用签名权限 (Android >= 10)
尽可能使用签名保护级别。使用此功能可确保只有与创建权限的应用具有相同签名的其他应用才能访问这些受保护的功能。确保您使用的是专用(而非重复使用)的签名证书,并将其安全地存储在密钥库中。
在清单中按如下方式定义自定义权限:
Xml
<permission
android:name="my.custom.permission.MY_PERMISSION"
android:protectionLevel="signature"/>
将对 activity 等的访问权限限制为仅授予了此自定义权限的应用,如下所示:
Xml
<activity android:name=".MyActivity" android:permission="my.custom.permission.MY_PERMISSION"/>
使用与声明此自定义权限的应用相同的证书签名的任何其他应用随后都将被授予对 .MyActivity activity 的访问权限,并且需要在其清单中按如下方式声明该权限:
Xml
<uses-permission android:name="my.custom.permission.MY_PERMISSION" />
注意签名自定义权限(Android < 10)
如果您的应用以 Android 10 之前的版本为目标平台,那么每当因卸载或更新而移除应用的自定义权限时,恶意应用可能仍能使用这些自定义权限,从而绕过检查。这是因为 Android 10 中修复了一个提权漏洞 (CVE-2019-2200)。
这也是建议使用签名检查而非自定义权限的原因之一(另一个原因是存在竞态条件风险)。
风险:竞态条件
如果某个正当应用 A 定义了签名自定义权限,该权限被其他 X 应用使用,但随后该正当应用被卸载,那么恶意应用 B 可以使用不同的 protectionLevel(例如 normal)定义相同的自定义权限。这样一来,B 即可访问 X 应用中受该自定义权限保护的所有组件,而无需使用与应用 A 相同的证书进行签名。
如果先安装 B,再安装 A,也会发生同样的情况。
缓解措施
如果您希望仅向与提供组件的应用具有相同签名的应用提供组件,或许可以避免定义自定义权限来限制对该组件的访问。在这种情况下,您可以使用签名检查。如果您的某个应用向您的另一个应用发出请求,后者会先验证两者是否使用相同的证书进行签名,只有证书相同时才会遵照该请求行事。
资源
- 尽量减少权限请求
- 权限概览
- 保护等级说明
- CustomPermissionTypo Android Lint
- 如何使用 Android Lint
- 研究论文,深入解释了 Android 权限和有趣的模糊测试结果