Hackear WhatsApp, parte 2: analizar el protocolo VOIP de Whatsapp





En este artículo, quiero contarles cómo descifré varias partes del protocolo VoIP de WhatsApp usando un dispositivo iOS con jailbreak y un conjunto de diferentes programas de análisis.



Recientemente, Whatsapp ha recibido mucha atención debido a las vulnerabilidades y oportunidades para los piratas informáticos.

Desde este punto de vista, es muy interesante para la investigación sobre su seguridad.



Todos los que también estén interesados ​​en esto, bienvenidos debajo del gato.





Aunque las páginas oficiales de Whatsapp tienen una descripción de su cifrado, de hecho, no hay información detallada en ninguna parte sobre cómo funciona y cómo se implementa en el protocolo.

Por lo tanto, no hay base para un análisis de seguridad detallado en Whatsapp.



Mi investigación se basó en tres cosas:



1. Análisis de tráfico de red

2. Análisis de binarios

3. Análisis de comportamiento de aplicaciones en diferentes modos



Kit de herramientas



Para analizar el cliente Wahtsapp para iOS, utilicé las siguientes herramientas:



- Descriptor binario - bfdecrypt

- Desensamblador de archivos binarios - Desmontador Hopper y radare2

- Análisis de tráfico de red - Wireshark

- Análisis de acciones de aplicaciones - Frida



Cómo configuro el jailbreak en iOS está más allá del alcance de este artículo.



Analizando el tráfico de la red



En esta parte analizaremos el tráfico de la red del cliente de Whatsapp durante una llamada, que registraremos mediante Wireshark.



Para registrar dicho tráfico, creé una interfaz de red virtual remota.



El comando para Makos tiene este aspecto:



rvictl -s Aquí, el UUID del dispositivo debe reemplazarse con el UUID del dispositivo con el cliente watsap.



Wireshark detecta el uso de utilidades de recorrido de sesión para NAT (STUN).

STUN es un protocolo de señalización que se requiere para establecer conexiones peer-to-peer entre clientes.







Aquí el cliente de WhatsApp utiliza paquetes TCP para comunicarse con diferentes servidores Watzap.

Al mismo tiempo, los paquetes UDP se utilizan para el intercambio entre clientes.

Cientos de paquetes UDP pasan en un minuto.

Vatsap utiliza el Protocolo de tiempo real seguro (SRTP) y es obvio que estos paquetes UDP contienen datos SRTP sobre la llamada.



El protocolo SRTP proporciona cifrado, autenticación y protección contra ataques de reproducción en el tráfico RTP.



Echemos un vistazo más de cerca a los paquetes SRTP intercambiados entre los lados A y B.

Para hacer esto, conviértalos a hexadecimal:







Se puede ver que los campos contienen encabezados RTP específicos de SRTP.



Los primeros cuatro bytes (resaltados en rojo) son los 7 campos de encabezado RTP.



Considérelos con más detalle:



0x8078001e = 0b10_0_0_0000_0_111100_00000000000011110 = V = 10 | P = 0 | X = 0 | CC = 0000 | M = 0 | PT = 111100 | SEQ = 00000000000011110







Los primeros 2 bits contienen el número de versión (V), en nuestro caso es la segunda versión.

El tercer bit es un campo para información opcional, en nuestro caso está vacío.

El cuarto bit - el campo de extensión (X) indica que en este caso no hay otros encabezados después del encabezado del paquete RTP.



Bits 5 a 8: contiene el número de identificadores CSRC que siguen al encabezado permanente.

CSRC (fuente contribuyente) es la fuente del flujo de paquetes RTP que contribuye al flujo total generado por el mezclador RTP. El mezclador inserta una lista de identificadores SSRC que identifican fuentes parciales en el encabezado de los paquetes RTP. Esta lista se llama lista CSRC. Por ejemplo, una audioconferencia en la que el mezclador marca a todos los oradores cuya voz genera paquetes salientes. Esto permite que el lado receptor identifique al hablante, aunque todos los paquetes tienen el mismo ID SSRC.



8 bits es un marcador de bits (M). Usado a nivel de aplicación y determinado por el perfil. Si se establece este campo, los datos del paquete tienen un significado especial para la aplicación.



Los siguientes 6 bits son códigos de tipo de datos adicionales. Estos datos no están definidos en los estándares RTP y SRTP. El significado de estos bits es probablemente uno personalizado elegido por Whatsapp.



Los últimos 17 bits indican la fuente del reloj. El número se incrementa en orden en 1 cuando se envía el siguiente paquete de datos RTP, este código puede ser utilizado por el receptor para registrar pérdidas de paquetes y restaurar el verdadero orden de los fragmentos enviados. Según el estándar, el valor inicial del código es aleatorio, pero el watsap no cumple esta recomendación, porque como podemos ver en los datos de Wireshark, el valor inicial del watsap es siempre 0.Los



