是否有一种合理的方法可以根据捕获的数据包构建 SSL 密码使用情况统计数据?
假设我的网络服务器支持一组密码,我想找出有多少个客户端协商每个密码套件。
答案1
是的. 如果你有捕获的数据包,只需从中提取协商的密码服务器 Hello 握手数据包:
密码套件
The single cipher suite selected by the server from the list in ClientHello.cipher_suites. For resumed sessions, this field is the value from the state of the session being resumed.
数据包本身很容易识别,所选密码位于数据包内的固定位置,很容易解析。因此,捕获所有 SSL 连接的前几个数据包,提取所选密码,您就能找到想要的东西。
这个想法很有趣,所以我决定尝试一下。经过一些修改,我能够改编一个 Python 脚本来实现它:
$ ./parser.py random2.pcap | sort -u
TLS 1.0 0x00,0x14
TLS 1.2 0x00,0x2f
TLS 1.2 0x00,0x30
$
有了这些信息,您就可以将密码套件 ID 与TLS 密码套件注册表来自IANA:
$ ./parser.py random2.pcap | sort -u | awk '{print $3}' | grep -if - ~/Downloads/tls-parameters-4.csv
"0x00,0x14",TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,Y,[RFC4346]
"0x00,0x2F",TLS_RSA_WITH_AES_128_CBC_SHA,Y,[RFC5246]
"0x00,0x30",TLS_DH_DSS_WITH_AES_128_CBC_SHA,Y,[RFC5246]
$
这是代码。这是TLS 客户端 Hello 工具所以如果你想玩它,考虑回到那里以获得一个不那么激烈的版本(并记住原来的重点是客户端 Hello,而我们关心的是服务器 Hello)。
#!/usr/bin/env python
# Hack-and-slash derived from https://github.com/pquerna/tls-client-hello-stats
import os, sys, dpkt
TLS_HANDSHAKE = 22
def pcap_reader(fp):
return dpkt.pcap.Reader(fp)
def grab_negotiated_ciphers(cap):
for ts, buf in cap:
eth = dpkt.ethernet.Ethernet(buf)
if not isinstance(eth.data, dpkt.ip.IP):
continue
ip = eth.data
if not isinstance(ip.data, dpkt.tcp.TCP):
continue
tcp = ip.data
if (tcp.dport != 443 and tcp.sport != 443) or (len(tcp.data) <= 0) or (ord(tcp.data[0]) != TLS_HANDSHAKE):
continue
records = []
try:
records, bytes_used = dpkt.ssl.TLSMultiFactory(tcp.data)
except dpkt.ssl.SSL3Exception, e:
continue
except dpkt.dpkt.NeedData, e:
continue
if len(records) <= 0:
continue
for record in records:
# TLS handshake only
if (record.type == 22 and len(record.data) != 0 and ord(record.data[0]) == 2):
try:
handshake = dpkt.ssl.TLSHandshake(record.data)
except dpkt.dpkt.NeedData, e:
continue
if isinstance(handshake.data, dpkt.ssl.TLSServerHello):
ch = handshake.data
print '%s\t0x%0.2x,0x%0.2x' %(dpkt.ssl.ssl3_versions_str[ch.version], (ch.cipher_suite&0xff00)>>8, ch.cipher_suite&0xff)
else:
continue
def main(argv):
if len(argv) != 2:
print "Tool to grab and print TLS Server Hello cipher_suite"
print ""
print "Usage: parser.py <pcap file>"
print ""
sys.exit(1)
with open(argv[1], 'rb') as fp:
capture = pcap_reader(fp)
stats = grab_negotiated_ciphers(capture)
if __name__ == "__main__":
main(sys.argv)
答案2
如果您的目标是找出 Web 服务器的客户端正在使用的密码套件,那么可能有更简单的方法。
例如,Nginx 的 ssl 模块有几个变量像$ssl_cipher
和$ssl_protocol
,哪个你可以 还记录:
log_format ssl '$remote_addr - $remote_user [$time_local] "$request" $status '
'$body_bytes_sent "$http_referer" "$http_user_agent" '
'$ssl_protocol $ssl_cipher';
access_log /var/log/nginx/ssl.log ssl;
2001:db8::1 - - [27/Mar/2015:21:11:04 +0000] "GET /index.html HTTP/1.1" 200
12345 "-" "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0"
TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256
(顺便说一句,对于 HTTP 流量使用该日志格式也是安全的。ssl 变量将显示为-
。)