Piratage de WhatsApp, partie 2 - Analyse du protocole VOIP Whatsapp





Dans cet article, je veux vous dire comment j'ai craqué plusieurs parties du protocole VoIP WhatsApp à l'aide d'un appareil iOS jailbreaké et d'un ensemble de différents programmes d'analyse.



Récemment, Whatsapp a reçu beaucoup d'attention en raison des vulnérabilités et des opportunités pour les pirates.

De ce point de vue, il est très intéressant pour la recherche sur sa sécurité.



Tous ceux qui s'y intéressent également, bienvenue sous le chat.





Bien que les pages officielles de Whatsapp aient une description de son cryptage, en fait, il n'y a aucune information détaillée sur son fonctionnement et sa mise en œuvre dans le protocole.

Par conséquent, il n'y a aucune base pour des analyses de sécurité détaillées sur Whatsapp lui-même.



Ma recherche était basée sur trois choses:



1. Analyse du trafic réseau

2. Analyse binaire 3. Analyse du

comportement des applications dans différents modes



Boîte



à outils Pour analyser le client Wahtsapp pour iOS, j'ai utilisé les outils suivants:



- Descripteur binaire - bfdecrypt

- Désassembleur de fichiers binaires - Désassembleur de trémie et radare2

- Analyse du trafic réseau - Wireshark

- Analyse des actions applicatives - Frida



La configuration du jailbreak sur iOS dépasse le cadre de cet article.



Analyse du trafic réseau



Dans cette partie, nous analyserons le trafic réseau du client Whatsapp lors d'un appel, que nous enregistrerons à l'aide de Wireshark.



Pour enregistrer ce trafic, j'ai créé une interface réseau virtuelle distante.



La commande pour Makos ressemble à ceci:



rvictl -s Ici, l'UUID du périphérique doit être remplacé par l'UUID du périphérique avec le client watsap.



Wireshark détecte l'utilisation des utilitaires de traversée de session pour NAT (STUN).

STUN est un protocole de signalisation requis pour établir des connexions peer-to-peer entre les clients.







Ici, le client WhatsApp utilise des paquets TCP pour communiquer avec différents serveurs Watzap.

Dans le même temps, les paquets UDP sont utilisés pour l'échange entre les clients.

Des centaines de paquets UDP passent en une minute.

Vatsap utilise le protocole SRTP (Secure Real Time Protocol) et il est évident que ces paquets UDP contiennent des données SRTP sur l'appel.



Le protocole SRTP fournit le chiffrement, l'authentification et la protection contre les attaques de relecture sur le trafic RTP.



Examinons de plus près les paquets SRTP échangés entre les côtés A et B.

Pour ce faire, convertissez-les en hexadécimal:







On voit que les champs contiennent des en-têtes RTP spécifiques à SRTP.



Les quatre premiers octets (surlignés en rouge) sont les 7 champs d'en-tête RTP.



Examinons-les plus en détail:



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







Les 2 premiers bits contiennent le numéro de version (V), dans notre cas c'est la deuxième version.

Le troisième bit est un champ d'informations optionnelles, dans notre cas il est vide.

Le quatrième bit - le champ d'extension (X) indique que dans ce cas, il n'y a pas d'autres en-têtes après l'en-tête de paquet RTP.



Bits 5 à 8 - Contient le nombre d'identificateurs CSRC suivant l'en-tête permanent.

CSRC (contribution source) est la source du flux de paquets RTP qui contribue au flux total généré par le mélangeur RTP. Le mélangeur insère une liste d'identificateurs SSRC qui identifient des sources partielles dans l'en-tête des paquets RTP. Cette liste est appelée une liste CSRC. Par exemple - une conférence audio où le mélangeur marque tous les orateurs dont la voix génère des paquets sortants. Cela permet au côté récepteur d'identifier le locuteur, bien que tous les paquets aient le même ID SSRC.



8 bits est un marqueur de bits (M). Utilisé au niveau de l'application et déterminé par le profil. Si ce champ est défini, les données du package ont une signification particulière pour l'application.



Les 6 bits suivants sont des codes de type de données supplémentaires. Ces données ne sont pas définies dans les normes RTP et SRTP. La signification de ces bits est probablement une signification personnalisée choisie par Whatsapp.



Les 17 derniers bits indiquent la source d'horloge. Le nombre est incrémenté dans l'ordre de 1 lors de l'envoi du prochain paquet de données RTP; ce code peut être utilisé par le récepteur pour enregistrer les pertes de paquets et restaurer le véritable ordre des fragments envoyés. Par la norme, la valeur initiale du code est aléatoire, mais cette recommandation n'est pas remplie par le watsap, car comme nous pouvons le voir à partir des données Wireshark, la valeur initiale du watsap est toujours 0.