siguientes 4 bytes (resaltados en azul) son la marca de tiempo del paquete.



4 bytes a partir de entonces (verde) - campo SSRC. Identifica la fuente de tiempo. Este identificador se elige al azar para que no haya dos códigos SSRC iguales dentro de una sesión RTP. Todas las aplicaciones deben poder detectar cuándo los SSRC son iguales. Si el remitente cambia su dirección de transporte, también debe cambiar el identificador SSRC.



Entonces, descubrimos que Whatsapp usa SRTP para proteger las llamadas.

Esto lo confirma la estructura de los paquetes UDP intercambiados entre los clientes Watcap.



Además, Watzap utiliza el protocolo TCP para intercambiar datos entre el cliente y el servidor.

A continuación, veremos cómo se utiliza el protocolo Noise Pipes para cifrar esta parte. Análisis binario







El cliente de WhatsApp para iOS contiene 2 binarios principales: el binario de la aplicación WhatsApp y el marco principal de WhatsApp.



En esta parte, los veremos más de cerca con el desensamblador de tolva y radare2.



Estos binarios se cifran cuando se descargan de la tienda de aplicaciones.

Aquí engañamos a Apple para que hiciera jailbreak a un dispositivo iOS y obtuviera acceso a estos archivos.



También agregue que estos binarios de Whatsapp fueron descifrados con bfdecrypt.



A continuación, le mostraré cómo reuní información sobre los fundamentos del protocolo, los algoritmos y las bibliotecas de código abierto que usa Whatsapp.



Las bibliotecas de código abierto son especialmente interesantes porque se pueden analizar fácilmente.



libsignal-protocol-c



Watsap usa libsignal-protocol-c, una biblioteca de código abierto, que implementó en el Protocolo de señal.



El protocolo se basa en el algoritmo Double Ratchet, que cifra los mensajes watsap.



Esta biblioteca se encontró en los binarios de Whatsapp para las siguientes características:



r2 WhatsAppCore

[0x0082b517]> / _signal_

Buscando 8 bytes en [0x0-0x654000]

hits: 33

0x00837a7b hit2_0 .il_key_data_from_signal_keydispatch_.

0x0083df33 hit2_1 ._torlice_signal_protocol_paramet.

0x008407c0 hit2_2 ​​.d_fac_3key_signal_message_big.

0x00840d50 hit2_3 .mmetric_signal_protocol_paramet.

0x00840e70 hit2_4 .ob_signal_protocol_paramet.

0x00841492 hit2_5 .pre_key_signal_messagesigna.

0x008de24b hit2_6 .agc_reset_alice_signal_protocol_paramet.

0x008de274 hit2_7 .rs_create_alice_signal_protocol_paramet.

0x008de440 hit2_8 .bitno_MRDTX_bob_signal_protocol_paramet.

0x008de467 hit2_9 .ters_create_bob_signal_protocol_paramet.

0x008e311c hit2_10 .pre_big_pre_key_signal_message_copy_pr.

0x008e3139 hit2_11 .ge_copy_pre_key_signal_message_create_.

0x008e3158 hit2_12 ._create_pre_key_signal_message_deserial.

0x008e317c hit2_13 .rialize_pre_key_signal_message_destroy.libsrtp




libsrtp



Watcap también usa libsrtp para implementar su protocolo de tiempo real seguro.

Los nombres de los símbolos se han eliminado de los binarios de Whatsapp, pero a pesar de esto, los binarios contienen líneas que indican directamente su enlace a libsrtp:



r2 WhatsApp

[0x1001ada34]> / libsrtp

0x100ee5546 hit1_0 .rc% 08XUnknown libsrtp error% duns.

0x100ee57eb hit1_1 .d para inicializar libsrtp:% sFailed to r.

0x100ee580a hit1_2 .led para registrar libsrtp deinit.Failed.

0x100ee5831 hit1_3. Para desinicializar libsrtp:% sAES_CM_128_.

0x100ee5883 hit1_4 .ck crypto Init libsrtp. crear grupo ..

0x100f07b80 hit1_5. paquete:% slibsrtpstat prueba% s: c.





Además, los binarios de watsap contienen cadenas que se utilizan en el código sin formato de libsrtp, por ejemplo, "secuencia de clonación (SSRC: 0x% 08x)":



r2 WhatsApp

[0x1013ddb4f]> / secuencia de clonación

Buscando 14 bytes en [0x100000000-0x100fb4000]

hits: 1

0x100f07823 hit7_0 .sent! Srtp% s: secuencia de clonación (SSRC: 0x% 08x).




PJSIP



Además, watsap utiliza PJSIP, que implementa comunicaciones multimedia, señalización y codificación de datos de audio y video.



STUN también se implementa allí, lo que se ve claramente al analizar con Wireshark.



