前言
mmtls是基于1.3的tls协议简化修改而来,根据某公司的描述,这种安全可行,用于短连接的安全模式,也给了其他的公司启发,一方面,使用https在越狱root环境下,也容易被窥探,所以开发一种通用而安全的短连接加密协议,确实有些必要,我们想看看到底是如何实现的,所以抓个包,hook下数据来看看。
(不太清楚在这样的氛围下,能不能讨论这个,所以我决定先写一部分,要是可以继续说,再写后半部分,大家参考这种实现,实现出自己的通用加密传输协议)
关于mmtls的介绍可以阅读这篇文章
协议源码????【 16-7750-7857 】 ********: @duofu
我们如何入手分析呢?
1.抓包
抓包显然是没有办法看到啥的,不过我们只关心短连接,所以我们需要一个环境来触发短连接的mmtls初始化,而且我们只关心mmtls,并不关心其他的信息,所以,我们可以利用一个早期版本,理由是,早期版本可以屏蔽掉长连接,而强制使用短连接。
2.算法
显然我们应该先对算法,有一些了解,甚至我们应该先阅读一下github上的tls1.3的实现,这样我们才能对tls1.3的答题轮廓有些印象,在我们逆向分析的过程中,省去很多的麻烦,所以我们应该先了解以下算法,包括以下算法的原理和使用。
- ECDH(ecdh主要用于秘钥协商,签名验证)
- AESGCM(aes gcm 带校验的aes加密算法,相比于之前的aes cbc要更安全)
- hkdf扩展(hkdf扩展是用来扩展秘钥的)
- sha256(哈希算法,基本上用于验证数据)
- hmac(签名)
- Hook
为了便于分析,我们会一次把以上的算法进行hook一次,弄清数据的来龙去脉,这也是分析协议最关键的技巧,跟着数据走,以数据为核心,来进行逆向,不用太在意到底是经过了那些算法,我们只需关心,数据在哪里变化了。所以我选取了PC版本26231,找出上面算法的hook地址,找算法地址其实不复杂,因为基于openssl的算法,搜关键字啥的就行了。为了方便大家对照,我在这里公布地址的列表:
- BuildWriteBuffer 0xA17B3A (发送数据)
- GenEcdhPublicKey 0xA475AC
- GenEcdhPublicKey 0xA475AC
- GenEcdhPriKey 0xA475C8
- ClientChannelReceive 0xA18FFD
- AesGcmCrypterCrypt 0xA6124B
- AesGcmCrypterCryptFinish 0xA61434
- OpenSslCryptoUtilHkdfExpand 0xA47827
- OpenSslCryptoUtilHkdfExpandFinish 0xA478BC
- OpenSslCryptoUtilEcdh 0xA4816B
- OpenSslCryptoUtilEcdhFinish 0xA482CF
- OpenSslCryptoUtilSHA256Update 0xA489A0
- OpenSslCryptoUtilSHA256Final 0xA48A65
- OpenSslCryptoUtilSHA256FinalFinish 0xA48B07
- OpenSslCryptoUtilHMacSHA256 0xA4810D
- OpenSslCryptoUtilHMacSHA256Finish 0xA4815F
启动注入研究Log
1.由于mmtls的必须启动注入,所以需要在dll被load进来就开始注入,这可以利用OD很方便,之后我们可以看到一些日志。
这里生成了两对ECDH秘钥对,参照官方解释,应该是一对用于加密,一对用于签名校验,因为放弃了原有的tls证书链形式签名校验,可能就采用这种方式来做签名。
2.之后看发送数据
首先可以看到这里对发送的数据进行了一次SHA256计算,之后便发送了,而发送数据从结构上来看应该是tls的clientHello包,但是好像结构并不能完全与之对应,从数据上大致可以分为以下几块:
- 总大小(00 00 00 D0)
- 可能是版本(01 03 f1)
- 内容大小
- 随机数32
- 时间戳
- 一大串不知道啥
- ECDHPUB1
- ECDHPUB2
- ***
3.看看Serverhello
Serverhello比较符合tls的规范0x16为协商过程,所以我们将数据
进行了切割,大体分为四个块,接下来分别以这四个块进行讲解,看看我们理解上的tls1.3与mmtls有什么区别,从文章来理解,砍掉了算法选择,RTT-1就可以协商完成。
-
第一个数据块
上面可以看到第一个数据块,返回了服务器的pubkey,用于跟我们之前的prikey进行一次ecdh协商然后得出一个32大小的数据。
之后进行一次HKDF的秘钥扩展生成56大小的数据,根据文章描述,应该是对这56的数据进行分开,此时的数据,则为服务器和客户端的共同加解密数据,但是这个数据并不是最终的固定秘钥,而是协商过程用于加解密数据的秘钥。秘钥按照(16 12 16 12)进行分解,作为AES GCM的 key 跟IV,这样便可以进行后续的解密操作。 -
第二个数据块
现在就开始解密第二个数据块了,key iv nonce都比较清晰,同理可以解密出来第三部分第四部分。 -
解密第三个数据块
第三个块解密出来的数据包含PSK data,以便于后续的直接PSK模式请求。 -
解密第四个数据块
第四个数据块包含server finish,服务器返回的数据就完全了,根据server finish可以做一些校验,判断数据是否有错误。
发送Client Finish
协商完成以后,客户端要发送client finish的报文,告诉服务器客户端以及完成了解密和协商。
用第一块数据hdkf扩展出来的加密key iv进行加密即可但是这里的IV做了异或自增加,为了防止重攻击,服务器应该解密之后自增长,这样重复请求就无法解密。
2.扩展出业务秘钥
到这里就和application.data.key.expansion一起扩展出来了真正的业务请求秘钥,现在就是安全的传输模式了。
总结
https的流量劫持从这篇文章我们其实了解到,从http时代的流量劫持到https时代都没有彻底解决,以前运营商恶心,现在证书信任依然是个很大的问题,所以我们如果能从mmtls的实现里面,对tls1.3进行改动,应该可以实现一套比较通用的安全传输协议层,而且基本上也与业务无关。