Les 4 octets suivants (surlignés en bleu) sont l'horodatage du paquet.



4 octets ensuite (vert) - champ SSRC. Il identifie la source de synchronisation. Cet identifiant est choisi au hasard afin qu'il n'y ait pas deux codes SSRC égaux dans une session RTP. Toutes les applications doivent pouvoir détecter si les SSRC sont égaux. Si l'expéditeur change son adresse de transport, il doit également changer l'identifiant SSRC.



Nous avons donc découvert que Whatsapp utilise le protocole SRTP pour protéger les appels.

Ceci est confirmé par la structure des paquets UDP échangés entre les clients Watcap.



En outre, Watzap utilise le protocole TCP pour échanger des données entre le client et le serveur.

Ci - dessous , nous verrons comment le protocole canaux de bruit est utilisé pour chiffrer cette partie. Binary



analyse



Le client WhatsApp pour iOS contient 2 binaires principaux - le binaire d'application WhatsApp et le cadre de base de WhatsApp.



Dans cette partie, nous les examinerons de plus près avec le désassembleur de trémie et radare2.



Ces binaires sont chiffrés lorsqu'ils sont téléchargés depuis l'Appstore.

Ici, nous avons incité Apple à jailbreaker un appareil iOS et à accéder à ces fichiers.



Ajoutez également que ces binaires Whatsapp ont été déchiffrés avec bfdecrypt.



Ensuite, je vais vous montrer comment j'ai rassemblé des informations sur les principes de base du protocole, les algorithmes et les bibliothèques open source que Whatsapp utilise.



Les bibliothèques open source sont particulièrement intéressantes car elles peuvent être facilement analysées.



libsignal-protocole-c



Watsap utilise libsignal-protocol-c - une bibliothèque open source - qu'il a implémentée dans le protocole Signal.



Le protocole est basé sur l'algorithme Double Ratchet, qui crypte les messages watsap.



Cette bibliothèque a été trouvée dans les binaires Whatsapp pour les caractéristiques suivantes:



r2 WhatsAppCore

[0x0082b517]> / _signal_