La biblioteca fue identificada por PJSIP por cadenas en los binarios watcap debido a la información de depuración en PJSIP:



r2 WhatsApp

[0x1013ddb4f]> / pjmedia

Buscando 7 bytes en [0x100000000-0x100fb4000]

hits: 180

0x100edd55f hit9_0.

0x100edd591 hit9_1 .r %d, stream %ppjmedia_audio_piggyback.

0x100edd5d4 hit9_2 .d, tx_packet %dpjmedia_audio_piggyback.

0x100edd601 hit9_3 .ideo_enabled %dpjmedia_audio_piggyback.

0x100eddcf3 hit9_4 .ibyuv converterpjmedia_converter_creat.

0x100eddd21 hit9_5 .rter count = %dpjmedia_converter_creat.

0x100ede3e3 hit9_6 .rame, status=%dpjmedia_delay_buf_get_s.

0x100ede46e hit9_7 .%sec_delay_bufpjmedia_echo_create2: %.

0x100ede64d hit9_8 .eUnknown pjmedia-videodev error.

0x100ede90c hit9_9 .o errorUnknown pjmedia-audiodev error.

0x100edebba hit9_10 .ATENCY)Unknown pjmedia error %dUnspec.

0x100ee027e hit9_11 .queue.format.cpjmedia_format_get_vide.

0x100ee02ca hit9_12 .mat información para% dpjmedia_format_get_vide.

0x100ee1446 hit9_13 .c_buf demasiado corto pjmedia_h26x_packetize.




mbed TLS



También watcap usa mbed TLS de código abierto para implementar su protocolo TLS.

Esta biblioteca se identificó en el código Watzap por los siguientes nombres de función:



r2 WhatsAppCore

[0x0082b517]> / mbedtls

Buscando 7 bytes en [0x814000-0x934000]

hits: 41

0x008e299b hit5_0 .TLSErrorDomain_mbedtls_aes_crypt_cbc

0x008e29b2 hit5_1 ._aes_crypt_cbc_mbedtls_aes_crypt_cfb12.

0x008e29cc hit5_2 .s_crypt_cfb128_mbedtls_aes_crypt_cfb8.

0x008e29e4 hit5_3 .aes_crypt_cfb8_mbedtls_aes_crypt_ctr_.

0x008e29fb hit5_4 ._aes_crypt_ctr_mbedtls_aes_crypt_ecb_.

0x008e2a12 hit5_5 ._aes_crypt_ecb_mbedtls_aes_decrypt_mb.

0x008e2a27 hit5_6 .ls_aes_decrypt_mbedtls_aes_encrypt_mb.

0x008e2a3c hit5_7 .ls_aes_encrypt_mbedtls_aes_free_mbedt.

0x008e2a4e hit5_8 .edtls_aes_free_mbedtls_aes_init_mbedt.

0x008e2a60 hit5_9 .edtls_aes_init_mbedtls_aes_setkey_dec.

0x008e2a78 hit5_10 .aes_setkey_dec_mbedtls_aes_setkey_enc.

0x008e2a90 hit5_11 .aes_setkey_enc_mbedtls_cipher_auth_dec.

0x008e2aad hit5_12 .r_auth_decrypt_mbedtls_cipher_auth_enc.

0x008e2aca hit5_13 .r_auth_encrypt_mbedtls_cipher_check_ta.






XMPP



Watzap también usa el Protocolo de Presencia y Mensajería Extensible (XMPP) para intercambiar mensajes asíncronos entre clientes.



Esto fue descubierto por los nombres de clase en el código watsap usado en XMPP:



r2 WhatsApp

[0x1013ddb4f]> / XMPP

Buscando 4 bytes en [0x1013ac000-0x1014b4000]

hits: 150

Buscando 4 bytes en [0x100fb4000-0x1013ac000]

hits: 150

Buscando 4 bytes en [0x100000000-0x100fb4000]

hits: 396

0x1013d05b5 hit12_0. @ _ OBJC_CLASS _ $ _ XMPPAckStanza @ _.

0x1013d05d6 hit12_1. @ _ OBJC_CLASS _ $ _ XMPPBinaryCoder.

0x1013d05fa hit12_2. @ _ OBJC_CLASS _ $ _ XMPPCallStanza.

0x1013d0624 hit12_3. @ _ OBJC_CLASS _ $ _ XMPPChatStateStanza.

0x1013d064b hit12_4. @ _ OBJC_CLASS _ $ _ XMPPConnection.

0x1013d0679 hit12_5. @ _ OBJC_CLASS _ $ _ XMPPError.

0x1013d069e hit12_6. @ _ OBJC_CLASS _ $ _ XMPPGDPRDeleteReport.

0x1013d06cd hit12_7. @ _ OBJC_CLASS _ $ _ XMPPGDPRGetReportSta.

