Un tutorial para aplicar ingeniería inversa a la API privada de su software: hackear su sofá

Publicado: 2022-03-11

Viajar es mi pasión y soy un gran fanático de Couchsurfing. Couchsurfing es una comunidad global de viajeros, donde puedes encontrar un lugar para quedarte o compartir tu propia casa con otros viajeros. Además de eso, Couchsurfing lo ayuda a disfrutar de una experiencia de viaje genuina mientras interactúa con los lugareños. He estado involucrado con la comunidad de Couchsurfing por más de 3 años. Asistí a reuniones al principio, y luego finalmente pude hospedar personas. ¡Qué viaje tan increíble fue! Conocí a tanta gente increíble de todo el mundo e hice muchos amigos. Toda esta experiencia realmente cambió mi vida.

Yo mismo he hospedado a muchos viajeros, muchos más de los que he navegado hasta ahora. Mientras vivía en uno de los principales destinos turísticos de la Riviera francesa, recibí una enorme cantidad de solicitudes de sofás (hasta 10 por día durante la temporada alta). Como desarrollador back-end independiente, inmediatamente me di cuenta de que el problema con el sitio web couchsurfing.com es que en realidad no maneja estos casos de "carga alta" correctamente. No hay información sobre la disponibilidad de su sofá: cuando recibe una nueva solicitud de sofá, no puede estar seguro de si ya está alojando a alguien en ese momento. Debe haber una representación visual de sus solicitudes aceptadas y pendientes, para que pueda administrarlas mejor. Además, si pudiera hacer pública la disponibilidad de su sofá, podría evitar solicitudes de sofá innecesarias. Para entender mejor lo que tengo en mente, echa un vistazo al calendario de Airbnb.

Muchas empresas son conocidas por no escuchar a sus usuarios. Conociendo la historia de Couchsurfing, no podía contar con ellos para implementar esta función en el corto plazo. Desde que el sitio web se convirtió en una empresa con fines de lucro, la comunidad se deterioró. Para entender mejor de lo que estoy hablando, sugiero leer estos dos artículos:

  • http://www.nithincoca.com/2013/03/27/el-aumento-y-la-caida-del-couchsurfing/
  • http://mechanicalbrain.wordpress.com/2013/03/04/couchsurfing-a-sad-end-to-a-great-idea/

Sabía que muchos miembros de la comunidad estarían felices de tener esta funcionalidad. Entonces, decidí hacer una aplicación para resolver este problema. Resulta que no hay una API pública de Couchsurfing disponible. Aquí está la respuesta que he recibido de su equipo de soporte:

“Lamentablemente, tenemos que informarles que nuestra API no es pública en realidad y no hay planes en este momento para hacerla pública”.

irrumpiendo en mi sofá

Era hora de usar algunas de mis técnicas favoritas de ingeniería inversa de software para ingresar a Couchsurfing.com. Asumí que sus aplicaciones móviles deben usar algún tipo de API para consultar el backend. Entonces, tuve que interceptar las solicitudes HTTP provenientes de una aplicación móvil al backend. Para ello configuré un proxy en la red local y conecté mi iPhone a él para interceptar las solicitudes HTTP. De esta manera, pude encontrar puntos de acceso de su API privada y descifrar su formato de carga útil JSON.

Finalmente, creé un sitio web que tiene el propósito de ayudar a las personas a administrar sus solicitudes de sofás y mostrar a los navegantes un calendario de disponibilidad de sofás. Publiqué un enlace a él en los foros de la comunidad (que también están bastante segmentados en mi opinión, y es difícil encontrar información allí). La recepción fue mayormente positiva, aunque a algunas personas no les gustó la idea de que el sitio web requiriera credenciales de couchsurfing.com, lo cual era realmente una cuestión de confianza.

El sitio web funcionaba así: inicia sesión en el sitio web con sus credenciales de couchsurfing.com y, después de unos pocos clics, obtiene el código html que puede incrustar en su perfil de couchsurfing.com y listo: tiene un calendario actualizado automáticamente en tu perfil. A continuación se muestra la captura de pantalla del calendario y aquí los artículos sobre cómo lo hice:

  • https://github.com/nderkach/couchsurfing-python

