Назад | Перейти на главную страницу

Статистика использования набора шифров SSL на основе собранных данных

Есть ли разумный метод построения статистики использования шифра SSL на основе захваченных пакетов?

Допустим, мой веб-сервер поддерживает набор шифров, и я хотел бы узнать, сколько клиентов согласовывают каждый набор шифров.

да. Если у тебя есть захваченные пакеты, просто извлеките согласованный шифр из Пакет подтверждения сервера Hello:

cipher_suite

  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
$

Имея эту информацию, вы можете сопоставить идентификаторы набора шифров с Реестр TLS Cipher Suite от 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 так что, если вы хотите поиграть с ним, подумайте о том, чтобы вернуться к нему для менее радикальной версии (и имейте в виду, что оригинал фокусируется на Client Hello, тогда как мы заботимся о Server 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)

Если ваша цель - выяснить, какие наборы шифров используют клиенты вашего веб-сервера, могут быть более простые методы.

Например, ssl-модуль Nginx имеет несколько переменных лайк $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 будут отображаться как -.)