0x1013d0707 hit12_8. @ _ OBJC_CLASS _ $ _ XMPPGDPRRequestRepor.

0x1013d0736 hit12_9. @ _ OBJC_CLASS _ $ _ XMPPIQStanza.

0x1013d0762 hit12_10. @ _ OBJC_CLASS _ $ _ XMPPMessageStanza.

0x1013d0787 hit12_11. @ _ OBJC_CLASS _ $ _ XMPPMessageStatusCha.

0x1013d07b9 hit12_12. @ _ OBJC_CLASS _ $ _ XMPPMultiReceipt.

0x1013d07dc hit12_13. @ _ OBJC_CLASS _ $ _ XMPPNotificationStan.

...




Marco de protocolo de ruido



Según informes oficiales, Watzap utiliza el marco de protocolo de ruido para comunicarse de forma segura entre clientes y servidores.

Noise Protocol Framework fue diseñado para crear protocolos criptográficos fáciles de usar utilizando un conjunto de bloques discretos.

Pero estrictamente hablando, Watzap solo usa el Noise Pipes Protocol, que fue tomado del más completo Noise Protocol Framework.



Estas líneas se encontraron en los archivos binarios de Watzap:



“Noise_XX_25519_AESGCM_SHA256”,

• “Noise_IK_25519_AESGCM_SHA256”,

• “Noise_XXfallback_25519_AESGCM_SHA256”.



Estas líneas contienen los patrones de protocolo de enlace implementados en los clientes de Watsap.



La primera línea pertenece a la clase WANoiseFullHandshake.

El segundo es WANoiseResumeHandshake y el último es WANoiseFallbackHandshak.



No consideraremos en detalle cómo funciona este protocolo en el marco de este artículo.



Análisis de tiempo de ejecución



En esta parte, exploraremos el comportamiento del cliente watsap usando Frida.



Frida es el llamado Dinamic Instrumentation Toolkit, que es un conjunto de herramientas que te permiten inyectar tu propio código en otras aplicaciones sobre la marcha.

Nos conectaremos a un proceso en la aplicación y cambiaremos su comportamiento usando una consola JS interactiva.



Transporte de claves



En esta parte, exploraremos los mecanismos clave de trabajo del protocolo watcap.

De acuerdo con la descripción oficial de Whatsapp que describe el cifrado de una llamada VOIP, el iniciador de la llamada genera un secreto maestro SRTP aleatorio de 32 bytes.

Luego, el mensaje encriptado se transmite al lado B con el contenido de este secreto maestro SRTP.

Esta información se usa luego para la reconstrucción del lado B.



Primero, hice un rastreo usando la palabra "secreto":



Frida-trace -U WhatsApp -m "* [* * Secreto *]" -m "* [* * * secreta]"

Tras el inicio de la llamada llamada vatsap método deriveSecretsFromInputKeyMaterial usando la clase WAHKDF:



+ [WAHKDF

deriveSecretsFromInputKeyMaterial: 0x121e08a20

sal: 0x0

la info : 0x121e07840

outputLength: 0x2e

withMessageVersion: 0x3

]




Los valores de entrada 0x121e08a20 y 0x121e07840 apuntan a objetos Objective-C.

Frida te permite crear objetos proxy Objective-C para JavaScript.



El gancho deriveSecretsFromInputKeyMaterial se utiliza para imprimir la descripción de los objetos:



{

onEnter: function (log, args, state) {

log ("+ [WAHKDF deriveSecretsFromInputKeyMaterial:" +

ObjC.Object (args [2]) .toString () + "\ n" +

"salt:" + ObjC.Object (args [3]) .toString () + "\ n" +

"info:" + ObjC. Objeto (args [4]) .toString () + "\ n" +

"bytes:" + args [5] .toInt32 () + "\ n" +

"withMessageVersion:" + args [6] .toInt32 () + "\ n]");

}

}





Después de la secuencia de comandos:



+ [WAHKDF deriveSecretsFromInputKeyMaterial: <09a38e76 fe90e4f1 26ed66d0 5a6783ba d48776b6 1daaf7c9 39c005ea 2d8ccdf6>

sal: nil

Info: <34393135 39303537 37313632 3040732e 77686174 73617070 2e6e6574>

Bytes: 46

withMessageVersion: 3

]




El primer y tercer parámetros como objetos NSData ,que contienen un búfer de bytes estático.

El primer parámetro tiene 32 bytes de longitud, como se describe en el documento técnico de WhatsApp.



El tercer parámetro es una cadena ASCII que contiene el JID de la persona que llama.

Más adelante veremos que, efectivamente, es la primera línea que contiene el secreto maestro.



Cifrar el secreto maestro



Según el libro blanco de WhatsApp, el secreto maestro es una parte necesaria para asegurar una sesión de llamada.

Por lo tanto, debe entregarse de manera segura al lado B.

