Permisos personalizados

Categoría de OWASP: MASVS-CODE: Calidad de código

Descripción general

Los riesgos asociados con los permisos personalizados surgen cuando falta la definición de permisos personalizados o está mal escrita, o cuando el atributo android:protectionLevel correspondiente se usa de forma incorrecta dentro del manifiesto.

Por ejemplo, estos riesgos se pueden explotar creando un permiso personalizado con el mismo nombre, pero definido por una app maliciosa y con diferentes niveles de protección aplicados.

Los permisos personalizados están diseñados para permitir el uso compartido de recursos y capacidades con otras apps. Estos son algunos ejemplos de uso legítimo de permisos personalizados:

  • Controlar la comunicación entre procesos (IPC) entre dos o más apps
  • Acceso a servicios de terceros
  • Cómo restringir el acceso a los datos compartidos de una app

Impacto

El impacto de aprovechar esta vulnerabilidad es que una app maliciosa podría obtener acceso a recursos que originalmente estaban destinados a estar protegidos. Las implicaciones de la vulnerabilidad dependen del recurso que se protege y de los permisos asociados del servicio de la aplicación original.

Riesgo: Errores de escritura en permisos personalizados

Se puede declarar un permiso personalizado en el manifiesto, pero se usa un permiso personalizado diferente para proteger los componentes de Android exportados debido a un error tipográfico. Una aplicación maliciosa puede aprovechar las aplicaciones que escribieron mal un permiso de las siguientes maneras:

  • Registrar ese permiso primero
  • Anticipar la ortografía en aplicaciones posteriores

Esto puede permitir que una aplicación tenga acceso no autorizado a los recursos o controle la aplicación de la víctima.

Por ejemplo, una app vulnerable quiere proteger un componente con un permiso READ_CONTACTS, pero, por error, escribe mal el permiso como READ_CONACTS. Una app maliciosa puede reclamar READ_CONACTS, ya que no pertenece a ninguna aplicación (ni al sistema) y obtener acceso al componente protegido. Otra variante común de esta vulnerabilidad es android:permission=True. Los valores como true y false, independientemente del uso de mayúsculas, son entradas no válidas para la declaración de permisos y se tratan de manera similar a otros errores de escritura de declaraciones de permisos personalizados. Para corregir este problema, el valor del atributo android:permission se debe cambiar a una cadena de permisos válida. Por ejemplo, si la app necesita acceder a los contactos del usuario, el valor del atributo android:permission debe ser android.permission.READ_CONTACTS.

Mitigaciones

Verificaciones de Lint de Android

Cuando declares permisos personalizados, usa las verificaciones de lint de Android para ayudarte a encontrar errores de escritura y otros posibles errores en tu código.

Convención de nombres

Usa una convención de nomenclatura coherente para que los errores de escritura sean más evidentes. Revisa cuidadosamente las declaraciones de permisos personalizados en el manifiesto de tu app para detectar errores de escritura.


Riesgo: Permisos huérfanos

Los permisos se usan para proteger los recursos de las apps. Hay dos ubicaciones diferentes en las que una app puede declarar los permisos necesarios para acceder a los recursos:

Sin embargo, a veces estos permisos no se definen con una etiqueta <permission> correspondiente en el manifiesto de un APK en el dispositivo. En este caso, se denominan permisos huérfanos. Esta situación puede ocurrir por varios motivos, como los siguientes:

  • Es posible que haya una falta de sincronización entre las actualizaciones del manifiesto y el código con la verificación de permisos.
  • Es posible que el APK con los permisos no se incluya en la compilación o que se incluya la versión incorrecta.
  • El nombre del permiso en la verificación o en el manifiesto podría estar escrito de forma incorrecta.

Una app maliciosa podría definir un permiso huérfano y adquirirlo. Si esto sucede, las aplicaciones con privilegios que confían en el permiso huérfano para proteger un componente podrían verse comprometidas.

