1 puntos por GN⁺ 2024-03-06 | 1 comentarios | Compartir por WhatsApp
  • El equipo de Texts.com intentó analizar Meta Messenger para macOS, una app de escritorio independiente similar a la suya, pero el certificate pinning bloqueaba el análisis MITM basado en proxy
  • El certificate pinning de Meta hace que la app confíe solo en los certificados permitidos, impidiendo interceptar y descifrar solicitudes con una autoridad certificadora creada por el usuario
  • La instrumentación dinámica basada en Frida provocaba muchos crashes y complicaba la distribución en Messenger, por lo que el equipo eligió un parche binario más pequeño y reproducible
  • El análisis con Hopper mostró que, al cambiar 4 bytes para que IsUsingSandbox() devuelva true, se podía tomar la ruta de código que desactiva la verificación SSL al usar un custom sandbox
  • Tras reemplazar el binario original con el ejecutable parcheado y gestionar la firma, las herramientas de proxy pudieron mostrar headers, cuerpo de las respuestas e información de las solicitudes

Por qué Texts.com analizó Messenger

  • Batuhan İçöz, a cargo de proyectos de la plataforma Meta en Texts.com, consideró que la app Messenger para macOS valía la pena analizarse porque es una app de escritorio independiente cercana al modelo de la empresa
  • Interceptar solicitudes de red tiene una barrera de entrada baja y sirve como primer paso para entender cómo funciona una app
  • Sin embargo, Meta aplica certificate pinning en la app para reforzar el modelo de seguridad, e incluso bloquea los análisis MITM que el usuario realiza sobre sí mismo

Qué bloquea el certificate pinning

  • Para interceptar solicitudes con un cliente proxy, el usuario debe configurar y confiar en una autoridad certificadora creada por él mismo
  • Con certificados emitidos por esa autoridad certificadora se puede interceptar y descifrar información de las solicitudes
  • Cuando un servicio implementa certificate pinning, la app solo acepta certificados emitidos por autoridades certificadoras específicas
  • En ese caso, el certificado creado por el usuario no es válido, por lo que no se pueden interceptar las solicitudes

Estado previo al parche y objetivo

  • Si no se desactiva el certificate pinning, todas las solicitudes devuelven “Internal Error
  • El software de proxy muestra “SSL Handshake Failed” y el ciclo de vida de la solicitud no avanza hasta el final
  • En este estado es difícil inferir el contenido de las solicitudes
  • El objetivo era poder leer directamente solicitudes, respuestas y headers en herramientas de depuración de red

Intentos de elusión fallidos y elección final

  • Un método que funcionó en el pasado consistía en cambiar cadenas de URL dentro del binario por un endpoint autoalojado que no implementara TLS
    • Ese endpoint reenviaba solicitudes y respuestas entre el cliente y el servidor
    • Es más adecuado para apps pequeñas que para una app grande como Messenger
  • Las librerías de instrumentación dinámica como Frida también eran candidatas, pero en Messenger resultaron poco estables
    • Había crashes frecuentes al aplicar hooks
    • El overhead dificultaba encontrar el punto problemático
    • La configuración del entorno y las herramientas necesarias para ejecutarlo complicaba distribuirlo al equipo
  • También probaron un script de Frida mantenido durante años
    • Este script se usaba con librerías comunes de certificate pinning y técnicas de elusión, y funcionaba en la mayoría de las apps
    • La familia de apps de Meta no estaba incluida en esa “mayoría”
  • Al final, el equipo eligió desactivar por completo el certificate pinning mediante un parche binario que pudiera compartirse fácilmente con otros integrantes

Punto de parche encontrado con Hopper

  • Tras descargar Messenger y moverlo a la carpeta de aplicaciones, importaron en Hopper el binario ARM compilado ubicado en /Applications/Messenger.app/Content/MacOS/Messenger
  • Hopper permite desensamblar, descompilar, recompilar, depurar y visualizar binarios compilados
  • Después de cargar el binario y las referencias, buscaron términos como certificate, ssl y pinning
  • La cadena "SSL pinning verification failed for host:" se convirtió en el punto de partida del análisis
  • Como un binario compilado puede crashear si se modifica demasiado, usaron una estrategia de aplicar el cambio más pequeño posible
    • El cambio ideal era un parche de alcance limitado, como invertir un valor booleano, invertir una condición o modificar unas pocas instrucciones