Para investigar cómo se lleva a cabo dicha entrega, hice un rastreo que contenía palabras clave relevantes para el proceso de encriptación:



frida-trace -U WhatsApp -m "* [* * crypt *]" -i "* crypt *"



Después de iniciar la llamada, llame a la función signal_encrypt desde la biblioteca libsignal-protocol-c.

Encabezado Signal_encrypt:



texto sin formato legible con el gancho de Frida:







los primeros 4 bytes se utilizan para serializar el secreto maestro utilizando los búferes de protocolo de Google.

(La serialización es el proceso de traducir alguna estructura de datos en una secuencia de bits).

Los siguientes bytes son el secreto maestro en sí.

Los últimos 13 bytes son relleno de cifrado.

El texto sin formato está cifrado con AES-256 en modo CBC.

Las claves de cifrado se obtienen mediante el algoritmo de doble trinquete, que forma parte del protocolo de señal.

Libsignal-protocol-c y Signal Protocol no se tratan en este artículo. Resultado de



Signal_encrypt:







el resultado contiene más bytes porque se agregó una etiqueta de autenticación al mensaje que usa HMAC-SHA256.



Hemos cubierto la primera parte del protocolo VoIP de WhatsApp.



En resumen, el secreto maestro se serializa y encripta usando una clave AES de 256 bits en modo CBC.

La clave de cifrado y la clave de autenticación se obtuvieron utilizando la biblioteca libsignal-protocol-c de código abierto.



Analizar el secreto maestro



Veamos cómo se cifra el secreto maestro.



Hacemos un rastreo con la palabra clave signal:



frida-trace -U WhatsApp -i “* signal *”

Frida muestra que la función textsecure__signal_message__pack está involucrada en el cifrado del secreto maestro.



La función crea un mensaje de señal que contiene el secreto maestro cifrado y otros parámetros requeridos: los







bytes resaltados en verde se utilizan para la serialización.

Bytes azules: trinquete de la clave del remitente (utilizado para el cifrado de extremo a extremo).

El contador de mensajes tiene bytes de color naranja.

Finalmente, los bytes del secreto maestro se resaltan en verde.



Al rastrear XMPP, podemos ver que se llama al método writeNoiseFrameToSocketWithPayload de la clase XMPPStream.

Este método envía mensajes XMPP encriptados por el protocolo Noise Pipes utilizando el protocolo TCP al servidor watchap.



Aquí abrí el contenido en la carga útil:







este es un mensaje XMPP binario que contiene el mensaje de señalización creado anteriormente.

Para el desmontaje, creamos un seguimiento de la clase XMPPBinaryCoder.



Esta clase tiene un método de serialización que crea una representación binaria de cadenas XMPP.

Al mostrar estos parámetros, puede ver varios pares de claves adjuntos al mensaje XMPP:



- [XMPPBinaryCoder serialize:

[call from='49**********@s.whatsapp.net '

id =' 1555415586-10 '

to='49**********@s.whatsapp.net '

[offer call-id ='

45D7827C624353A70084AED9B8C509D3'call-creator='49***********@s.whatsapp .net '

[audio rate =' 8000 'enc =' opus ']

[audio rate =' 16000 'enc =' opus ']

[net medium =' 3 ']

[capacidad ver =' 1 '{5b}]

[codificar keygen = '2']

[enc v = '2' type = 'pkmsg' {201b}]

]

]

] comprimido: 0x0]




Me las arreglé para hacer una notificación falsa sobre una llamada perdida de A a B, aunque la llamada fue iniciada por Mallory ...

Esto fue posible después de reescribir el creador de llamadas y de los parámetros en el JID en el lado A.

Aunque el nombre de Mallory se muestra en la notificación.

Cuando el Partido B comienza a responder a un mensaje de este tipo, se llama al Partido A en lugar de a Mallory.

Este comportamiento será más interesante de analizar más adelante.







Resumamos los resultados intermedios: en watsap, el secreto maestro cifrado se empaqueta en un mensaje de señal, que se agrega a las cadenas XMPP.

Las cadenas XMPP también contienen el ID y JID de ambos lados.



Pasar el secreto maestro a la otra parte



Según la descripción oficial de Watsup, los clientes utilizan el protocolo Noise Pipes con Curve25519, AESGCM y SHA256 del Noise Protocol Framework.



Si usa rastreo que contiene palabras clave relacionadas con el marco de protocolo de ruido, puede ver que la clase WANoiseStreamCipher se usa para encriptar llamadas a servidores Vatsap.

La clase usa el método encryptPlaintext.

Una vez iniciada la llamada, el valor de texto sin formato es el mensaje XMPP descrito anteriormente.

A continuación, el mensaje se vuelve a cifrar utilizando la biblioteca TLS de mbed mbedtls_gcm_crypt_and_tag.