Ejemplo de calendario

Creé una excelente función para Couchsurfing y, naturalmente, supuse que apreciarían mi trabajo, tal vez incluso me ofrecerían un puesto en su equipo de desarrollo. Envié un correo electrónico a jobs(at)couchsurfing.com con un enlace al sitio web, mi currículum y una referencia. Una nota de agradecimiento dejada por uno de mis invitados de couchsurfing:

Nota de agradecimiento.

Unos días después, dieron seguimiento a mis esfuerzos de ingeniería inversa. En la respuesta quedó claro que lo único que les preocupaba era su propia seguridad, por lo que me pidieron que eliminara las publicaciones del blog que había escrito sobre la API y, finalmente, el sitio web. Eliminé las publicaciones de inmediato, ya que mi intención no era violar los términos de uso y buscar las credenciales de los usuarios, sino ayudar a la comunidad de couchsurfing. Tuve la impresión de que me trataban como a un delincuente y la empresa se centró únicamente en el hecho de que mi sitio web requiere credenciales de usuario.

Les propuse darles mi aplicación gratis. Podrían alojarlo en su entorno y conectarlo a través de la autenticación de Facebook. Después de todo, es una gran característica y la comunidad la necesitaba. Aquí está la resolución final que recibí:

“Estamos volviendo al ritmo de las cosas aquí después de las vacaciones y queríamos hacer un seguimiento.

Tuvimos una discusión interna sobre su aplicación y cómo podemos honrar la creatividad y la iniciativa que muestra sin comprometer potencialmente la privacidad y seguridad de los datos de los usuarios de Couchsurfing cuando ingresan sus credenciales en un sitio de terceros.

El calendario claramente llena un vacío de funciones en nuestro sitio, una función que es parte de un proyecto más grande en el que estamos trabajando ahora.

Pero el problema de recopilar nombres de usuario y contraseñas permanece. No pudimos encontrar una manera fácil de configurarlo para que podamos alojarlo o brindarle soporte sin permitirle acceder a esos datos o que su sitio se vea como nuestro producto de trabajo.

La API que está actualmente disponible pronto será reemplazada por una versión que requerirá autenticación/autorización de las aplicaciones que acceden a ella”.

Hoy, mientras escribo este tutorial de software de ingeniería inversa (un año después de los eventos), la función de calendario todavía no está implementada en Couchsurfing.

Regreso a la inocencia - Hackeando mi sofá, otra vez

Hace unas semanas me inspiré para escribir un artículo sobre las técnicas de ingeniería inversa de API privadas. Naturalmente, decidí resumir los artículos anteriores que he escrito sobre este tema y agregar algunos detalles más. Cuando comencé a escribir el nuevo artículo, quería mostrar el proceso de ingeniería inversa con una API actualizada y tomar otro trozo de piratería de API. En base a mi experiencia previa y al hecho de que Couchsurfing anunció recientemente un sitio web y una aplicación móvil completamente nuevos http://blog.couchsurfing.com/the-future-of-couchsurfing-is-on-the-way/, he decidió piratear su API nuevamente.

¿Por qué estoy haciendo este proceso de ingeniería inversa? Bueno, en primer lugar, es muy divertido aplicar ingeniería inversa al software en general. Lo que más me gusta de él es que no involucra solo tu habilidad técnica, sino también tu intuición. A veces, la mejor manera de resolver las cosas es hacer una conjetura: te ahorrará mucho tiempo en comparación con la fuerza bruta. Recientemente escuché una historia de una empresa que tenía que trabajar con API propietarias y poca o ninguna documentación. Habían tenido problemas para descifrar la carga útil de la respuesta de la API en un formato desconocido durante días, luego alguien decidió probar ?decode=true al final de la URL y tenían un JSON adecuado. A veces, si tiene suerte, todo lo que necesita hacer es embellecer la respuesta JSON.

