SSL pinning, I hate you!

NOTA: Ante todo destacar que esta publicación consiste en una recopilación de artículos que he usado (y alguna cosa con la que me he tenido que pegar). El contenido pertenece a otras personas y los enlaces a los originales se encuentran en esta misma publicación.

En lo que a la auditoría de aplicaciones para móviles Android se refiere, nunca me he metido demasiado, principalmente por falta de tiempo y porque al final aunque se pueda ver el código, cada desarrollador hace las cosas a su manera (las buenas manías de cada developer).

Hace un tiempo me encontré un caso de un cliente en el que, por necesidad (por co****s), había que hacer uso de una aplicación para analizar todo el flujo de trabajo (workflow para angloparlantes) de la solución diseñada. El problema de esto es que, pese a que la aplicación era accesible y su código (versión Android) también, se hacía pesadísimo el análisis de las funcionalidades, además de que prácticamente todas las interesantes estaban relacionadas con peticiones a diversos servidores.

Lo normal habría sido usar un proxy para poder ver las comunicaciones pero el SSLpinning es algo que ya está prácticamente en todas las aplicaciones de los markets y dado que Android desde su versión 6 hace caso omiso de los certificados de usuario pues…

En esta situación me topé con que tenía que bypassear (aka evadir/evitar para los hispanohablantes) el mecanismo de seguridad.
Mi primera intención fue modificar el código de la aplicación. De este modo, identifiqué que se hacía uso de OkHTTP3.

Así que, siguiendo uno de los tantos manuales para ello que hay en Internet, este por ejemplo, me dispuse a aplicar un “parche”. Después de identificar la parte correspondiente del código en el smali y modificarla (en varias ocasiones) al recompilar pues… no había forma de que funcionase.


Para qué engañarnos, hace cosa de 4 años que no he desarrollado nada de Android, y como he dicho antes cada desarrollador tiene sus mañas. Es probable que alguien con más experiencia no hubiese tenido problema en hacer esto.

La solución vino dada por Frida, recordé que meses atrás (año y pico) leí como en una con, habían empleado Burp y Frida para saltarse el SSLPinning. Me puse a leer y (EPIC WIN) encontré que había una forma de ver todo el tráfico de una aplicación haciendo uso de Frida y Burp. Aquí tenéis el link. El tuto está muy bien, pero me encontré con diversos “problemillas”, el principal debido a Android (la seguridad va mejorando). A causa de las nuevas versiones del sistema operativo, los certificados de usuario (en este caso) sirven para menos que un palo para hacer fotos. Así que tocaba poner el certificado propio (en este caso el generado por BURP) como CA, pero esto tiene truco, como leí a posteriori.

Llegados a este punto, nos ponemos manos a la obra:

Primer paso, generar el cert. En este caso con Burp por comodidad.

Una vez obtenido nuestro DER, será necesario “toquetearlo” un poco para que Android lo acepte. Por tanto habrá que hacer una conversión a PEM.

$ openssl x509 -inform DER -in cacert.der -out cacert.pem

Ahora, con el PEM ya generado (dada la amabilidad de Android) debemos darle un nombre concreto. Para ello, debemos obtener el hash del certificado, pero haciendo uso del algoritmo viejuno de openssl.

$ openssl x509 -inform PEM -subject_hash_old -in cacert.pem | head -1 9a5ba575

Ese hash que devuelve es el nombre que debemos darle al certificado (al PEM), pero acabado en 0.

9a5ba575.0

Y ahora es el momento de la “diversión”, o lo que es lo mismo, usar ADB root en una ROM de fabricante. En este caso todas las pruebas se han realizado haciendo uso de un BQ A4.5 con Android ONE en su versión 7.0 (la máxima disponible por parte del fabricante). Dado que es una ROM propia de BQ no es posible acceder desde ADB como root (adb root) pero sí que es posible elevar privilegios una vez hemos accedido a la shell del teléfono.

Pero antes hay que copiar el server de Frida que corresponda a nuestro terminal al dispositivo (realmente se puede copiar primero el cert creado, pero por mantener un orden y evitar reinicios tontos). Para identificar la versión adecuada hay que conocer el procesador empleado, para ello se ejecuta en la consola de Android:

getprop ro.product.cpu.abi

En el repositorio de Frida, se descarga la versión correspondiente del server, ARM en este caso y después de extraerlo:

./adb push ~/frida-server-android.arm /sdcard/Download/

Una vez copiado a la memoria del terminal, hay que copiar el servidor a una carpeta que permita ejecución,  /data/tmp/, cambiar el propietario (en mi caso) del usuario shell a root, y darle permisos de ejecución, como en cualquier máquina Linux.

Lo mismo con el cert que se ha creado:

 ./adb push ~/9a5ba575.0 /sdcard/Download/

Después de darle permisos (644), copiamos a la carpeta:

/system/etc/security/cacerts/

Ahora, viene el momento del reboot del móvil (o emulador). Se puede hacer con ADB, en el caso del emulador es mucho más cómodo a mi parecer.

Al iniciar el certificado que hemos metido, ya aparecerá dentro de las entidades certificadoras.


El siguiente paso, ejecutar el servidor de Frida. Entramos en la shell del terminal mediante ADB y elevamos privilegios. Vamos a /data/tmp y ejecutamos el servidor.

./frida-server-12.2.25-android-arm &

Si todo ha funcionado bien, al ejecutar en la terminal del equipo el siguiente comando, nos devolverá una lista con todos los servicios en ejecución en Android:

frida-ps -U

A continuación, viene la magia.

Java.perform(function() {                
 
    var array_list = Java.use("java.util.ArrayList");
    var ApiClient = Java.use('com.android.org.conscrypt.TrustManagerImpl');
 
    ApiClient.checkTrustedRecursive.implementation = function(a1,a2,a3,a4,a5,a6) {
            // console.log('Bypassing SSL Pinning');
            var k = array_list.$new(); 
            return k;
    }
 
},0);

Este fragmento de código, publicado por Mattia Vinci en techblog.mediaservice.net/ JS es el que permite hacer un bypass “fácilmente” del tan odiado SSL Pinning.

Pero primero habrá que arrancar BURP y «setear» la configuración de Android para que haga uso del proxy de éste.

Hecho esto, ya podemos ejecutar el JS indicando la aplicación que queremos auditar.

frida -U -l frida-ssl-pinning-bypass.js --no-paus -f org.s2grupo.valencia.APP

En el terminal se iniciará la aplicación en primer plano y las peticiones a servicios de esta aparecerán en BURP.

NOTA FINAL: Si la aplicación hace uso de servicios de Google (por ejemplo de la tienda de aplicaciones) o un WebView para autenticarse, será necesario deshabilitar el proxy de la configuración de Android. Aún investigo como poder pasarlo todo a BURP.