mbedtls_gcm_setkey tiene un tamaño de 256 bits, lo que significa que se utiliza AES-256-GCM.

La clave de cifrado se utiliza del Protocolo de canalizaciones de ruido, que no se trata en este artículo.

El texto plano cifrado pasa a través de TCP al servidor Watsap (esto se puede ver con Wireshark).

El servidor luego reenviará este mensaje a la parte llamada para iniciar la llamada.







Modelado de clave



En esta parte, veremos cómo funciona la Función de formación de clave (KDF) /

Los resultados se obtuvieron usando Frida mientras se rastreaba la clase WAHKDF y la biblioteca libcommonCrypto.

La clase WAHKDF se utilizó para extraer claves, sal y códigos de un solo uso al inicializar secuencias SRTP.

El método deriveSecretsFromInputKeyMaterial se llama 10 veces antes de que comience la llamada:



+[WAHKDF deriveSecretsFromInputKeyMaterial: <09a38e76 fe90e4f1 26ed66d0 5a6783ba d48776b6 1daaf7c9 39c005ea 2d8ccdf6>, salt: nil, info: <34393135 39303537 37313632 3040732e 77686174 73617070 2e6e6574>, bytes: 46, withMessageVersion: 3] => result: <4633c47f 94d5ed59 93a6dba8 514d5fb8 5092ba90 4256f8d3 4d56e72e 665bcd4c 5b6c418b db811e7f 84a70c83 f401>+[WAHKDF deriveSecretsFromInputKeyMaterial: <09a38e76 fe90e4f1 26ed66d0 5a6783ba d48776b6 1daaf7c9 39c005ea 2d8ccdf6>, salt: nil, info: <34393137 ******** ******** ******** ******** 6170702e 6e6574>, bytes: 46, withMessageVersion: 3] => result: <a174670a e25d8138 4de0ed3b f4ce7f76 c62c1d00 9ece6573 2ecb497b 1f6ed09c 18c444b9 c180fbd3 51713739 761c>+[WAHKDF deriveSecretsFromInputKeyMaterial: <34354437 38323743 36323433 35334137 30303834 41454439 42384335 30394433>, salt: <00000000>, info: <34393135 39303537 37313632 3040732e 77686174 73617070 2e6e6574>, bytes: 4, withMessageVersion: 3] => result: <0ec654fd>+[WAHKDF deriveSecretsFromInputKeyMaterial: <34354437 38323743 36323433 35334137 30303834 41454439 42384335 30394433>, salt: <01000000>, info: <34393135 39303537 37313632 3040732e 77686174 73617070 2e6e6574>, bytes: 4, withMessageVersion: 3] => result: +[WAHKDF deriveSecretsFromInputKeyMaterial: <34354437 38323743 36323433 35334137 30303834 41454439 42384335 30394433>, salt: <04000000>, info: <34393135 39303537 37313632 3040732e 77686174 73617070 2e6e6574>, bytes: 4, withMessageVersion: 3] => result: +[WAHKDF deriveSecretsFromInputKeyMaterial: <34354437 38323743 36323433 35334137 30303834 41454439 42384335 30394433>, salt: <00000000>, info: <34393137 ******** ******** ******** ******** 6170702e 6e6574>, bytes: 4, withMessageVersion: 3] => result: +[WAHKDF deriveSecretsFromInputKeyMaterial: <34354437 38323743 36323433 35334137 30303834 41454439 42384335 30394433>, salt: <01000000>, info: <34393137 ******** ******** ******** ******** 6170702e 6e6574>, bytes: 4, withMessageVersion: 3] => result: +[WAHKDF deriveSecretsFromInputKeyMaterial: <34354437 38323743 36323433 35334137 30303834 41454439 42384335 30394433>, salt: <04000000>, info: <34393137 ******** ******** ******** ******** 6170702e 6e6574>, bytes: 4, withMessageVersion: 3] => result:



, JID .

6 SRTP , 3 .



JavaScript:



const crypto = require(«crypto»);



// master secret

const keyMaterial = new Buffer(

«09a38e76fe90e4f126ed66d05a6783bad48776b61daaf7c939c005ea2d8ccdf6»,

«hex»

);

// JID param: 4915905771620@s.whatsapp.net

const info = «3439313539303537373136323040732e77686174736170702e6e6574»;

const salt = new Buffer(

«0000000000000000000000000000000000000000000000000000000000000000»,

«hex»

);

const initialKey = crypto.createHmac(«sha256», salt)

.update(keyMaterial)

.digest();

const temp1 = crypto.createHmac(«sha256», initialKey)

.update(new Buffer(info + «01», «hex»))

.digest();

const temp2 = new Buffer(temp1.toString(«hex») + info + «02», «hex»);

const temp3 = crypto.createHmac(«sha256», initialKey)

.update(temp2)

