从TLS客户端hello中提取服务器名称指示(SNI)

bus*_*ens 12 language-agnostic ssl protocols rfc binary-data

如何从TLS客户端Hello消息中提取服务器名称指示.我很难理解这个关于TLS扩展的非常神秘的RFC 3546,其中定义了SNI.

到目前为止我已经理解的事情:

  • 当你输入缓冲区时,主机是utf8编码和可读的.
  • 主机前一个字节,它决定了它的长度.

如果我能找到该长度字节的确切位置,那么提取SNI将非常简单.但是我如何首先到达那个字节?

dlu*_*ist 32

我在sniproxy中做了这个,在Wireshark中检查TLS客户端hello数据包,同时阅读RFC是一个很好的方法.这不是太难,只需要跳过很多变长字段,检查是否有正确的元素类型.

我正在进行我的测试,并且有一个可能有用的带注释的示例数据包:

const unsigned char good_data_2[] = {
    // TLS record
    0x16, // Content Type: Handshake
    0x03, 0x01, // Version: TLS 1.0
    0x00, 0x6c, // Length (use for bounds checking)
        // Handshake
        0x01, // Handshake Type: Client Hello
        0x00, 0x00, 0x68, // Length (use for bounds checking)
        0x03, 0x03, // Version: TLS 1.2
        // Random (32 bytes fixed length)
        0xb6, 0xb2, 0x6a, 0xfb, 0x55, 0x5e, 0x03, 0xd5,
        0x65, 0xa3, 0x6a, 0xf0, 0x5e, 0xa5, 0x43, 0x02,
        0x93, 0xb9, 0x59, 0xa7, 0x54, 0xc3, 0xdd, 0x78,
        0x57, 0x58, 0x34, 0xc5, 0x82, 0xfd, 0x53, 0xd1,
        0x00, // Session ID Length (skip past this much)
        0x00, 0x04, // Cipher Suites Length (skip past this much)
            0x00, 0x01, // NULL-MD5
            0x00, 0xff, // RENEGOTIATION INFO SCSV
        0x01, // Compression Methods Length (skip past this much)
            0x00, // NULL
        0x00, 0x3b, // Extensions Length (use for bounds checking)
            // Extension
            0x00, 0x00, // Extension Type: Server Name (check extension type)
            0x00, 0x0e, // Length (use for bounds checking)
            0x00, 0x0c, // Server Name Indication Length
                0x00, // Server Name Type: host_name (check server name type)
                0x00, 0x09, // Length (length of your data)
                // "localhost" (data your after)
                0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
            // Extension
            0x00, 0x0d, // Extension Type: Signature Algorithms (check extension type)
            0x00, 0x20, // Length (skip past since this is the wrong extension)
            // Data
            0x00, 0x1e, 0x06, 0x01, 0x06, 0x02, 0x06, 0x03,
            0x05, 0x01, 0x05, 0x02, 0x05, 0x03, 0x04, 0x01,
            0x04, 0x02, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02,
            0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03,
            // Extension
            0x00, 0x0f, // Extension Type: Heart Beat (check extension type)
            0x00, 0x01, // Length (skip past since this is the wrong extension)
            0x01 // Mode: Peer allows to send requests
};
Run Code Online (Sandbox Code Playgroud)


小智 6

使用WireShark并通过添加过滤器仅捕获TLS(SSL)包tcp port 443.然后找到"客户端Hello"消息.您可以在下面看到其原始数据.

展开 ,你会看到.Handshake包中的服务器名称未加密.Secure Socket Layer->TLSv1.2 Record Layer: Handshake Protocol: Client Hello->...
Extension: server_name->Server Name Indication extension

http://i.stack.imgur.com/qt0gu.png

  • 我们正在寻找一种确定SNI的程序化方法.但是,对于某些人来说这可能会很有趣,所以请不要删除它. (3认同)

Æðe*_*tan 5

对于任何感兴趣的人,这是 C/C++ 代码的暂定版本。到目前为止它已经奏效了。该函数返回服务器名称在包含客户端问候的字节数组中的位置以及参数中名称的长度len

char *get_TLS_SNI(unsigned char *bytes, int* len)
{
    unsigned char *curr;
    unsigned char sidlen = bytes[43];
    curr = bytes + 1 + 43 + sidlen;
    unsigned short cslen = ntohs(*(unsigned short*)curr);
    curr += 2 + cslen;
    unsigned char cmplen = *curr;
    curr += 1 + cmplen;
    unsigned char *maxchar = curr + 2 + ntohs(*(unsigned short*)curr);
    curr += 2;
    unsigned short ext_type = 1;
    unsigned short ext_len;
    while(curr < maxchar && ext_type != 0)
    {
        ext_type = ntohs(*(unsigned short*)curr);
        curr += 2;
        ext_len = ntohs(*(unsigned short*)curr);
        curr += 2;
        if(ext_type == 0)
        {
            curr += 3;
            unsigned short namelen = ntohs(*(unsigned short*)curr);
            curr += 2;
            *len = namelen;
            return (char*)curr;
        }
        else curr += ext_len;
    }
    if (curr != maxchar) throw std::exception("incomplete SSL Client Hello");
    return NULL; //SNI was not present
}
Run Code Online (Sandbox Code Playgroud)