入侵WhatsApp,第2部分-解析Whatsapp VOIP协议





在本文中,我想告诉您如何使用越狱的iOS设备和一组不同的分析程序破解WhatsApp VoIP协议的几个部分。



最近,由于漏洞和黑客的机会,Whatsapp得到了很多关注。

从这个角度来看,对其安全性的研究非常有趣。



每个对此也感兴趣的人,欢迎光临。





尽管Whatsapp的官方页面上已对其加密进行了描述,但实际上,在任何地方都没有有关它如何工作以及如何在协议中实现的详细信息。

因此,没有基础对Whatsapp本身进行详细的安全分析。



我的研究基于以下三点:



1.网络流量分析

2.二进制

分析3.不同模式下的应用程序行为分析



工具包



为了分析iOS的Wahtsapp客户端,我使用了以下工具:



-二进制描述符-bfdecrypt-

二进制文件反汇编程序-Hopper Disassembler和radare2

-网络流量分析-Wireshark-

应用程序动作分析-Frida



我如何在iOS上设置越狱超出了本文的范围。



分析网络流量



在这一部分中,我们将分析呼叫期间Whatsapp客户端的网络流量,我们将使用Wireshark进行记录。



为了记录此类流量,我创建了一个远程虚拟网络接口。



Makos的命令如下所示:



rvictl -s这里需要用watsap客户端将设备UUID替换为设备的UUID。



Wireshark检测到会话遍历实用程序用于NAT(STUN)。

STUN是在客户端之间建立对等连接所必需的信令协议。







在这里,WhatsApp客户端使用TCP数据包与不同的Watzap服务器进行通信。

同时,UDP数据包用于客户端之间的交换。

一分钟内会通过数百个UDP数据包。

Vatsap使用安全实时协议(SRTP),很明显,这些UDP数据包包含有关呼叫的SRTP数据。



SRTP协议提供加密,身份验证和保护,以防止对RTP流量进行重放攻击。



让我们仔细看看A和B端之间交换的SRTP数据包。

为此,请将它们转换为十六进制:







可以看出,这些字段包含特定于SRTP的RTP标头。



前四个字节(以红色突出显示)是7个RTP标头字段。



让我们更详细地考虑它们:



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







前两个位包含版本号(V),在我们的情况下是第二个版本。

第三位是可选信息的字段,在本例中为空。

第四位-扩展字段(X)表示在这种情况下,RTP数据包头之后没有其他头。



位5到8-包含永久标头后的CSRC标识符的数量。

CSRC(贡献源)是RTP数据包流的源,它有助于RTP混合器生成的总流。混合器将标识部分源的SSRC标识符列表插入RTP数据包的标头中。此列表称为CSRC列表。例如,在音频会议中,调音台标记所有发言者的语音,这些发言者的语音会产生传出数据包。尽管所有数据包都具有相同的SSRC ID,这使接收方可以识别说话者。



8位是位标记(M)。在应用程序级别使用并由配置文件确定。如果设置了此字段,则包数据对于应用程序具有某些特殊含义。



接下来的6位是其他数据类型代码。 RTP和SRTP标准中未定义此数据。这些位的含义很可能是Whatsapp选择的自定义位。



后17位指示时钟源。发送下一个RTP数据包时,该编号按顺序递增1,接收方可以使用此代码注册包丢失并恢复发送的片段的真实顺序。按照标准,代码的初始值是随机的,但是watsap不能满足此建议,因为从Wireshark数据可以看出,watsap的初始值始终为0。



接下来的4个字节(以蓝色突出显示)是数据包时间戳。



此后4字节(绿色)-SSRC字段。它标识同步源。随机选择此标识符,以便在一个RTP会话中没有两个相等的SSRC代码。所有应用程序都必须能够检测SSRC何时相等。如果发送方更改其传输地址,则它还必须更改SSRC标识符。



因此,我们发现Whatsapp使用SRTP协议来保护呼叫。

Watcap客户端之间交换的UDP数据包的结构证实了这一点。



另外,Watzap使用TCP协议在客户端和服务器之间交换数据。

下面我们将看到的噪音管道协议是如何使用该部分进行加密。二进制



分析



iOS的Vatsap客户端包含2个主要的二进制文件-WhatsApp应用程序二进制文件和WhatsApp核心框架。



在这一部分中,我们将使用料斗拆卸器和radare2对其进行更仔细的研究。



从Appstore下载这些二进制文件是加密的。

在这里,我们欺骗了Apple越狱iOS设备并获得对这些文件的访问权。



还应补充说明,这些Whatsapp二进制文件已使用bfdecrypt解密。



接下来,我将向您展示如何收集有关Whatsapp使用的协议基础知识,算法和开源库的信息。



开源库特别有趣,因为它们很容易解析。



libsignal协议



Watsap使用libsignal-protocol-c(一个开源库),他在信号协议中实现了该库。



该协议基于双棘轮算法,该算法对watsap消息进行加密。



该库在Whatsapp二进制文件中具有以下特征:



r2 WhatsAppCore