.digest();



const result = Buffer.concat([temp1, temp3.slice(0, 14)]);

console.log(result.toString(«hex»));



// 4633c47f94d5ed5993a6dba8514d5fb85092ba904256f8d34d56e72e665bcd4c5b6c418bdb811e7f84a70




SRTP .

Frida.

KDF libcommonCrypto .



3 HMAC-SHA256.

KDF RFC 5869.







SRTP, libsrtp, VOIP .

, libsrtp .

.

libsrtp .



libsrtp , .

.

, libsrtp.

, .

libsrtp 12 .

Frida .

Frida.



srtp_aes_icm_context_init libsrtp.

SRTP AES-ICM.

, , .



srtp_aes_icm_context_init, 2 :



debug_print(srtp_mod_aes_icm, «key: %s»,

srtp_octet_string_hex_string(key, base_key_len));

debug_print(srtp_mod_aes_icm, «offset: %s», v128_hex_string(&c→offset));




debug_print .



, .

Hopper Disassembler:



int sub_100bbda00(int arg0, int arg1) {

r31 = r31 — 0x60;

var_30 = r24;

stack[-56] = r23;

var_20 = r22;

stack[-40] = r21;

var_10 = r20;

stack[-24] = r19;

saved_fp = r29;

stack[-8] = r30;

r19 = arg0;

sub_100bbf094(arg0, arg1 + 0x10);

r20 = r19 + 0x10;

sub_100bbf094(r20, arg1 + 0x10);

*(int16_t *)(r19 + 0x1e) = 0x0;

*(int16_t *)(r19 + 0xe) = 0x0;

if (*(int32_t *)dword_1012b5760 != 0x0) {

sub_100bbf048(&var_40);

sub_100bc085c(0x7, "%s: key: %s\n");

if (*(int32_t *)0x1012b5760 != 0x0) {

sub_100bbf048(r20);

sub_100bc085c(0x7, "%s: offset: %s\n");

}

}

sub_100bbbffc(&var_40, r19 + 0x30);

*(int32_t *)(r19 + 0xe0) = 0x0;

return 0x0;

}




19 22 .

, .

iOS Address Space Layout Randomization (ASLR) .



.



srtp_aes_icm_context_init :



const apiResolver = new ApiResolver(«objc»);

const resolvedMatches = apiResolver.enumerateMatches(

"+[NSURL URLWithUnicodeString:]"

);



const SCAN_SIZE = 100000;

const scanStart = resolvedMatches[0].address;

const scanResults = Memory.scanSync(

ptr(scanStart),

SCAN_SIZE,

// first bytes of the hexadecimal representation of srtp_aes_icm_context_init

«FF 83 01 D1 F8 5F 02 A9 F6 57 03 A9»

);



// srtp_err_status_t srtp_aes_icm_context_init(void *cv, const uint8_t *key)

const targetPointer = ptr(scanResults[0].address);

const targetFunction = new NativeFunction(targetPointer, «int», [

«pointer»,

«pointer»

]);



console.log(«scan start: » + scanStart);

console.log(«srtp_aes_icm_context_init: » + scanResults[0].address);



Interceptor.attach(targetFunction, {

onEnter: function(args) {

/*

static srtp_err_status_t srtp_aes_icm_context_init(void *cv, const uint8_t *key)



typedef struct {

v128_t counter; holds the counter value

v128_t offset; initial offset value

v128_t keystream_buffer; buffers bytes of keystream

srtp_aes_expanded_key_t expanded_key; the cipher key

int bytes_in_buffer; number of unused bytes in buffer

int key_size; AES key size + 14 byte SALT

} srtp_aes_icm_ctx_t;



*/

console.log(«srtp_aes_icm_context_init » + args[0] + " key:");

console.log(

hexdump(args[1], {

offset: 0,

length: 16

})

);

},

onLeave: function(args) {}

});





ApiResolver Frida .

ApiResolver .

Frida.

URLWithUnicodeString, 3 .

, linear search .



SCAN_SIZE .

12 12 .

, NativeFunction, 17 Frida ( ).

2 — encryption context (cv) encryption key (key).



srtp_aes_icm_context_init 6 6 SRTP .

key.



AES-ICM.

srtp_aes_icm_alloc, “allocating cipher with key length %d”.



key length , 16 .

AES-128-ICM SRTP .

46 key derivation function, 30 .

16 2 .

16 !







srtp_aes_icm_encrypt, libsrtp .



SRTP AES-128-ICM.

“block index: %d” .



SRTP srtp_aes_icm_encrypt:







12 , , .

SRTP payload.

4 ( ) – authentication tag.

6 , SRTP payload .



Call Integrity



SRTP .



libsrtp srtp_hmac_compute.

authentication tag SRTP .



srtp_hmac_compute Frida,

“intermediate state: %s” .