Otra razón por la que estoy haciendo este tutorial es que a algunas empresas les toma mucho tiempo adoptar una función particular solicitada por sus usuarios. En lugar de esperar a que se implemente, puede aprovechar el poder de su API privada y crearla usted mismo.

Entonces, con la nueva API de couchsurfing.com, comencé con un enfoque similar e instalé su última aplicación para iOS.

Primero, debe configurar un proxy en su LAN para falsificar solicitudes HTTP provenientes de la aplicación a la API mediante la realización de un ataque de intermediario (MITM).

Para las conexiones no encriptadas, el ataque es bastante simple: un cliente se conecta al proxy y usted transmite las solicitudes entrantes al servidor de destino de un lado a otro. Posiblemente podría modificar la carga útil, si es necesario. En una WLAN pública, es bastante fácil realizar esto disfrazado haciéndose pasar por el enrutador WiFi.

Para las conexiones encriptadas, hay una pequeña diferencia: todas las solicitudes están encriptadas de extremo a extremo. no es posible que el atacante descifre el mensaje, a menos que de alguna manera obtenga acceso a la clave privada (que, por supuesto, no se envía durante estas interacciones). Habiendo dicho eso, aunque el canal de comunicación de la API es seguro, los puntos finales, especialmente el cliente, no son tan seguros.

Se deben cumplir las siguientes condiciones para que SSL funcione correctamente:

  • El certificado del servidor debe estar firmado con una autoridad de certificación (CA) de confianza.
  • El nombre común del servidor, en el certificado, debe coincidir con el nombre de dominio del servidor

Para superar el cifrado en un ataque MITM, nuestro Proxy debe actuar como una CA (Autoridad de certificación) y generar certificados sobre la marcha. Por ejemplo, si un cliente intenta conectarse a www.google.com, el proxy crea dinámicamente un certificado para www.google.com y lo firma. Ahora, el cliente piensa que el proxy es en realidad www.google.com

Este diagrama describe los pasos para aplicar ingeniería inversa a una API privada.

Para implementar un proxy de detección utilizado para aplicar ingeniería inversa a la API privada, usaré una herramienta llamada mitmproxy. Puede usar cualquier otro proxy HTTPS transparente. Charles es otro ejemplo con una buena GUI. Para que esto funcione, necesitamos configurar las siguientes cosas:

Configure la puerta de enlace predeterminada de la conexión WiFi de su teléfono para que sea el proxy (para que el proxy esté en el medio y todos los paquetes pasen) Instale el certificado del proxy en el teléfono (para que el cliente tenga la clave pública del proxy en su almacén de confianza)

Consulte la documentación de su proxy sobre la instalación del certificado. Aquí están las instrucciones para mitmproxy. Y aquí está el archivo PEM del certificado para iOS.

Para monitorear las solicitudes HTTP interceptadas, simplemente inicie mitmproxy y conéctese desde su teléfono móvil (el puerto predeterminado es 8080).

Configuración del teléfono móvil.

Abra un sitio web en su navegador móvil. En este punto, debería poder ver el tráfico en mitmproxy.

Una vez que haya confirmado que todo funciona, puede comenzar la ingeniería de software inversa.

Una vez que se asegure de que todo funciona según lo planeado, es hora de comenzar a explorar la API privada de su elección. Básicamente, en este punto, puede simplemente abrir la aplicación, jugar con ella y tener una idea sobre los puntos finales de la API y la estructura de la solicitud.

No existe un algoritmo estricto sobre cómo aplicar ingeniería inversa a una API de software: la mayoría de las veces confía en su intuición y hace suposiciones.

Mi enfoque es replicar las llamadas a la API y jugar con diferentes opciones. Un buen comienzo es reproducir una solicitud que capturó en mitmproxy y ver si funciona (presione 'r' para reproducir una solicitud). El primer paso es averiguar qué encabezados son obligatorios. Es bastante conveniente jugar con encabezados con mitmproxy: presione 'e' para ingresar al modo de edición, luego 'h' para modificar los encabezados. Con los atajos que usan, los adictos a vim se sentirían como en casa. También puede usar extensiones de navegador como Postman para probar la API, pero tienden a agregar encabezados innecesarios, por lo que sugiero apegarse a mitmproxy o curl.