Recherche de 8 octets dans [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 utilise également libsrtp pour implémenter son protocole Secure Real Time.

Les noms de symboles ont été supprimés des binaires Whatsapp, mais malgré cela, les binaires contiennent des lignes qui indiquent directement leur lien vers libsrtp:



r2 WhatsApp

[0x1001ada34]> / libsrtp

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

0x100ee57eb hit1_1 .d pour initialiser libsrtp:% s échoué sur r

0x100ee580a hit1_2 .led pour enregistrer libsrtp deinit.Failed.

0x100ee5831 hit1_3 .pour désinitialiser libsrtp:% sAES_CM_128_.

0x100ee5883 hit1_4 .ck crypto Init libsrtp. créer un pool ..

0x100f07b80 hit1_5. paquet:% slibsrtpstat test% s: c.





De plus, les binaires watsap contiennent des chaînes qui sont utilisées dans le code brut de la libsrtp, par exemple «flux de clonage (SSRC: 0x% 08x)»:



r2 WhatsApp

[0x1013ddb4f]> / cloning stream La

recherche de 14 octets dans [0x100000000-0x100fb4000]

hits: 1

0x100f07823 hit7_0 .sent! Srtp% s: flux de clonage (SSRC: 0x% 08x).




PJSIP



De plus, watsap utilise PJSIP, qui implémente les communications multimédias, la signalisation et le codage des données audio et vidéo.



De plus, STUN y est implémenté, ce qui est clairement visible lors de l'analyse avec Wireshark.



La bibliothèque a été identifiée par PJSIP par des chaînes dans les binaires watcap en raison des informations de débogage dans PJSIP:



r2 WhatsApp

[0x1013ddb4f]> / pjmedia

Recherche de 7 octets dans [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 info hit9_12 .mat pour% dpjmedia_format_get_vide.

0x100ee1446 hit9_13 .c_buf trop courtpjmedia_h26x_packetize.




mbed TLS



Aussi, Watzap utilise le TLS open source mbed pour implémenter son protocole TLS.

Cette bibliothèque dans le code Watzap a été identifiée par les noms de fonction suivants:



r2 WhatsAppCore

[0x0082b517]> / mbedtls

Recherche de 7 octets dans [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 utilise également le protocole XMPP (Extensible Messaging and Presence Protocol) ouvert pour échanger des messages asynchrones entre les clients.



Cela a été découvert par les noms de classe dans le code watsap utilisé dans XMPP:



r2 WhatsApp

[0x1013ddb4f]> / XMPP

Recherche de 4 octets dans [0x1013ac000-0x1014b4000]

hits: 150

Recherche de 4 octets dans [0x100fb4000-0x1013ac000]

résultats: 150

Recherche de 4 octets dans [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.

...




Noise Protocol Framework



Selon les rapports officiels, Watzap utilise le Noise Protocol Framework pour communiquer en toute sécurité entre les clients et les serveurs.

Le Noise Protocol Framework a été conçu pour créer des protocoles cryptographiques faciles à utiliser en utilisant un ensemble de blocs discrets.

Mais à proprement parler, Watzap n'utilise que le Noise Pipes Protocol, qui a été tiré du Noise Protocol Framework plus complet.



Ces lignes ont été trouvées dans les binaires Watzap:



«Noise_XX_25519_AESGCM_SHA256»,

• «Noise_IK_25519_AESGCM_SHA256»,

• «Noise_XXfallback_25519_AESGCM_SHA256».



Ces lignes contiennent les modèles de prise de contact implémentés dans les clients watsap.



La première ligne appartient à la classe WANoiseFullHandshake.

Le second est à WANoiseResumeHandshake et le dernier à WANoiseFallbackHandshak.



Nous n'examinerons pas en détail le fonctionnement de ce protocole dans le cadre de cet article.



Analyse d'exécution



Dans cette partie, nous explorerons le comportement du client watsap en utilisant Frida.



Frida est le soi-disant Dinamic Instrumentation Toolkit, qui est un ensemble d'outils qui vous permet d'injecter votre propre code dans d'autres applications à la volée.

Nous nous connecterons à un processus dans l'application et modifierons son comportement à l'aide d'une console JS interactive.



Key Transport



Dans cette partie, nous explorerons les mécanismes clés du travail du protocole watcap.

Selon la description officielle de Whatsapp décrivant le cryptage d'un appel VOIP - l'initiateur de l'appel génère un secret maître SRTP aléatoire de 32 octets.

Ensuite, le message chiffré est transmis au côté B avec le contenu de ce secret principal SRTP.

Ces informations sont ensuite utilisées pour la reconstruction du côté B.



J'ai d'abord fait une trace en utilisant le mot «secret»:



FRIDA trace -U WhatsApp -m "* [* * secret *]" -m "* [* secrète * *]"

Après l'ouverture de l'appel d'appel vatsap méthode deriveSecretsFromInputKeyMaterial utilisant la classe WAHKDF:



+ [WAHKDF

deriveSecretsFromInputKeyMaterial: 0x121e08a20

sel: 0x0

l'info : 0x121e07840

outputLength: 0x2e

withMessageVersion: 0x3

]




Les valeurs d'entrée 0x121e08a20 et 0x121e07840 pointent vers des objets Objective-C.

Frida vous permet de créer des objets proxy Objective-C pour JavaScript.



Le hook deriveSecretsFromInputKeyMaterial est utilisé pour imprimer la description des objets:



{

onEnter: function (log, args, state) {

log ("+ [WAHKDF deriveSecretsFromInputKeyMaterial:" +

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

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

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

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

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

}

}





Une fois le script:



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

sel: néant

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

octets: 46

withMessageVersion: 3

]




Les premier et troisième paramètres tels que les objets NSData ,qui contiennent un tampon d'octets statique.

Le premier paramètre fait 32 octets, comme décrit dans le livre blanc WhatsApp.



Le troisième paramètre est une chaîne ASCII contenant le JID de l'appelant.

Plus tard, nous verrons qu'en effet, c'est la première ligne qui contient le maître secret.



Crypter le secret principal



Selon le livre blanc de WhatsApp, le secret principal est une partie nécessaire pour sécuriser une session d'appel.

Par conséquent, il doit être livré en toute sécurité au côté B.

Pour étudier comment une telle livraison a lieu, j'ai fait une trace contenant des mots-clés pertinents pour le processus de cryptage:



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



Après avoir lancé l'appel, appelez la fonction signal_encrypt depuis la bibliothèque libsignal-protocol-c.

En-tête Signal_encrypt:



texte clair lisible avec le hook de Frida:







les 4 premiers octets sont utilisés pour sérialiser le secret principal en utilisant les tampons de protocole de Google.

(La sérialisation est le processus de traduction d'une structure de données en une séquence de bits.)

Les octets suivants sont le secret principal lui-même.

Les 13 derniers octets sont un remplissage de cryptage.

Le texte en clair est crypté avec AES-256 en mode CBC.

Les clés de cryptage sont obtenues à l'aide de l'algorithme Double Ratchet, qui fait partie du protocole de signal.

Libsignal-protocol-c et Signal Protocol ne sont pas traités dans cet article. Résultat



Signal_encrypt:







le résultat contient plus d'octets car une balise d'authentification a été ajoutée au message qui utilise HMAC-SHA256.



Nous avons couvert la première partie du protocole VoIP WhatsApp.



Pour résumer, le secret principal est sérialisé et chiffré à l'aide d'une clé AES 256 bits en mode CBC.

La clé de chiffrement et la clé d'authentification ont été obtenues à l'aide de la bibliothèque open source libsignal-protocol-c.



Analyser le secret principal



Voyons comment le secret principal est chiffré.



Nous faisons une trace avec le mot



- clé signal: frida-trace -U WhatsApp -i “* signal *”

Frida montre que la fonction textecure__signal_message__pack est impliquée dans le chiffrement du secret principal.



La fonction crée un message Signal contenant le secret principal chiffré et d'autres paramètres nécessaires: Les







octets surlignés en vert sont utilisés pour la sérialisation.

Blue bytes - clé à cliquet de l'expéditeur (utilisée pour le chiffrement de bout en bout).

Le compteur de messages est composé d'octets orange.

Enfin, les octets du secret principal sont surlignés en vert.



Lors du traçage de XMPP, nous pouvons voir que la méthode writeNoiseFrameToSocketWithPayload de la classe XMPPStream est appelée.

Cette méthode envoie des messages XMPP chiffrés par le protocole Noise Pipes à l'aide du protocole TCP au serveur watchap.



Ici, j'ai ouvert le contenu dans la charge utile:







Il s'agit d'un message XMPP binaire contenant le message de signalisation créé ci-dessus.

Pour le démontage, nous créons une trace de la classe XMPPBinaryCoder.



Cette classe a une méthode de sérialisation qui crée une représentation binaire des chaînes XMPP.

Lors de l'affichage de ces paramètres, vous pouvez voir diverses paires de clés ajoutées au message XMPP:



- [XMPPBinaryCoder serialize:

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

id =' 1555415586-10 '

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

[offre call-id ='

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

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

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

[net medium =' 3 ']

[capacité ver =' 1 '{5b}]

[encopt keygen = '2']

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

]

]

] compressé: 0x0]




J'ai réussi à faire une fausse notification à propos d'un appel manqué de A à B, bien que l'appel ait été en fait initié par Mallory ...

Cela est devenu possible après la réécriture du créateur d'appel et des paramètres du JID du côté A.

Bien que le nom Mallory soit affiché dans la notification.

Lorsque la partie B commence à répondre à un tel message, la partie A est appelée à la place de Mallory.

Ce comportement sera plus intéressant à analyser plus tard.







Résumons les résultats intermédiaires - dans vatsap, le secret principal chiffré est emballé dans un message de signal, qui est ajouté aux chaînes XMPP.

Les chaînes XMPP contiennent également l'ID et le JID des deux côtés.



Transfert du secret principal à l'autre partie



Selon la description officielle des clients Watsup, utilisez le protocole Noise Pipes avec Curve25519, AESGCM et SHA256 à partir du Noise Protocol Framework.



Si vous utilisez un traçage contenant des mots-clés liés à Noise Protocol Framework, vous pouvez voir que la classe WANoiseStreamCipher est utilisée pour crypter les appels aux serveurs Vatsap.

La classe utilise la méthode encryptPlaintext.

Une fois l'appel lancé, la valeur en texte brut est le message XMPP décrit ci-dessus.

Le message est ensuite à nouveau chiffré à l'aide de la bibliothèque mbed TLS mbedtls_gcm_crypt_and_tag.

mbedtls_gcm_setkey a une taille de 256 bits, ce qui signifie que AES-256-GCM est utilisé.

La clé de chiffrement est utilisée à partir du protocole Noise Pipes, qui n'est pas traité dans cet article.

Le texte en clair chiffré passe ensuite par TCP vers le serveur watsap (cela peut être vu avec Wireshark).

Le serveur transmettra ensuite ce message à l'appelé pour initier l'appel.







Key Shaping



Dans cette partie, nous verrons comment fonctionne la Key Shaping Function (KDF) /

Les résultats ont été obtenus en utilisant Frida tout en traçant la classe WAHKDF et la bibliothèque libcommonCrypto.

La classe WAHKDF a été utilisée pour extraire les clés, le sel et les codes à usage unique lors de l'initialisation des flux SRTP.

La méthode deriveSecretsFromInputKeyMaterial est appelée 10 fois avant le début de l'appel:



+[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 .



.



.



De plus, les développeurs doivent également supprimer les constantes de chaîne qui contiennent des informations critiques ou peuvent être utiles pour identifier les fonctionnalités.



All Articles