srtp_hmac_compute :



static srtp_err_status_t srtp_hmac_compute(void *statev,

const uint8_t *message,

int msg_octets,

int tag_len,

uint8_t *result)




srtp_hmac_compute HMAC-SHA1 .

Frida , tag_len SRTP .



tag_len message srtp_hmac_compute :

Attaching…

search srtp_hmac_compute in memory from: 0x1016380ac

found srtp_hmac_compute at: 0x10163b5f4

tag_len: 10

message: 81 ca 00 07 fe 67 2e 32 56 14 89 75 c5 c0 39 4a d3 a0 cd 48 8c 4b 61 8a 78 32 a7 89 1e b7 71 26 80 00 00 01tag_len: 4

message: 00 00 00 00tag_len: 10

message: 81 d0 00 02 fe 67 2e 32 b5 6f 93 8e 80 00 00 02tag_len: 4

message: 00 00 00 00tag_len: 4

message: 00 00 00 00tag_len: 4

message: 00 00 00 00tag_len: 4

message: 00 00 00 00tag_len: 10

message: 81 ca 00 07 83 42 f3 44 81 78 9f f5 39 b1 23 50 48 19 e0 f1 61 5b b5 32 dc b3 10 08 e7 47 a8 4b 80 00 00 01tag_len: 10

message: 81 d0 00 02 83 42 f3 44 94 60 21 fe 80 00 00 02tag_len: 4

message: 00 00 00 00tag_len: 4

message: 00 00 00 00tag_len: 10

message: 81 c8 00 12 fe 67 2e 32 87 b7 69 f8 5a 27 4c 76 b4 29 f6 5d 59 26 de af bd e9 4c 8b f3 ff 48 e3 a9 7e 62 cf db 9c 8a 3d 34 50 48 f8 fc 0e 88 7a 17 eb 17 94 9f 3d 91 27 89 d5 cc bd 21 ea 01 39 27 e1 05 07 66 69 1f 68 08 53 1a 18 02 9e bc 50 ed 8e 40 3e 8a 7b d3 b6 19 e8 54 6f 6b 58 ac 4e e3 25 f5 c2 e8 1c 97 bb 46 f9 38 45 80 00 00 03...




2 :



1. SRTP 4 .

Message SRTP .

4 authentication tag.

, , .

- .



2. 10 .

, VOIP .

SRTP , 10 :



const scanStart = new ApiResolver(«objc»).enumerateMatches(

"+[NSURL URLWithUnicodeString:]"

)[0].address;



console.log(«search srtp_hmac_compute in memory from: » + scanStart);



const size = 100000;

const matches = Memory.scanSync(

ptr(scanStart),

size,

// first bytes of the hexadecimal representation of srtp_hmac_compute

«E0 03 16 AA 4C 00 00 94 D5 02 01 91»

);

const targetPtr = ptr(matches[0].address);

console.log(«found srtp_hmac_compute at: » + matches[0].address);



const targetFunction = new NativeFunction(targetPtr, «int», [

«pointer»,

«pointer»,

«int»,

«int»,

«pointer»

]);



const MANIPULATABLE_TAG_SIZE = 10;

const manipulatedTag = Memory.alloc(MANIPULATABLE_TAG_SIZE);

manipulatedTag.writeByteArray([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]);



Interceptor.attach(ptr(targetFunction), {

onEnter: function(args) {

/*

static srtp_err_status_t srtp_hmac_compute(void *statev,

const uint8_t *message,

int msg_octets,

int tag_len,

uint8_t *result)

*/

console.log(«srtp_hmac_compute tag (» + args[3].toInt32() + "):");

const tag_len = args[3].toInt32();

if (tag_len === MANIPULATABLE_TAG_SIZE) {

console.log(

hexdump(args[1], {

length: args[2].toInt32()

})

);

args[3] = 0;

args[4].writePointer(manipulatedTag);

}

}

});




Frida, VOIP .

, SRTP .

, , .







WhatsApp VoIP …

, .



:



— libsignal-protocol-c, libsrtp, PJSIP mbed TLS VOIP .



— “master secret” 2 SRTP , AES-128-ICM.

key derivation function (HKDF), , nonces SRTP.



— Noise Pipes Protocol, Signal Protocol XMPP .

Signal Protocol, XMPP , Noise Pipes Protocol .

.



— VOIP – SRTP .

, , SRTP .



— SRTP , VOIP .



— .

, .

.



:



github.com/schirrmacher/files/blob/master/WhatsApp%20VoIP%20Protocol.pdf

github.com/schirrmacher/files/blob/master/WhatsApp

github.com/schirrmacher/files/blob/master/WhatsAppCore



– , , .



Frida .



.



.



Además, los desarrolladores también deben eliminar las constantes de cadena que contienen información crítica o pueden ser útiles para identificar la funcionalidad.



All Articles