Hacer que IsUsingSandbox() siempre devuelva true

  • Visualizaron el flujo de ejecución con un grafo de flujo de control y siguieron hacia arriba las referencias conectadas
  • Encontraron la cadena "Using custom sandbox -> turn off SSL verification"
  • Buscaron en el archivo las referencias a la función que determinaba esta bandera y confirmaron esa referencia al inicio del procedimiento
  • Rastrearon el punto donde se asignaba el valor de retorno en la función IsUsingSandbox()
    • El registro w0 se devuelve después de recibir el valor movido desde w19
    • w19 originalmente se asignaba mediante una instrucción load byte
  • Si se evita cargar w19 y se lo configura siempre en true, IsUsingSandbox() devuelve true
  • Según la cadena encontrada antes, al usar un custom sandbox se desactiva la verificación SSL, por lo que este cambio desactiva el certificate pinning
Original
ARM: ldrb w19, [sp, #0x40 + var_20]
HEX: F3 83 40 39

Rewritten
ARM: mov w19, #1
HEX: 33 00 80 52
  • Este reemplazo se realizó modificando directamente el bytecode de la aplicación en modo hexadecimal

Resultado de ejecución y refirma

  • Exportaron un nuevo ejecutable con la opción “Produce New Executable” de Hopper
  • Después de quitar la firma del ejecutable, reemplazaron el binario original de Messenger por el nuevo binario
  • Al volver a ejecutar Messenger, la herramienta de proxy mostró headers, cuerpo de respuestas y otra información de solicitudes
  • De un tamaño total de binario de 97,477,728 bytes, bastó con modificar 4 bytes para hacer posible la interceptación de solicitudes
  • Si quieres ver un enfoque similar en iOS, puedes consultar el artículo de Hassan Mostafa de 2020 sobre cómo eludir el certificate pinning de Instagram
    • Ese artículo describe un caso en el que se desactivó el certificate pinning de Instagram invirtiendo una instrucción de bifurcación condicional en un iPhone con jailbreak
  • El binario compilado se le entregó a Batuhan
    • Batuhan consiguió e instaló un certificado de firma y luego firmó la aplicación
    • Después pudo usar ese binario en su propio sistema para ver sus propias solicitudes
codesign --force --deep -s CERTNAME_OR_ID /Applications/Messenger.app

1 comentarios

 
GN⁺ 2024-03-06
Opiniones en Hacker News
  • Iba por un camino parecido, pero me rendí justo cuando llegué al punto de intentar descompilar/modificar/recompilar.
    A ese nivel ya es pura perseverancia, y me da curiosidad cuántas horas le habrá tomado realmente. Yo había definido un criterio para detenerme y lo respeté.

    • Este artículo originalmente era una publicación interna de Texts.com, y al pulirlo para compartirlo quité la parte donde contaba que unas semanas antes yo había intentado exactamente el mismo enfoque y me había rendido al llegar al límite de tiempo que había fijado.
      Al principio pasé 2 horas probando varios cambios de comandos y me rendí. Después vi un artículo de ingeniería inversa de “Hassan Mostafa” (cyclon3), que había tenido éxito antes con el mismo método, aplicando Hopper Disassembler a Instagram en iOS, y esa noche volví a intentarlo, pero fallé. Incluso encontré y cambié el mismo comando.
      Entonces decidí dejarlo, y unas semanas después, todavía con algo de espina clavada, lo intenté de nuevo de forma improvisada; tras encontrar la función de sandbox, lo terminé en unos 30 minutos.
  • Parece que con eBPF se pueden leer los datos antes del cifrado TLS: Debugging with eBPF Part 3: Tracing SSL/TLS connections https://blog.px.dev/ebpf-openssl-tracing/

    • Es un método práctico, y casi seguro también se puede hacer de otra forma, como enganchar con Frida las funciones de envío y recepción de TLS. Aun así, si se logra eludir el certificate pinning, la ventaja es que el investigador puede hacer pasar el tráfico por herramientas existentes como Burp Suite o mitmproxy.
      Enrutar el tráfico real de la app hacia un proxy que lo intercepte reduce mucho el tiempo según el objetivo. Por ejemplo, si quieres cambiar automáticamente un solo parámetro de una solicitud que solo ocurre después de la autenticación/configuración de sesión, es mucho más rápido dejar que la app haga todo por su cuenta y cambiar una sola cosa en el proxy que escribir un cliente nuevo que ejecute todo el flujo inicial o programar la lógica de modificación con filtros eBPF.
    • Como referencia, este método no funciona con programas en Rust que enlazan estáticamente rustls, la biblioteca TLS de Rust más usada.
  • Es un enfoque muy ingenioso. Aunque creo que incluso en modo sandbox podrían haber forzado el certificate pinning.
    En la universidad intenté observar Snapchat con un ataque de intermediario, y recuerdo que también usaban certificate pinning, así que al final no pude resolverlo.

    • Yo intenté lo mismo y logré parchear la app para interceptar las solicitudes, pero me rendí intentando hacer ingeniería inversa del objeto compartido responsable de firmar las solicitudes.
      Ni siquiera pude encontrar el punto de entrada. Para ser una app de redes sociales relativamente pequeña, en 2015 ya tenía una seguridad absurdamente fuerte.
    • Si el usuario puede modificar el binario, en el fondo es difícil forzar el certificate pinning.
      Aunque hubieran usado certificate pinning en modo sandbox, probablemente había otra forma de eliminar la verificación del certificado fijado.
    • Exacto. Podrían haberlo implementado haciendo que, dentro de la función consumidora, la salida de la función del flag de sandbox se asignara siempre a true, pero en este caso este método también funcionó bien :)
    • Me da risa que tanta gente haya intentado esto en apps móviles y se haya topado con una pared.
      Aunque ya no es así.
  • Este artículo me recordó la época de +Orc. Parece que se ha perdido mucho conocimiento que antes era común, como encontrar una rama no deseada y convertirla en NOP.
    Es entendible, hoy hay muchas más habilidades que aprender.
    [1]: https://en.m.wikipedia.org/wiki/Old_Red_Cracker

    • Ver menciones a +Orc o Fravia (RIP) siempre me da nostalgia.
      Aun así, creo que todavía hay mucha gente haciendo parches NOP. Solo que la complejidad aumentó. Sigue habiendo gente que rompe DRM o investiga apps móviles arbitrarias con editores hexadecimales y herramientas similares.
      Los programas actuales son más complejos, así que empezar es más difícil, pero al mismo tiempo el conocimiento necesario es más accesible.
  • Si quieres interceptar tráfico de apps de Meta, no hace falta hacer todo eso.
    https://www.facebook.com/whitehat/bugbounty-education/261571...

    • Esto solo funciona en Android. No nos interesaba interceptar la app de Android.
  • Me pregunto si los checksums binarios en tiempo de ejecución habrían ayudado a dificultar este tipo de modificación.
    ¿No es una práctica estándar en apps móviles? ¿Los SDK de iOS o Android ofrecen algo así? Imagino que estaría vinculado al proceso oficial de lanzamiento y se impondría en sus respectivas plataformas sin jailbreak.
    Es una pregunta básica, pero como la solución final fue modificar unos pocos bytes del binario, parecía algo que se podría bloquear.

    • Es macOS (escritorio), no iOS (móvil).
    • De todos modos, si modificas el binario tienes que volver a firmarlo, así que termina teniendo el mismo efecto.
      En plataformas sin jailbreak, normalmente se hace con un certificado de desarrollador.
  • Las defensas contra ingeniería inversa de Meta, o al menos de Messenger, parecen bastante relajadas.
    Sin llegar a ofuscación avanzada, habría sido fácil eliminar por completo IsUsingSandbox() en el build de producción.

    • Cuando trabajé ahí, la defensa contra ingeniería inversa nunca fue un objetivo.
      El certificate pinning estaba pensado para dificultar la manipulación por parte de atacantes, no para dificultársela a los usuarios.
    • Las apps de Meta incluyen un menú de depuración completo incluso en builds de producción. Es muy probable que las cadenas que encontró el autor formen parte de ese menú.
  • La primera vez que crackeé una app, pensé que obviamente iba a fallar, pero resultó que encontrar un punto JNE/JEZ fácil de modificar así era más sencillo de lo que esperaba.
    Aunque elijas mal, vuelves al archivo original y pruebas otro punto.
    Esto parece algo que la IA podría automatizar fácilmente: invertir JEZ/JNZ en varios puntos candidatos, ejecutar la app y ver si aparece la pantalla molesta.

    • Esto no es exactamente un problema de IA, sino más bien de fuzzing.
      Si la condición de fallo está bien definida, al final solo hay que reducir los candidatos.
      Si la IA pudiera romper algo como Denuvo en cero disparos (0-shot), eso ya sería otra historia.
    • Herramientas así ya existían en los 90. No era IA, era simplemente fuerza bruta.
  • Me pregunto por qué una aplicación de una empresa tan grande no está completamente ofuscada y tampoco incluye suficientes protecciones para impedir la ejecución de binarios modificados.

    • Hablando desde la perspectiva de quien tomó esa decisión por primera vez en la app de Facebook: no vale la pena.
      Si se trata de una persona, grupo o gobierno con suficiente habilidad o motivación, tarde o temprano lo van a romper. Distribuir un binario de cliente tiene esa naturaleza.
      Podrías gastar muchísimo tiempo y dinero intentando impedirlo. Antes Pinterest quiso distribuir su propio lenguaje y máquina virtual, y yo me opuse. O simplemente aceptas que el código del cliente básicamente ya está comprometido, pones la lógica en el servidor y sigues adelante.
      El pinning de certificados es casi gratis y funciona como un mecanismo tipo “solo puedes subir si tienes al menos esta clave”. No es seguro, pero filtra intentos menores.
    • La ofuscación tiene un costo, y el pinning de certificados apunta más a dificultar ataques de intermediario perjudiciales para el usuario que a impedir la ingeniería inversa.
      Claro que también afecta la ingeniería inversa, pero eso es más bien un extra.
      Al final, el código se ejecuta en el dispositivo del usuario, y el usuario puede observar lo que hace el código, así que siempre es posible desofuscarlo. Si una persona lo resuelve y comparte el resultado, replicarlo también se vuelve muy fácil. No significa que la ofuscación no sirva, pero no es algo en lo que convenga invertir demasiado tiempo.
    • En apps móviles/frontend, aunque hagas eso, no sirve de mucho.
      Si el atacante puede acceder físicamente al dispositivo, en ese punto no hay forma de detenerlo. Lo único que puedes hacer es volver el proceso más molesto y esperar que se frustre y abandone.
    • Lo más probable es que sea un tema de prioridades y costo-beneficio.
      La ofuscación casi no habría afectado el resultado de este experimento, y quizá el enfoque habría cambiado hacia usar un poco más de instrumentación dinámica. La ofuscación más efectiva que he visto fue la ofuscación con VM, pero tiene un impacto considerable en el rendimiento. La ofuscación también dificulta la depuración normal.
      La prevención de binarios modificados se hace a nivel del sistema, y también puede implementarse a nivel de aplicación; de hecho es común. Pero esa función también se puede eludir, y después de que terminan las comprobaciones de seguridad se puede modificar con una biblioteca de instrumentación dinámica como Frida.
      Desde la perspectiva de Meta, no creo que lo mejor sea entrar en un juego del gato y el ratón con ingenieros inversos.
    • ¿Por qué no todos los bancos están protegidos como Fort Knox?
  • Me da curiosidad qué herramienta de proxy usaron en el artículo. Cuando está en ejecución, ¿enruta todo el tráfico de las aplicaciones por ahí?
    Perdón si es una pregunta tonta.

    • Buena pregunta. En el artículo usé Proxyman.
      En macOS enruta todo el tráfico de las aplicaciones por ahí, y también puedes usarlo como proxy para dispositivos iOS si instalas un certificado autofirmado en el dispositivo y lo conectas al proxy.