En los casos en que la app con privilegios usa el permiso para proteger o restringir cualquier componente, esto podría otorgar a la app maliciosa acceso a ese componente. Entre los ejemplos, se incluyen el inicio de actividades protegidas por un permiso, el acceso a un proveedor de contenido o la transmisión a un receptor de transmisiones protegido por el permiso huérfano.

También podría crear una situación en la que se engañe a la aplicación privilegiada para que crea que la aplicación maliciosa es legítima y, por lo tanto, cargue archivos o contenido.

Mitigaciones

Asegúrate de que todos los permisos personalizados que usa tu app para proteger componentes también estén definidos en tu manifiesto.

La app usa los permisos personalizados my.app.provider.READ y my.app.provider.WRITE para proteger el acceso a un proveedor de contenido:

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"/>

La app también define y usa estos permisos personalizados, lo que impide que otras apps maliciosas lo hagan:

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" />

Riesgo: Se usó de forma incorrecta android:protectionLevel

Este atributo describe el nivel de riesgo potencial del permiso y también indica los procedimientos que debe seguir el sistema para decidir si otorga o no el permiso.

Mitigaciones

Evita el nivel de protección normal o peligroso

Usar un protectionLevel normal o peligroso en tus permisos significa que la mayoría de las apps pueden solicitar y obtener el permiso:

  • "normal" solo requiere que se declare
  • "peligroso" será aprobado por muchos usuarios

Por lo tanto, estos protectionLevels brindan poca seguridad.

Usa permisos de firma (Android >= 10)

Usa niveles de protección de firma siempre que sea posible. El uso de esta capacidad garantiza que solo otras apps firmadas con el mismo certificado que la app que creó el permiso puedan acceder a esas funciones protegidas. Asegúrate de usar un certificado de firma exclusivo (no reutilizado) y almacénalo de forma segura en un almacén de claves.

Define un permiso personalizado de la siguiente manera en tu manifiesto:

XML

<permission
    android:name="my.custom.permission.MY_PERMISSION"
    android:protectionLevel="signature"/>

Restringe el acceso a, p.ej., una actividad, solo a las apps que tengan otorgado este permiso personalizado, de la siguiente manera:

XML

<activity android:name=".MyActivity" android:permission="my.custom.permission.MY_PERMISSION"/>

Cualquier otra app que esté firmada con el mismo certificado que la app que declaró este permiso personalizado tendrá acceso a la actividad .MyActivity y deberá declararlo de la siguiente manera en su manifiesto:

XML

<uses-permission android:name="my.custom.permission.MY_PERMISSION" />

Ten cuidado con los permisos personalizados de firma (Android < 10)

Si tu app se segmenta para Android < 10, cada vez que se quiten los permisos personalizados de la app debido a desinstalaciones o actualizaciones, podría haber apps maliciosas que aún puedan usar esos permisos personalizados y, por lo tanto, omitir las verificaciones. Esto se debe a una vulnerabilidad de elevación de privilegios (CVE-2019-2200) que se corrigió en Android 10.

Este es uno de los motivos (junto con el riesgo de condiciones de carrera) por los que se recomiendan las verificaciones de firma en lugar de los permisos personalizados.


Riesgo: Condición de carrera

Si una app legítima A define un permiso personalizado de firma que usan otras apps X, pero luego se desinstala, una app maliciosa B puede definir ese mismo permiso personalizado con un protectionLevel diferente, p.ej., normal. De esta manera, B obtiene acceso a todos los componentes protegidos por ese permiso personalizado en las apps de X sin necesidad de estar firmado con el mismo certificado que la app A.

Lo mismo sucede si se instala B antes que A.

Mitigaciones

Si deseas que un componente solo esté disponible para las apps firmadas con la misma firma que la app proveedora, es posible que puedas evitar la definición de permisos personalizados para restringir el acceso a ese componente. En esta situación, puedes usar verificaciones de firma. Cuando una de tus apps realiza una solicitud a otra de tus apps, la segunda puede verificar que ambas estén firmadas con el mismo certificado antes de cumplir la solicitud.


Recursos