Introduccion
A mediados de abril,
publicamos noticias sobre el troyano
Android.InfectionAds.1 , que explotó varias vulnerabilidades críticas en el sistema operativo Android. Uno de ellos, CVE-2017-13156 (también conocido como
Janus ), permite que un programa malicioso infecte archivos APK sin dañar su firma digital.
El otro es CVE-2017-13315. Le otorga al troyano privilegios avanzados y puede instalar y desinstalar aplicaciones de forma independiente. Un análisis detallado de
Android.InfectionAds.1 está disponible en nuestra biblioteca de virus, se puede encontrar
aquí . Nos detendremos en la vulnerabilidad CVE-2017-13315 con más detalle y veremos cómo es.
CVE-2017-13315 pertenece al grupo de vulnerabilidades que recibió el nombre general EvilParcel. Se encuentran en varias clases de sistema del sistema operativo Android. Debido a errores en este último al intercambiar datos entre las aplicaciones y el sistema, es posible reemplazar estos datos. Los programas maliciosos que explotan las vulnerabilidades de EvilParcel reciben mayores privilegios y pueden hacer lo siguiente con su ayuda:
- instalar y desinstalar aplicaciones con cualquier permiso sin la confirmación del usuario;
- cuando se usa junto con otras vulnerabilidades, infecta los programas instalados en el dispositivo y reemplaza los originales "limpios" con copias infectadas;
- Restablecer código de bloqueo de pantalla para dispositivo Android
- Restablezca el PIN de la pantalla de bloqueo del dispositivo Android.
Actualmente hay 7 vulnerabilidades conocidas de este tipo:
- CVE-2017-0806 (error en la clase GateKeeperResponse), publicado en octubre de 2017;
- CVE-2017-13286 (error en la clase OutputConfiguration, publicado en abril de 2018;
- CVE-2017-13287 (error en la clase VerifyCredentialResponse), publicado en abril de 2018;
- CVE-2017-13288 (error en la clase PeriodicAdvertizingReport), publicado en abril de 2018;
- CVE-2017-13289 (error en la clase ParcelableRttResults), publicado en abril de 2018;
- CVE-2017-13311 (error en la clase SparseMappingTable), publicado en mayo de 2018;
- CVE-2017-13315 (error en la clase DcParamObject), publicado en mayo de 2018.
Todos ellos amenazan los dispositivos que ejecutan las versiones 5.0 - 8.1 del sistema operativo Android que no tienen instaladas las actualizaciones de seguridad de mayo de 2018 y posteriores.
Requisitos previos para las vulnerabilidades de EvilParcel
Veamos cómo surgen las vulnerabilidades de EvilParcel. En primer lugar, veremos algunas de las características de las aplicaciones de Android. En el sistema operativo Android, todos los programas interactúan entre sí, así como con el propio sistema operativo, enviando y recibiendo objetos de tipo Intent. Estos objetos pueden contener un número arbitrario de pares clave-valor dentro de un objeto de tipo Bundle.
Cuando se transmite Intención, el objeto Bundle se convierte (serializa) en una matriz de bytes envuelta en Parcel, y cuando lee claves y valores de un Bundle serializado, se deserializa automáticamente.
En el paquete, la cadena es la clave, y el valor puede ser casi cualquier cosa. Por ejemplo, un tipo primitivo, cadena o contenedor que contiene tipos primitivos o cadenas. Además, puede ser un objeto de tipo Parcelable.
Por lo tanto, en el Paquete, puede colocar un objeto de cualquier tipo que implemente la interfaz Parcelable. Para hacer esto, deberá implementar los métodos writeToParcel () y createFromParcel () para serializar y deserializar el objeto.
Como buen ejemplo, creemos un paquete serializado simple. Escribamos un código que coloque tres pares clave-valor en el paquete y lo serialice:
Bundle demo = new Bundle ();
demo.putString ("String", "Hello, World!");
demo.putInt ("Entero", 42);
demo.putByteArray ("ByteArray", nuevo byte [] {1, 2, 3, 4, 5, 6, 7, 8});
Parcela parcela = Parcel.obtain ();
parcel.writeBundle (demo);
Después de ejecutar este código, obtenemos un paquete de la siguiente forma:
Figura 1. Estructura de un objeto Bundle serializado.
Prestemos atención a las siguientes características de la serialización de Bundle:
- todos los pares clave-valor se escriben uno tras otro;
- antes de cada valor se indica su tipo (13 para una matriz de bytes, 1 para un entero, 0 para una cadena, etc.);
- antes de los datos de longitud variable, se indica su tamaño (longitud de la cadena, número de bytes para la matriz);
- Todos los valores se escriben con una alineación de 4 bytes.
Debido al hecho de que todas las claves y valores en el Paquete se escriben secuencialmente, al acceder a cualquier clave o al valor de un objeto Bundle serializado, este último se deserializa por completo, incluida la inicialización de todos los objetos Parcelable contenidos en él.
Parecería, ¿cuál podría ser el problema? Y es que en algunas clases de sistema que implementan Parcelable, los métodos createFromParcel () y writeToParcel () pueden encontrar errores. En estas clases, el número de bytes leídos en el método createFromParcel () será diferente del número de bytes escritos en el método writeToParcel (). Si coloca un objeto de dicha clase dentro del paquete, los límites del objeto dentro del paquete cambiarán después de la re-serialización. Y aquí es donde se crean las condiciones para explotar la vulnerabilidad EvilParcel.
Aquí hay un ejemplo de una clase con un error similar:
class Demo implements Parcelable { byte[] data; public Demo() { this.data = new byte[0]; } protected Demo(Parcel in) { int length = in.readInt(); data = new byte[length]; if (length > 0) { in.readByteArray(data); } } public static final Creator<Demo> CREATOR = new Creator<Demo>() { @Override public Demo createFromParcel(Parcel in) { return new Demo(in); } }; @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeInt(data.length); parcel.writeByteArray(data); } }
Si el tamaño de la matriz de datos es 0, al crear un objeto, create intFromParcel () leerá un int (4 bytes) y escribirá intToParcel () escribirá dos int (8 bytes). El primer int se escribirá en una llamada explícita a writeInt. El segundo int se escribirá cuando se llame a writeByteArray (), porque su longitud siempre se escribe en la matriz antes de Parcel (consulte la Figura 1).
Las situaciones en las que el tamaño de la matriz de datos es 0 son raras. Pero incluso cuando esto sucede, el programa sigue funcionando si solo se transmite un objeto en forma serializada a la vez (en nuestro ejemplo, el objeto Demo). Por lo tanto, tales errores, como regla, pasan desapercibidos.
Ahora intentemos colocar un objeto de demostración con una longitud de matriz cero en el paquete:
Figura 2. El resultado de agregar un objeto de demostración con una longitud de matriz cero al paquete.
Nosotros serializamos el objeto:
Figura 3. Objeto de paquete después de la serialización.
Tratemos de deserializarlo:
Figura 4. Después de deserializar el objeto Bundle.
Cual es el resultado? Considere un fragmento de paquete:
Figura 5. Estructura de la parcela después de la deserialización del paquete.
De las Figuras 4 y 5, vemos que durante la deserialización, se leyó un int en el método createFromParcel en lugar de dos escritos previamente. Por lo tanto, todos los valores posteriores del paquete no se leyeron correctamente. El valor 0x0 en la dirección 0x60 se leyó como la longitud de la siguiente clave. Y el valor 0x1 en la dirección 0x64 se leyó como una clave. En este caso, el valor 0x31 en la dirección 0x68 se leyó como el tipo de valor. No hay valores en Parcel cuyo tipo sea 0x31, por lo que readFromParcel () informó fielmente un error (excepción).
¿Cómo se puede usar esto en la práctica y convertirse en una vulnerabilidad? A ver! El error descrito anteriormente en las clases del sistema Parcelable le permite construir Bundle, que puede diferir durante la primera y las deserializaciones repetidas. Para demostrar esto, modifique el ejemplo anterior:
Parcel data = Parcel.obtain(); data.writeInt(3);
Este código crea un paquete serializado que contiene una clase vulnerable. Veamos el resultado de ejecutar este código:
Figura 6. Creación de un paquete con una clase vulnerable.
Después de la primera deserialización, este paquete contendrá las siguientes claves:
Figura 7. Resultado de deserializar un paquete con una clase vulnerable.
Ahora serialice nuevamente el paquete resultante, luego deserialícelo nuevamente y mire la lista de claves:
Figura 8. Resultado de la re-serialización y deserialización de un paquete con una clase vulnerable.
Que vemos La clave oculta (con el valor de cadena "¡Hola!") Apareció en el paquete, que no estaba allí antes. Considere el fragmento de paquete de este paquete para comprender por qué sucedió esto:
Figura 9. Estructura de parcela del objeto Bundle con la clase vulnerable después de dos ciclos de serialización-deserialización.
Aquí la esencia de las vulnerabilidades de EvilParcel se vuelve más clara. Es posible crear un paquete especialmente formado que contendrá una clase vulnerable. Cambiar los límites de esta clase le permitirá colocar cualquier objeto en este paquete, por ejemplo, Intent, que aparecerá en el paquete solo después de la segunda deserialización. Esto permitirá ocultar Intent de los mecanismos de protección del sistema operativo.
Operation EvilParcel
Android.InfectionAds.1 usando CVE-2017-13315 instaló y desinstaló programas por sí solo sin la intervención del propietario del dispositivo infectado. ¿Pero cómo va esto?
En 2013, también se descubrió el error
7699048 , también conocido como Launch AnyWhere. Permitía que una aplicación de terceros ejecutara actividades arbitrarias en nombre del sistema de usuario más privilegiado. El siguiente diagrama muestra su mecanismo de acción:
Figura 10. Esquema del error 7699048.
Con esta vulnerabilidad, una aplicación de explotación podría implementar el servicio AccountAuthenticator, que está diseñado para agregar nuevas cuentas al sistema operativo. Gracias al error 7699048, el exploit puede ejecutar actividades para instalar, desinstalar, reemplazar aplicaciones, restablecer el PIN o Bloqueo de patrón y hacer otras cosas desagradables.
Google ha solucionado este vacío al prohibir el lanzamiento de actividades arbitrarias desde el AccountManager. Ahora el AccountManager solo permite el lanzamiento de actividades que provienen de la misma aplicación. Para hacer esto, verifica y compara la firma digital del programa que inició el inicio de la actividad con la firma de la aplicación en la que se encuentra la actividad iniciada. Se ve así:
if (result != null && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { int authenticatorUid = Binder.getCallingUid(); long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId); int targetUid = resolveInfo.activityInfo.applicationInfo.uid; if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authenticatorUid, targetUid)) { throw new SecurityException( "Activity to be started with KEY_INTENT must " + "share Authenticator's signatures"); } } finally { Binder.restoreCallingIdentity(bid); } }
Parece que el problema se ha resuelto, pero no todo aquí es tan sencillo. ¡Resultó que esta solución se puede eludir usando la conocida vulnerabilidad EvilParcel CVE-2017-13315! Como ya sabemos, después de arreglar Launch AnyWhere, el sistema verifica la firma digital de la aplicación. Si esta comprobación tiene éxito, el paquete se pasa a IAccountManagerResponse.onResult (). Al mismo tiempo, onResult () se llama a través del mecanismo IPC, por lo que el Bundle se serializa nuevamente. En la implementación de onResult (), sucede lo siguiente:
private class Response extends IAccountManagerResponse.Stub { public void onResult(Bundle bundle) { Intent intent = bundle.getParcelable(KEY_INTENT); if (intent != null && mActivity != null) {
A continuación, el paquete se extrae la clave de intención y la actividad se inicia sin controles. Como resultado, para comenzar una actividad arbitraria con derechos del sistema, es suficiente construir el paquete de tal manera que el campo de intención esté oculto durante la primera deserialización y aparezca en la deserialización repetida. Y, como ya hemos visto, es precisamente esta tarea la que cumplen las vulnerabilidades de EvilParcel.
Por el momento, todas las vulnerabilidades conocidas de este tipo se corrigen mediante arreglos en las propias clases vulnerables Parcelable. Sin embargo, no puede descartarse la reaparición de clases vulnerables en el futuro. La implementación del Bundle y el mecanismo para agregar nuevas cuentas siguen siendo los mismos que antes. Todavía le permiten crear exactamente el mismo exploit cuando descubre (o nuevas) clases Parcelable vulnerables. Además, la implementación de estas clases todavía se realiza manualmente, y el programador debe vigilar la longitud constante del objeto Parcelable serializado. Y este es un factor humano con todas las consecuencias. Sin embargo, esperamos que tales errores sean lo menos posible y que las vulnerabilidades de EvilParcel no molesten a los usuarios de dispositivos Android.
Puede verificar las vulnerabilidades de EvilParcel en su dispositivo móvil utilizando nuestro antivirus
Dr.Web Security Space . El "Auditor de seguridad" incorporado informará sobre los problemas identificados y dará recomendaciones para resolverlos.