[0x0082b517]> / _signal_

在[0x0-0x654000]

匹配中搜索8个字节: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还使用libsrtp来实现其安全实时协议。

符号名称已从Whatsapp二进制文件中删除,但是尽管如此,这些二进制文件中仍包含直接指示其链接到libsrtp的行:



r2 WhatsApp

[0x1001ada34]> / libsrtp

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

0x100ee57eb hit1_1 .d初始化libsrtp:%s失败。

0x100ee580a hit1_2 .led已注册libsrtp deinit。失败。

0x100ee5831 hit1_3。以初始化libsrtp:%sAES_CM_128_。

0x100ee5883 hit1_4 .ck crypto初始化libsrtp创建池..

0x100f07b80 hit1_5。数据包:%slibsrtpstat测试%s:c。





此外,watsap二进制文件包含原始libsrtp代码中使用的字符串,例如“克隆流(SSRC:0x%08x)”:



r2 WhatsApp

[0x1013ddb4f]> /克隆流

在[0x100000000-0x100fb4000]中搜索14个字节

匹配:1

0x100f07823 hit7_0 .sent!Srtp%s:克隆流(SSRC:0x%08x)。




PJSIP



另外,watsap使用PJSIP,它实现了多媒体通信,信令以及音频和视频数据编码。



STUN也在那里实现,使用Wireshark分析时可以清楚地看到。



由于PJSIP中的调试信息,PJSIP通过watcap二进制文件中的字符串标识了该库:



r2 WhatsApp

[0x1013ddb4f]> / pjmedia

在[0x100000000-0x100fb4000]

匹配中搜索7个字节: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信息,用于%dpjmedia_format_get_vide。

0x100ee1446 hit9_13 .c_buf太短了pjmedia_h26x_packetize。




mbed TLS



同样,Watzap使用开源的mbed TLS来实现其TLS协议。

Watzap代码中的以下函数名称标识了该库:



r2 WhatsAppCore

[0x0082b517]> / mbedtls

在[0x814000-0x934000]

匹配中搜索7个字节: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还使用开放的可扩展消息和状态协议(XMPP)在客户端之间交换异步消息。



这是通过XMPP中使用的watsap代码中的类名发现的:



r2 WhatsApp

[0x1013ddb4f]> / XMPP

在[0x1013ac000-0x1014b4000]

匹配中

搜索4个字节:150在[0x100fb4000-0x1013ac000]

匹配中

搜索4个字节:150在4个字节中搜索[0x100000000-0x100fb4000]

匹配: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。

...




噪声协议框架



根据官方报告,Watzap使用噪声协议框架在客户端和服务器之间进行安全通信。

噪声协议框架旨在使用一组离散块创建易于使用的加密协议。

但严格来说,Watzap仅使用噪声管道协议,该协议取自更完整的噪声协议框架。



这些行在Watzap二进制文件中找到:



“ Noise_XX_25519_AESGCM_SHA256”,

•“ Noise_IK_25519_AESGCM_SHA256”,

•“ Noise_XXfallback_25519_AESGCM_SHA256”。



这些行包含在watsap客户端中实现的握手模式。



第一行属于WANoiseFullHandshake类。

第二个是WANoiseResumeHandshake,最后一个是WANoiseFallbackHandshak。



在本文的框架中,我们不会详细考虑该协议的工作方式。



运行时分析



在这一部分中,我们将使用Frida探索watsap客户端的行为。



Frida是所谓的Dinamic Instrumentation Toolkit,它是一组工具,使您可以将自己的代码动态地注入到其他应用程序中。

我们将连接到应用程序中的流程,并使用交互式JS控制台更改其行为。



密钥传输



在这一部分中,我们将探讨watcap协议工作的关键机制。

根据来自Whatsapp的官方描述,该描述描述了VOIP呼叫的加密-呼叫的发起者会生成随机的32字节SRTP主密钥。

然后,将加密的消息连同此SRTP主密钥的内容一起发送到B侧。

然后,这些信息将用于B侧重建:



首先,我使用“秘密”一词进行了跟踪:



frida-trace -U WhatsApp -m“ * [* * * *]” -m“ * [* * * *]”

在使用WAHKDF类启动呼叫调用vatsap validateSecretsFromInputKeyMaterial方法之后:



+ [WAHKDF

defineSecretsFromInputKeyMaterial:0x121e08a20

盐:0x0

该信息:0x121e07840

outputLength:

0x2e withMessageVersion:0x3

]




输入值0x121e08a20和0x121e07840指向Objective-C对象。

Frida允许您为JavaScript创建代理Objective-C对象。



validateSecretsFromInputKeyMaterial钩子用于打印对象的描述:



{

onEnter:函数(日志,参数,状态){

日志(“ + [WAHKDF validateSecretsFromInputKeyMaterial:” +

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

“盐:” + ObjC.Object(args [3]).toString()+“ \ n” +

“ info:” + ObjC。对象(args [4]).toString()+“ \ n” +

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

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

}

}





脚本后:



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

盐:无

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

字节:46

withMessageVersion:3

]




的第一个和第三个参数等的NSData对象,其中包含一个静态字节缓冲区。

如WhatsApp白皮书中所述,第一个参数的长度为32个字节。



第三个参数是包含调用者JID的ASCII字符串。

稍后我们将看到,实际上是包含主密钥的第一行。



加密主密码



根据WhatsApp白皮书,主密码是保护通话会话的必要部分。

因此,必须将它安全地传递到B面。

为了研究这种传递是如何发生的,我进行了跟踪,其中包含与加密过程相关的关键字:



frida-trace -U WhatsApp -m“ * [* * crypt *]” -i “ * crypt *”



启动调用后,从libsignal-protocol-c库中调用signal_encrypt函数。

Signal_encrypt标头:



用Frida的钩子可读的明文:







前4个字节用于使用Google的协议缓冲区序列化主密码。

(串行化是将某些数据结构转换为比特序列的过程。)

下一个字节是主密钥本身。

最后的13个字节是加密填充。

在CBC模式下,纯文本使用AES-256加密。

加密密钥是使用双棘轮算法获得的,该算法是信号协议的一部分。

本文不涉及Libsignal-protocol-c和Signal Protocol。



Signal_encrypt







结果由于使用HMAC-SHA256的消息中已添加了身份验证标签,因此结果包含更多字节。



我们已经介绍了WhatsApp VoIP协议的第一部分。



总而言之,在CBC模式下使用256位AES密钥对主密码进行序列化和加密。

使用开源libsignal-protocol-c库获取加密密钥和身份验证密钥。



解析主密码



让我们看看如何对主密码进行加密。



我们使用signal关键字



进行跟踪frida-trace -U WhatsApp -i“ * signal *”

Frida显示textsecure__signal_message__pack函数涉及加密主密钥。



该函数创建一个包含加密的主密钥和其他必要参数的信号消息:







以绿色突出显示的字节用于序列化。

蓝色字节-发送方密钥棘轮(用于端到端加密)。

消息计数器是橙色字节。

最后,主密钥的字节以绿色突出显示。



跟踪XMPP时,我们可以看到调用了XMPPStream类中的writeNoiseFrameToSocketWithPayload方法。

此方法将使用TCP协议由“噪声管道协议”加密的XMPP消息发送到watchap服务器。



在这里,我打开了有效负载中的内容:







这是一个二进制XMPP消息,其中包含上面创建的信令消息。

为了进行反汇编,我们创建了XMPPBinaryCoder类的跟踪。



此类具有序列化方法,该方法可创建XMPP字符串的二进制表示形式。

当显示这些参数时,您会看到各种附加在XMPP消息上的密钥对:



-[XMPPBinaryCoder序列化:

[调用来自='49**********@s.whatsapp.net'id

='1555415586-10'

to

='49

**********@ s.whatsapp.net' [提供通话ID ='45D7827C624353A70084AED9B8C509D3'call-creator='49**********@s.whatsapp .net'

[音频率='8000'enc ='opus']

[音频率='16000'enc ='opus']

[网络媒介='3']

[功能ver ='1'{5b}]

[encopt keygen ='2']

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

]

]

]压缩:0x0]




尽管该呼叫实际上是由Mallory发起的,但我还是设法从A到B发出了虚假通知...



尽管在通知中显示了Mallory名称,但 在重写调用创建者并从A边的JID中的参数后,这成为可能

当乙方开始响应此消息时,则呼叫甲方而不是马洛里。

此行为将在以后进行分析时变得更加有趣。







让我们总结一下中间结果-在watsap中,加密的主密钥被打包到信号消息中,该消息被添加到XMPP字符串中。

XMPP字符串还包含双方的ID和JID。



将主密钥传输给另一方



根据Watsup的官方描述,客户端将噪声管道协议与Curve25519,AESGCM和SHA256一起使用来自噪声协议框架。



如果使用包含与“噪声协议框架”相关的关键字的跟踪,则可以看到WANoiseStreamCipher类用于加密对Vatsap服务器的调用。

该类使用cryptoPlaintext方法。

发起呼叫后,纯文本值为上述XMPP消息。

然后,使用mbed TLS库mbedtls_gcm_crypt_and_tag再次加密该消息。

mbedtls_gcm_setkey的大小为256位,这意味着使用AES-256-GCM。

加密密钥是从“噪音管道协议”中使用的,本文未介绍。

加密的明文然后通过TCP到达watsap服务器(可以在Wireshark中看到)。

然后,服务器将将此消息转发给被叫方以发起呼叫。







密钥整形



在这一部分中,我们将研究密钥整形功能(KDF)的工作原理/

结果是使用Frida跟踪WAHKDF类和libcommonCrypto库时获得的。

初始化SRTP流时,使用WAHKDF类提取密钥,盐和一次性代码。

在调用开始之前,调用了10次deriveSecretsFromInputKeyMaterial方法:



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



.



.



此外,开发人员还应删除包含关键信息或可能对识别功能有用的字符串常量。



All Articles