Si estás familiarizado con el desarrollo de aplicaciones para Android, seguro que has trabajado con componentes (aunque quizás no los conozcas bajo esta terminología) y en concreto, con proveedores de contenido. En esta entrada, voy a hablar sobre cómo podemos usar inyección SQL para acceder a datos que proveen este tipo de componentes y cómo protegerse ante este tipo de ataques.

Pero antes, un poco de contexto para introducir los componentes de una aplicación Android y los proveedores de contenido.

¿Qué es un componente de una aplicación Android?

Una aplicación Android puede contener varios componentes con un fin específico y un ciclo de vida diferente. Estos componentes, según la documentación oficial de Android, son puntos de entrada del sistema a nuestra aplicación y ayudan a definir el comportamiento general de la misma.

Los cuatro tipos de componentes de una aplicación son:

Actividades

Servicios

Proveedores de contenido

Receptores de mensajes

Quitando los proveedores de contenido, no voy a entrar en detalle a explicar cada uno de los componentes ya que me llevaría una nueva entrada del blog pero si que podéis leer toda la información en la documentación oficial o incluso leer nuestra serie de entradas sobre Servicios en Android.

¿Qué es un proveedor de contenido?

Como su propio nombre indica, es el encargado de proveer datos de una aplicación, gestionando el acceso a los mismos. Estos datos pueden estar almacenados en el sistema de ficheros, en una base de datos SQLite, en la Web o en cualquier otra ubicación de almacenamiento persistente accesible desde nuestra aplicación.

A través de un proveedor de contenido:

Cualquier aplicación con los permisos correspondientes puede leer o incluso modificar los datos, haciendo uso de un conjunto estándar de API que la permita realizar las transacciones necesarias.

que la permita realizar las transacciones necesarias. Nuestra aplicación puede leer y escribir datos privados y que no sean compartidos.

En nuestro caso, nos vamos a centrar en intentar acceder al proveedor de contenido de nuestra aplicación usando otra aplicación diferente, desde la cual inyectaremos SQL.

Preparando el ataque a un proveedor de contenido

Vamos a utilizar una herramienta de pruebas de seguridad para Android denominada Drozer, así que lo primero que tenemos que hacer es descargarla e instalarla en nuestro ordenador. También nos hará falta descargar un .apk que instalaremos en un dispositivo Android y que contiene la aplicación agente desde la cual accederemos al proveedor de contenido de nuestra aplicación y realizaremos la inyección SQL.

Una vez instalada la consola Drozer en nuestro ordenador y la aplicación agente en nuestro dispositivo, nos quedaría conectar las dos. Para ello sigamos los siguientes pasos:

1. Configurar una redirección de puertos para que nuestro ordenador se pueda conectar al socket TCP abierto por el agente (drozer usa el puerto 31415 por defecto):

adb forward tcp:31415 tcp:31415

2. Lanzar la aplicación agente en el dispositivo y levantar el servidor embebido

2.1. Activar el servidor 2.2. Esperar mensaje de confirmación

3. Conectarse utilizando la consola drozer:

Nota: todos los comandos que se utilizarán en esta entrada son para Linux, para Windows leer el README de Drozer.

drozer console connect

Una vez ejecutado este comando, debería aparecer algo así:

Selecting xxxxxxxxxxxxxx (samsung SM-G935F 7.0) .. ..:. ..o.. .r.. ..a.. . ....... . ..nd ro..idsnemesisand..pr .otectorandroidsneme. .,sisandprotectorandroids+. ..nemesisandprotectorandroidsn:. .emesisandprotectorandroidsnemes.. ..isandp,..,rotectorandro,..,idsnem. .isisandp..rotectorandroid..snemisis. ,andprotectorandroidsnemisisandprotec. .torandroidsnemesisandprotectorandroid. .snemisisandprotectorandroidsnemesisan: .dprotectorandroidsnemesisandprotector. drozer Console (v2.4.3) dz>

4. A continuación, ejecutaremos un módulo de drozer para listar los paquetes instalados en el dispositivo.

dz> run app.package.list

Si queremos buscar el paquete de una aplicación específica podemos ejecutar el comando:

dz> run app.package.list -f nombre_de_la_app

En este punto deberíamos tener el paquete de la aplicación que queremos atacar. Pongamos que el paquete de la app que queremos atacar es algo como com.org.testapp.

5. Con el siguiente comando, identificaremos los componentes de la aplicación que son vulnerables a un ataque, como aquellos que han sido exportados para ser usados por otras aplicaciones.

dz> run app.package.attacksurface com.org.testapp

Una vez ejecutado este comando, obtendríamos algo de este tipo:

Attack Surface: 2 activities exported 1 broadcast receivers exported 2 content providers exported 2 services exported is debuggable

Como podemos ver, hay dos proveedores de contenido vulnerables.

6. Una vez que sabemos que hay proveedores de contenido que pueden ser atacados, usaremos un módulo de escaneo de Drozer que nos permite obtener un listado de las URIs con contenido accesible.

dz> run scanner.provider.finduris -a com.org.testapp

Abajo podemos ver lo que obtenemos al ejecutar este comando, lo cual nos indica que esas URIs del proveedor de contenido contienen datos que podemos extraer.

Scanning com.org.testapp... Unable to Query content://org.testapp.documents/ Unable to Query content://org.testapp.documents Able to Query content://org.testapp Unable to Query content://org.testapp.images/ Unable to Query content://org.testapp.images Able to Query content://org.testapp/ Accessible content URIs: content://org.testapp content://org.testapp/