Hice un script que lee el archivo de volcado mitmproxy y genera una cadena curl: https://gist.github.com/nderkach/bdb31b04fb1e69fa5346

Comencemos con la solicitud enviada cuando está iniciando sesión.

 POST https://hapi.couchsurfing.com/api/v2/sessions ← 200 application/json 

El primer paso en este tutorial de ingeniería inversa es replicar las llamadas a la API y jugar con las opciones resultantes.

Lo primero que noté es que cada solicitud contiene un encabezado obligatorio X-CS-Url-Signature que es diferente cada vez. También traté de reproducir una solicitud después de un tiempo para verificar si hay una verificación de marca de tiempo en el servidor, y no hay ninguna. Lo siguiente que debe hacer es averiguar cómo se calcula esta firma.

En este punto, decidí aplicar ingeniería inversa al binario y descifrar el algoritmo. Naturalmente, teniendo experiencia en el desarrollo para iPhone y teniendo un iPhone a mi disposición, decidí comenzar con el iPhone ipa (aplicación de iPhone entregable). Resulta que para descifrar uno, necesito un teléfono con jailbreak. ¡Detener! Hora del martillo.

Luego, recordé que también tienen una aplicación para Android. Dudé un poco en probar este enfoque, ya que no sé nada sobre Android o Java. Entonces pensé que sería una buena oportunidad para aprender algo nuevo. Resultó ser más fácil obtener un código casi fuente legible por humanos al descompilar el código de bytes de Java que el código de máquina de iPhone altamente optimizado.

Apk (aplicación de Android entregable) es básicamente un archivo zip. Puede usar cualquier extractor de zip para descomprimir su contenido. Encontrará un archivo llamado classes.dex, que es un código de bytes de Dalvik. Dalvik es una máquina virtual que se utiliza para ejecutar código de bytes de Java traducido en Android.

Para descompilar el archivo .dex en el código fuente .java, utilicé la herramienta llamada dex2jar. El resultado de esta herramienta es un archivo jar, que puede descompilar con una variedad de herramientas. Incluso puede abrir un jar en Eclipse o IntelliJ IDEA y hará todo el trabajo por usted. La mayoría de estas herramientas producen un resultado similar. Realmente no nos importa si podemos volver a compilarlo para ejecutarlo, simplemente lo estamos usando para analizar el código fuente.

Aquí hay una lista de herramientas que he probado:

  • FernFlower (ahora parte de IntelliJ IDEA)
  • CFR
  • JD-GUI
  • Krakatoa
  • Proción

CFR y FernFlower me funcionaron mejor. JD-GUI no pudo descompilar algunas partes críticas del código y fue inútil, mientras que las otras tenían la misma calidad. Afortunadamente, parece que el código del código Java no ha sido ofuscado, pero hay herramientas como ProGuard http://developer.android.com/tools/help/proguard.html para ayudarlo a desofuscar el código.

La descompilación de Java no es realmente el alcance de este tutorial de ingeniería inversa; hay mucho escrito sobre este tema, así que supongamos que descompiló y desofuscó con éxito su código Java.

He combinado todo el código relevante utilizado para calcular X-CS-Url-Signature en la siguiente esencia: https://gist.github.com/nderkach/d11540e9af322f1c1c74

En primer lugar, busqué menciones de X-CS-Url-Signature , que encontré en RetrofitHttpClient . Una llamada en particular parecía interesante: al módulo EncUtils . Al investigarlo, me di cuenta de que están usando HMAC SHA1. HMAC es un código de autenticación de mensajes que utiliza una función criptográfica (SHA1 en este caso) para calcular un hash de un mensaje. Se utiliza para garantizar la integridad (es decir, para evitar que un intermediario modifique la solicitud) y la autenticación.

Necesitamos dos cosas para calcular la X-CS-Url-Signature : la clave privada y el mensaje codificado (probablemente alguna variación de la URL y la carga útil de la solicitud HTTP).

 final String a2 = EncUtils.a(EncUtils.a(a, s)); final ArrayList<Header> list = new ArrayList<Header>(request.getHeaders()); list.add(new Header("X-CS-Url-Signature", a2));

