例子,cloudflare的warp-svc.exe。抓包获取密钥。
用proxifier尝试了一下强行代理,无效,因为proxifier是通过Hook Socket函数方式实现的,但这个程序可能没有用Socket函数进行通信。
之后发现通过nekoray基于gvisor的VPN白名单模式全局路由可以强制代理。但发现MITMproxy抓不了包而且在Event Log返回sslv3 alert bad certificate。
尝试了google的ssl_log,无效,ssl_log的原理是hook openssl库,可能这个程序没有用openssl库来处理tls。
过IDA pro查看发现有很多cargo的字样,猜测是rust写的。进一步搜索alert 发现一个字符串"Sending warning alert",网上一查发现是rustls库里的字符串,那么就从rustls入手。
先从bad certificate开始搜索rustls的源码,由于在ssl握手里的序号是42,
接着搜索RootCertStore,发现在anchors命名空间里
去anchors命名空间里翻找发现结构体RootCertStore是OwnedTrustAnchor类型是vector数组
接着搜索OwnedTrustAnchor找到它的结构体
那么RootCertStore 是从哪里赋值的呢?代码里没搜到,通过readme可以看到,example里面有个tlsclient-mio.rs,应该是调用rustls的例子:
从里面可以看到RootCertStore 被webpki_roots::TLS_SERVER_ROOTS所赋值,于是去寻找webpki_roots发现确实有这样一个项目,这个项目里面有个脚本build.py,生成了src/lib.rs文件,进入这个文件一看:
发现根证书的存储方式是存储subject和spki,这两个值通过src/bin
/process_cert.rs这个程序处理证书然后生成,通过cargo build之后在target文件夹里面生成的process_cert.exe文件。
其中每个证书的spki大小都只有几种,而subject大小不一样。去IDA里面搜索subject代表的二进制数值,果然搜到了。
之后的思路就简单了,直接用openssl生成一个相同大小subject和spki的的证书然后使用process_cert.exe提取出来,再使用010editor替换目标程序warp-svc.exe里面的字节串即可。一个简单的方法是使用与你的目标域名的签发机构根证书相同的生成信息,比如api.cloudflareclient.com的根证书是Baltimore CyberTrust Root,提取出来后使用命令
openssl x509 -in '.\Baltimore CyberTrust Root.crt' -noout -text
得到它的subject:
Subject: C = IE, O = Baltimore, OU = CyberTrust, CN = Baltimore CyberTrust Root
然后使用openssl生成subject一模一样的证书
openssl genrsa -out ca.key 2048
openssl req -new -x509 -key ca.key -out ca.crt
cat ca.key ca.crt >mitmproxy-ca.pem
最后用process_cert.exe处理这个证书提取出二进制格式的subject和spki并使用010editor替换目标程序warp-svc.exe里面的字节串。