Ya hemos conseguido obtener las URIs a atacar pero antes de empezar con la inyección, vamos a repasar algunos conceptos SQL. Para aquellos que ya conocéis los conceptos de proyección o selección SQL, podéis saltar directamente al paso 7 del apartado de Inyección SQL.

Repaso de conceptos SQL

Proyección : nos permite elegir las columnas de la tabla a devolver por la consulta SQL.

: nos permite elegir las columnas de la tabla a devolver por la consulta SQL. Selección: nos permite incluir una condición para elegir las filas de la tabla a devolver por la consulta SQL.

Veamos un ejemplo con una consulta básica a una tabla que hemos llamado videos: Con esta consulta queremos extraer los campos nombre, tamaño y formato (columnas – proyección) de todas aquellas entradas (filas – selección) cuyo tamaño sea mayor que 20.

Inyección SQL sobre proyección en un proveedor de contenido

Este tipo de ataque nos permite infiltrar código SQL en la proyección de una consulta SQL, aprovechándonos de una vulnerabilidad de una aplicación Android en la validación de las entradas, para realizar operaciones sobre una base de datos a través del proveedor de contenido.

7. Volvamos a la consola de comandos de drozer para realizar la primera inyección SQL sobre proyección.

dz> run app.provider.query content://org.testapp/ --projection "*FROM SQLITE MASTER WHERE type='table';--"

Con este comando estamos inyectando la sentencia “*FROM SQLITE MASTER WHERE type=’table’;–“ en la proyección de la consulta SQL, con la que accederemos a información de todas las tablas de la base de datos. Al poner “;–“ al final de la sentencia, estamos forzando a que la consulta acabe ahí, comentando todo lo que podría venir detrás y que el sistema está esperando.

Veamos un esquema para entender mejor la inyección SQL que hemos realizado con el comando anterior:

La salida de este comando podría ser algo como:

| type | name | tbl_name | rootpage | sql | | table | documents | documents | 3 | CREATE TABLE documents(_id INTEGER PRIMARY KEY, name TEXT, description TEXT) | | table | images | images | 4 | CREATE TABLE images(_id INTEGER PRIMARY KEY, name TEXT, size INTEGER, format TEXT) | table | videos | videos | 5 | CREATE TABLE videos(_id INTEGER PRIMARY KEY,name TEXT,size INTEGER, format TEXT, duration INTEGER)

8. Por último, una vez que sabemos el nombre de las tablas de la base de datos, vamos a obtener todos los datos de una de ellas.

dz>run app.provider.query content://org.testapp/ --projection "* FROM videos;--"

Este comando nos devolvería todos los datos de la tabla videos

| _id | name | size | format | duration | | 1 | video1 | 30 | mp4 | 5 | | 2 | video2 | 15 | avi | 2 | 3 | video3 | 140 | mkv | 10 |

Inyección SQL sobre selección en un proveedor de contenido

A continuación, vamos a introducir SQL no deseado en la parte de la selección de una consulta.

9. El ataque sobre selección más conocido es el basado en “1=1 is Always True”.

dz>run app.provider.query content://org.testapp/ --selection "1=1"

En este caso obtendríamos todos los datos accesibles, ya que la condición insertada en la selección (where 1=1) siempre se cumple.

Esta inyección es muy peligrosa, puesto que un atacante podría obtener todos los datos (incluyendo usuarios y passwords si los hubiera) poniendo 1=1 en un determinado input de la aplicación que haga esa petición.

¿Cómo proteger un proveedor de contenido?

A nivel del manifiesto de la aplicación: lo primero que nos tenemos que preguntar cuando implementemos un proveedor de contenido en nuestra aplicación es si es necesario que otras aplicaciones accedan a él: Si no es necesario, habría que incluir android:exported=false en el manifiesto. De esta forma, el proveedor de contenido no aparecería cuando intentamos realizar un ataque usando drozer u otra herramienta.

en el manifiesto. De esta forma, el proveedor de contenido no aparecería cuando intentamos realizar un ataque usando drozer u otra herramienta. Si es necesario pero solo queremos que sea accesible desde otras aplicaciones firmadas por nosotros, podemos añadir el atributo android:protectionLevel="signature" al manifiesto de la aplicación. A nivel de permisos (proveedor de contenido exportado para que lo usen otras apps): Se pueden definir permisos de lectura y/o escritura para el proveedor de contenido A nivel de consultas: Proyección : comprobar que los campos a consultar (name, size, format en el ejemplo de debajo) existen realmente en la tabla de la que queremos obtener los datos.

: comprobar que los campos a consultar (name, size, format en el ejemplo de debajo) existen realmente en la tabla de la que queremos obtener los datos. Selección: usar métodos de consulta parametrizados.

Ejemplo de consulta parametrizada

En resumen

En esta entrada hemos introducido los conceptos de componentes en Android, proveedores de contenido, cómo acceder a los datos que proveen mediante un ataque de inyección SQL sobre proyección y selección y cómo protegerse frente a estos ataques.

Los ejemplos de inyección que hemos incluido son básicos, de consulta de datos, pero investigando un poco más, podría ser posible modificar o incluso borrar los datos.

Otros enlaces relacionados

Android Services desde cero

Android Services desde cero (II): servicios iniciados

OCR en Android

Beacons en Android