En el código a es un mensaje y s es la clave que se usa para calcular el encabezado a2 (la doble llamada a EncUtils solo calcula un resumen hexadecimal HMAC SHA1).

Encontrar la clave no fue un problema: se almacenó en texto sin formato en ApiModule y se usó para inicializar el segundo parámetro de RetrofitHttpClient.

 RetrofitHttpClient a(OkHttpClient okHttpClient) { return new RetrofitHttpClient(okHttpClient, "v3#!R3v44y3ZsJykkb$E@CG#XreXeGCh"); }

Si observamos la llamada a EncUtils , podemos ver que el literal de cadena anterior se usa textualmente como clave para calcular el HMAC, excepto en el caso en que se define this.b En el último caso, this.b se agrega con un punto.

 String s; if (this.b == null) { s = this.a; } else { s = this.a + "." + this.b; }

Ahora, con solo mirar el código, no me quedó claro dónde y cómo se inicializa this.b (lo único que pude descubrir es que se llama en un método con una firma this.a(String b) , pero no pude encontrar una llamada en ninguna parte del código).

 public void a(final String b) { this.b = b; }

Te animo a que lo descompiles y lo descubras por ti mismo :)

Descubrir el mensaje fue bastante sencillo: en el código puede ver que es una concatenación de la ruta de la URL, es decir, /api/v2/sessions y una cadena con carga JSON (si corresponde).

 final byte[] b = this.b(request.getUrl()); byte[] a; if (request.getBody() != null && request.getBody() instanceof JsonTypedOutput) { System.out.println("body"); // this.a(x, y) concatenates byte arrays a = this.a(b, ((JsonTypedOutput)request.getBody()).a); } else { a = b; }

Con solo mirar el código, fue difícil descubrir el algoritmo exacto para el cálculo de HMAC. Entonces, decidí reconstruir la aplicación con símbolos de depuración para descubrir exactamente cómo funciona la aplicación. Usé una herramienta llamada apktool https://code.google.com/p/android-apktool/ para desarmar el bytecode de Dalvik usando smali https://code.google.com/p/smali/. Seguí la guía en https://code.google.com/p/android-apktool/wiki/SmaliDebugging

Después de compilar el apk, debe iniciar sesión e instalarlo en su dispositivo. Como no tenía un dispositivo Android, usé el emulador que viene con el SDK de Android. Con un poco de alimentación con cuchara, así es como lo hace:

 jarsigner -verbose -keystore ~/.android/debug.keystore -storepass android -keypass android <path_to_your_built_apk> androiddebugkey jarsigner -verify -verbose -certs <path_to_your_built_apk> zipalign -v 4 <path_to_your_built_apk> <path_to_your_output_signed_apk>

Usé un emulador de Android incorporado que viene con el SDK y una imagen virtual Atom x86 con HAXM habilitado para garantizar que funcione sin problemas.

 tools/emulator -avd mydroid -no-boot-anim -cpu-delay 0

Aquí hay una buena guía sobre cómo configurar una imagen virtual: http://jolicode.com/blog/speed-up-your-android-emulator

Asegúrese de ver que la línea HAX está funcionando y que el emulador se ejecuta en modo virtual rápido al iniciar el emulador para asegurarse de que HAXM está habilitado.

Luego, instalé el apk en el emulador y ejecuté la aplicación. Siguiendo la guía de apktool, aproveché el depurador remoto IntelliJ IDEA para conectarme al emulador y establecer algunos puntos de interrupción de línea:

Algunas técnicas de ingeniería inversa implican ejecutar la aplicación y simplemente ver qué sucede.

Jugando un poco con la aplicación, pude darme cuenta de que la clave privada utilizada para inicializar RetrofitHttpClient se usa para calcular el HMAC de una firma de solicitud de inicio de sesión. En la respuesta al POST de inicio de sesión, recibe una identificación de usuario y un token de acceso ( X-Access-Token ). El token de acceso se utiliza para autorizar todas las siguientes solicitudes. El HMAC para todas las solicitudes posteriores al inicio de sesión se construye de la misma manera que la solicitud de inicio de sesión, excepto que la clave se compone agregando .<user_id> a la clave privada original.

Esto muestra el proceso de autorización necesario para aplicar ingeniería inversa a esta API privada.

Una vez que está autorizado, la aplicación envía la siguiente solicitud:

 POST https://hapi.couchsurfing.com/api/v2/users/1003669205/registerDevice ← 200 application/json

Como pude deducir empíricamente, esta solicitud es opcional para la autenticación. ¡Puntos de bonificación si descubres para qué se usa!

Una vez autenticado, puede enviar una solicitud para recuperar su perfil de usuario (o el de cualquier otra persona), así:

 GET https://hapi.couchsurfing.com/api/v2/users/1003669205 ← 200 application/json 

En este proceso de ingeniería inversa, puede obtener el perfil de usuario de cualquier persona.

No entré mucho en detalles, pero noté que un perfil se actualiza con una solicitud PUT. Solo por diversión, intenté actualizar otro perfil con la misma solicitud; no estaba autorizado, por lo que aparentemente se implementaron los conceptos básicos de seguridad.

Escribí un script de Python simple para iniciar sesión con sus credenciales de couchsurfing.com y obtener su perfil de usuario: https://gist.github.com/nderkach/899281d7e6dd0d497533. Aquí está el envoltorio de Python para la API: https://github.com/nderkach/couchsurfing-python con un paquete disponible en el repositorio de pypi (pip install couchsurfing).

Próximos pasos

No estoy seguro de qué voy a hacer exactamente con la API esta vez. El código HTML en los perfiles de usuario ya no está permitido, por lo que tendré que pensar en un enfoque diferente para el problema anterior. Continuaré desarrollando y mejorando el envoltorio API de python, si hay una demanda para ello, y suponiendo que couchsurfing.com no cause demasiados problemas. No exploré demasiado la API y solo la probé en busca de algunas vulnerabilidades básicas. Parece lo suficientemente seguro, pero sería interesante averiguar si puede obtener acceso a los datos que no están disponibles a través del sitio web. De cualquier manera, ahora puede usar mi ingeniería de software inversa para crear un cliente alternativo para Windows Phone, Pebble o su sofá inteligente.

Cierre con una pregunta

Hay una discusión que me gustaría abrir: ¿por qué no publicar su API y hacerla pública? Incluso si no lograra piratear la API, aún sería posible raspar el sitio web. Sería más lento y más difícil de mantener, pero seguramente preferirían que los consumidores usaran una API en lugar de un web scraper. La disponibilidad de las API permitiría a los desarrolladores externos mejorar el producto de la empresa y crear un servicio de valor agregado a su alrededor. Se puede argumentar que sería más costoso mantener la API pública en lugar de una privada; pero, de nuevo, las ventajas de los servicios de construcción de su comunidad además de su producto superarían los costos de mantenimiento de la API.

¿Es posible evitar por completo el uso de una API privada por parte de clientes de terceros? No lo creo. El uso de la fijación de SSL evitaría olfatear las solicitudes de API utilizando una técnica de proxy transparente simple como se describió anteriormente. Al final, incluso si ofusca el binario, un pirata informático motivado con algunos recursos y tiempo siempre podrá aplicar ingeniería inversa al binario de la aplicación y obtener la clave/certificado privado. Creo que la suposición de que el punto final del cliente es seguro es inherentemente incorrecta. Un cliente API es un punto débil.

Al mantener una API privada, una empresa básicamente transmite un mensaje de desconfianza a sus usuarios. Seguramente, puede intentar proteger aún más su API privada. Sin embargo, ¿no preferiría implementar una seguridad básica para la API para evitar el uso malintencionado? y, en su lugar, concentrar sus recursos en mejorar el software para brindar una mejor experiencia de usuario?

Couchsurfing, bastante por favor, con azúcar encima, abra la API.