本文提出了logjam,CVE-2015-4000,告诫我们不要兼容因为安全问题而被抛弃的旧特性。
主要内容来自论文和网络,本文为内容摘要和背景知识(当然,也算是翻译)。
forward secrecy 前向安全性:长期使用的主密钥泄漏不会导致过去的会话密钥泄漏
攻击逻辑主要分为两步,第一步是利用CVE2015 logjam将密码降级到512位;第二步是使用NFS,利用预计算的结果对DH进行快速求解。
此外,本文还提出结论,768位可以被academic attacker破解,1024位可以被state
1.DH密钥交换
1.1 原根
如果a是素数p的一个原根,那么数值 a mod p,a2 mod p,…,ap-1 mod p 是各不相同的整数,并且以某种排列方式组成了从1到p-1的所有整数。对于一个整数b和素数p的一个原根a,可以找到惟一的指数i,使得 b = ai mod p 其中0 ≤ i ≤ (p-1)。
1.2 算法描述
1,有两个全局公开的参数,一个素数q和一个整数a,a是q的一个原根.
2,用户A和用户B分别选择小于q的秘密随机数XA,XB作为私钥
3,用户A和用户B分别计算公钥YA=a ^ XA mod q, YB=b ^ XB mod q,并告知对方
4,用户A和用户B分别计算YB ^XA mod q 和 YA ^ XB mod q,作为共享密钥
1.3 最有效的求解方法
对于素数域实现的DH算法(现在ECDHE,也就是椭圆曲线实现的DH密钥交换更加流行了,但不是本文讨论的内容),最有效的求解方法是number field sieve,NFS,也就是下图描述的算法。
如果攻击者针对p进行预计算,会显著降低问题求解的难度。可以看出,前三步只需要p,y和g是预计算之后才加入的。
上图即NFS算法,文章对polynomial selection, sieving, linear algebra, descent都做了讲解。
NFS算法有很多参数,通过合理的设置与取舍,能让求解最快。比如,sieving多花些时间,能减少linear algebra的工作量;precomputation多花些时间,就能减少descent的工作量。
2.DH与TLS
DH是TLS支持的密钥交换方法之一。大约有三分之二的常用https网站支持,一般使用1024位的p。但也有部分常用站点(百分之8.4)支持上古版本export-grade(512位的p)。
这百分之8.4中,还有百分之92.3使用了两个常用的p(原文table 1)。文章的攻击几乎可以搞定这百分之92.3。
这里的trick在于,服务器其实知道dhe_export已经没有多少client在用,只是为了兼容支持了dhe_export,只要client不用dhe_export,就是安全的。服务器只管兼容,安全责任甩给了client,这是不恰当的。
图中,cr和sr是随机数,certs是服务器证书,后面是用签名密钥签名后的(cr,sr,512位的p,g,gb)。
攻击的难点是,
中间人能近乎实时计算出离散对数(有的服务器会复用gb,与服务器实现和用户配置有关);
中间人能延后握手的完成,以便为计算提供时间(跟不同客户端的实现有关)。
即使来不及计算,最终因为握手超时而被迫断开,考虑到TLS false start(客户端在握手结束之前就发送应用数据,此举是为了减少延迟),中间人至少可以截获这一段报文。而这一段报文往往会有密码和cookie等信息(因为是连接刚建立发送的第一批信息)。
攻击的防范是,
客户端设置更大的最小长度(比如要求严格大于512);
服务端在Figure 2中第三行的签名信息中加入“加密套件”字段。
除了TLS,还应关切IKE和SSH。DH的应用很广泛。
3.作者建议
切换到椭圆曲线DH,即ECDH
客户端提至1024及以上,服务器使用2048,禁用DHE_EXPORT
如有可能,使用新的群
4.我对漏洞利用可行性的调研
第一种思路,是看客户端对DH的位数是否有要求
https://firefox-source-docs.mozilla.org/contributing/directory_structure.html 查看firefox源码目录结构
https://searchfox.org/mozilla-central/source firefox源码
https://gitee.com/mirrors/chromium gitee上chromium的镜像
https://opensource.com/article/19/7/open-source-browsers 其它开源浏览器
git clone https://github.com/curl/curl.git curl源码
第二种思路,是模仿作者对https网站进行扫描(通过自己写的客户端),以此判断服务端是否支持DHE_EXPORT。
curl有一个功能,可以Store the HTTP headers in a separate file (headers.txt in the example)
curl --dump-header headers.txt curl.se
通过读curl的readme,发现了一些有趣的功能,比如可以提交表单;
可以指定referer字段;可以伪装成某个浏览器;可以伪造cookie;可以自定义包的header(以及指定if-modified-since的时间);也可以发起telnet请求。
为了访问一些老网站,curl也可以显式指定旧版,如ssl3(参数为-3)、ssl2(参数为-2)和tls1(参数为-1)。
curl -2 https://secure.site.com/
https://zhuanlan.zhihu.com/p/142157217 对模板包进行十六进制修改,伪造数据包的通用方法
这篇文章提供了一些启发,写客户端的时候可能需要通过抓包来获取模板,基于这个模板修改请求的域名。
python提供pycurl库。
curl的时候要注意,显式指定https协议,否则是http流量。
第三种思路,是看TLS1.3(彼时2015年还未推出)是否已经从根本上解决了logjam,如果解决了,TLS1.3的普及程度如何?
https://datatracker.ietf.org/doc/rfc8446/ ietf对tls1.3的文档,
https://www.rfc-editor.org/rfc/pdfrfc/rfc7919.txt.pdf 从RFC7919可以看出tls1.3标准下,基于有限域的DH最小位数是2048,因此logjam攻击无法进行。
下图援引自:http://blog.itpub.net/31559359/viewspace-2286705/
在TLS 1.2中,g和p的值由双方自己生成。TLS 1.3则定义了几组g、p对,双方只需要选择想要使用的DH组即可。使用确定的参数值保证了DH密钥协商过程具有足够的安全性。(https://blog.csdn.net/andylau00j/article/details/79269499)
在https://www.ssllabs.com可以检查客户端和服务器对tls版本的支持(包括了上述的思路一和思路二),截图示例(某网站服务器支持的所有加密套件):
从这个网站上看出,即便是安全性评级A+的网站,也不会只支持tls1.3,一般是同时支持tls1.3和tls1.2(或者只支持tls1.2)。所以logjam应该还有利用空间。当然,仅根据此网站的A+评级标准就得出结论,这并不严谨。还是需要通过思路一和思路二,做数据统计后再得出结论。
第四种思路,就是直接调用现成的漏洞检测/漏洞扫描。实际上,上面提到的SSLlab,就提供api。
以下代码可以批量扫描域名,查看tls版本支持和是否有logjam漏洞。
域名列表文件名为domainlist,可以花钱去alexa上找,也可以到http://stuffgate.com/stuff/website上找。
import requests
import time
import sys
import logging
API = 'https://api.ssllabs.com/api/v3/'
def requestAPI(path, payload={}):
'''This is a helper method that takes the path to the relevant
API call and the user-defined payload and requests the
data/server test from Qualys SSL Labs.
Returns JSON formatted data'''
url = API + path
try:
response = requests.get(url, params=payload)
except requests.exception.RequestException:
logging.exception('Request failed.')
sys.exit(1)
data = response.json()
return data
def newScan(host, publish='off', startNew='on', all='done', ignoreMismatch='on'):
path = 'analyze'
payload = {
'host': host,
'publish': publish,
'startNew': startNew,
'all': all,
'ignoreMismatch': ignoreMismatch
}
results = requestAPI(path, payload)
payload.pop('startNew')
while results['status'] != 'READY' and results['status'] != 'ERROR':
time.sleep(30)
results = requestAPI(path, payload)
return results
with open('domainlist','r') as f:
for line in f:
results=newScan(line)
print(line,results['endpoints'][0]['details']['protocols'],results['endpoints'][0]['details']['logjam'])
运行效果如下图:
布尔值就表示是否具有logjam漏洞。
这个方法唯一的问题是,api的功能太多,会做很多很多检测,而我只用了返回结果中很小一部分,这导致效率低下。一个网站大约需要三分钟才能检测完毕,对于同域名多ip的情况会更慢。如果像论文作者一样对前1000000进行了检测,那就需要六年才能检测完毕。不过这个api非常强大,在其它研究中肯定也能提供方便。如果想获取其它信息,比如加密套件信息,可以先clone https://github.com/ssllabs/ssllabs-scan/,试用一下,对返回的json进行格式化(比如oschina的在线格式化工具),看看自己想要的字段在什么位置即可。