
Hola, mi nombre es Andrey Batutin, soy desarrollador sénior de iOS en DataArt. En un
artículo anterior, hablamos sobre cómo puede detectar el tráfico de nuestra aplicación móvil utilizando un proxy HTTPS. En esto, discutiremos cómo evitar el anclaje SSL. Por si acaso, te recomiendo leer el primer artículo si aún no lo has leído: será necesario para entender el texto a continuación.
En realidad, en la práctica, SSL Pinning se utiliza para que el método descrito para inspeccionar y modificar el tráfico de una aplicación móvil no sea accesible para los malos o un chef curioso.
¿Qué es el anclaje SSL?
En el artículo anterior, instalamos el Certificado Charles Root en nuestro dispositivo móvil, lo que permitió que nuestro Proxy Charles recibiera, descifrara, nos mostrara el tráfico, lo volviera a cifrar y lo enviara a Dropbox.
Si yo, como desarrollador de una aplicación móvil, quiero que mi tráfico sea inspeccionado solo por mi servidor y nadie más, incluso si este otro ha instalado su certificado SSL en el dispositivo, puedo usar el anclaje SSL.
Su esencia se reduce al hecho de que durante el
protocolo de enlace SSL, el cliente verifica el certificado recibido del servidor.
Este artículo analiza el método de fijación de SSL más fácil de implementar utilizando una lista permitida de certificados conectados a la aplicación (lista blanca).
Puede encontrar más información sobre los tipos de anclaje SSL
aquí .
Implementación de fijación de SSL en FoodSniffer
El código completo del proyecto está
aquí . Primero, necesitamos obtener dos certificados en formato DER para dos hosts:
El segundo servidor almacena JSON con una lista de nuestras compras.
Para obtener los certificados en el formato correcto, utilicé Mozila Firefox.
Abra dropbox.com en el navegador.
Haga clic en el símbolo del candado en la barra de direcciones.


Haga clic en Más información, seleccione Seguridad -> Ver certificado.

Luego seleccione Detalles y busque el certificado final en la Jerarquía de certificados.

Haga clic en Exportar y guarde en formato DER.

Repita el mismo procedimiento para uc9b17f7c7fce374f5e5efd0a422.dl.dropboxusercontent.com.
NotaEl servidor de contenido de Dropbox (* .dl.dropboxusercontent.com) usa un certificado comodín. Por lo tanto, el certificado que extrajo para el servidor uc9b17f7c7fce374f5e5efd0a422 será adecuado para cualquier otro servidor Dropbox * .dl.dropboxusercontent.com.
Como resultado, obtuve dos archivos con certificados:
dropboxcom.crt ,
dldropboxusercontentcom.crt ,
que agregué al proyecto de la aplicación FoodSniffer para iOS.

Luego agregué extensión para la clase FoodListAPIConsumer, en la que verifico el certificado recibido del servidor. Para hacer esto, lo busco en la lista de certificados permitidos, procesando el delegado de Authentication Challenge del protocolo NSURLSessionDelegate:
extension FoodListAPIConsumer { func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { guard let trust = challenge.protectionSpace.serverTrust else { completionHandler(.cancelAuthenticationChallenge, nil) return } let credential = URLCredential(trust: trust) if (validateTrustCertificateList(trust)) { completionHandler(.useCredential, credential) } else { completionHandler(.cancelAuthenticationChallenge, nil) } } func validateTrustCertificateList(_ trust:SecTrust) -> Bool{ for index in 0..<SecTrustGetCertificateCount(trust) { if let certificate = SecTrustGetCertificateAtIndex(trust, index){ let serverCertificateData = SecCertificateCopyData(certificate) as Data if ( certificates.contains(serverCertificateData) ){ return true } } } return false } }
En la matriz de
certificados , tengo representaciones de datos de mis certificados permitidos.
Ahora, cuando se ejecuta Charles Proxy, la aplicación se desconectará debido a que el certificado de Charles no está incluido en la lista de permitidos. El usuario verá el siguiente error:

¡Hackers derrotados!
Pero ahora hay un pequeño problema: ¿cómo puedo yo, el desarrollador, monitorear el tráfico HTTPS de mi propia aplicación?
Frida
Una opción es deshabilitar el anclaje SSL con el marco de inyección de código dinámico del marco de
Frida .
La idea es que el método
validateTrustCertificateList siempre devuelve verdadero durante el desarrollo de la aplicación.
Esto, por supuesto, se puede lograr sin la inyección de código dinámico, por ejemplo, usando la
condición #if targetEnvironment (simulador) para deshabilitar el
anclaje SSL en el simulador, pero es demasiado simple.
Usando Frida, podemos escribir un script JavaScript (hábilmente, ¿verdad?) En el que reemplazamos la implementación de validateTrustCertificateList con una que siempre devuelve verdadero.
Y este script se inyectará en la aplicación en la etapa de ejecución.
Cómo funciona Frida en iOS, puedes leer
aquí .
Instalación de Frida (tomada de aquí ).
sudo pip install frida-toolsGuión Frida
El script directo para reemplazar la función validateTrustCertificateList se ve así:
// Are we debugging it? DEBUG = true; function main() { // 1 var ValidateTrustCertificateList_prt = Module.findExportByName(null, "_T016FoodSnifferFrida0A15ListAPIConsumerC024validateTrustCertificateD0SbSo03SecG0CF"); if (ValidateTrustCertificateList_prt == null) { console.log("[!] FoodSniffer!validateTrustCertificateList(...) not found!"); return; } // 2 var ValidateTrustCertificateList = new NativeFunction(ValidateTrustCertificateList_prt, "int", ["pointer"]); // 3 Interceptor.replace(ValidateTrustCertificateList_prt, new NativeCallback(function(trust) { if (DEBUG) console.log("[*] ValidateTrustCertificateList(...) hit!"); return 1; }, "int", ["pointer"])); console.log("[*] ValidateTrustCertificateList(...) hooked. SSL pinnig is disabled."); } // Run the script main();
- Encontramos el puntero para validarTrustCertificateList en el binario de la aplicación por el nombre completo de la función.
- Envolvemos el puntero en un contenedor NativeFunction, que indica el tipo del parámetro y el valor de salida de la función.
- Reemplace la implementación de la función validateTrustCertificateList con una que siempre devuelva 1 (es decir, verdadero).
El script completo está en
{raíz_origen} /fridascrpts/killCertPinnig.js .
Uno de los problemas es cómo se
obtuvo el nombre completo de la función
_T016FoodSnifferFrida0A15ListAPIConsumerC024validateTrustCertificateD0SbSo03SecG0CFPara esto, utilicé la siguiente técnica.
- Creó un objetivo adicional FoodSnifferFrida en la aplicación.
- Conecté la biblioteca FridaGadget.dylib , que tomé aquí. El procedimiento de conexión de la biblioteca se describe con más detalle aquí.
- Lanzó la aplicación FoodSniffer en el simulador.
- Usó este comando para encontrar el nombre de función completamente calificado validateTrustCertificateList :
frida-trace -R -f re.frida.Gadget -i "* validateTrust *" - Lo tengo en la forma:

Y luego lo usó en
killCertPinnig.js .
Por qué un nombre tan "extraño" llegó al final y qué significan todos estos T016 y 0A15, se puede ver
aquí .
Fijación de asesinato SSL
¡Ahora finalmente lanza FoodSniffer con SSL Pinnig desactivado!
Lanzamiento Charles Proxy.
Ejecute el objetivo FoodSnifferFrida en el proyecto Xcode en el simulador. Deberíamos ver solo una pantalla blanca. La aplicación espera a que Frida se conecte a ella.

Ejecute Frida para ejecutar el script
killCertPinnig.js :
frida -R -f re.frida.Gadget -l ./fridascrpts/killCertPinnig.jsEsperemos la conexión a la aplicación iOS:

Continúe la aplicación con el comando% resume:

Ahora deberíamos ver la lista de alimentos en la aplicación:

Y JSON en Charles Proxy:

Beneficio!
Conclusión
Frida es como Wireshark para binarios. Funciona en plataformas iOS, Android, Linux, Windows. Este marco le permite rastrear llamadas a métodos y funciones, tanto del sistema como del usuario. Y también reemplazar los valores de parámetros, valores de retorno e implementación de funciones.
Pasar por alto SSL Pinning en un entorno de desarrollo con Frida puede parecer un poco
exagerado . Me atrae porque no necesito tener una lógica específica en la aplicación para la depuración y el desarrollo de aplicaciones. Esta lógica satura el código y, si se implementa incorrectamente, puede filtrarse en la versión de lanzamiento del ensamblaje (macros, ¡hola!).
Además, Frida es aplicable para Android. Lo que me da la oportunidad de facilitarle la vida a todo mi equipo y garantizar un proceso fluido de desarrollo de toda la línea de productos.
Frida se posiciona como una herramienta de inyección de código de proceso de caja negra. Con él, es posible, sin cambiar el código directo de la aplicación iOS, agregar el método de registro de llamadas al tiempo de ejecución, lo que puede ser indispensable al depurar errores complejos y raros.