手搓https协议 - client hello

admin 2024年11月21日13:39:23评论14 views字数 7050阅读23分30秒阅读模式

Q4忙其他的事情。发点存货,2022年的时候给EDR驱动做纯内核https通讯写的东西。因为对接的是grpc,而grpc是基于tls + http3的(这个东西也是依托答辩),所以在内核实现了一个内核版本的GPRC+TLS+http3(纯人肉手搓,天然无添加)。把实现步骤放在这里,现在也快忘的差不多了,要是这篇文章流量高,继续更新整个系列(估计说完要七八篇)

0x0 简介

年尾了,发点存货吧.人肉实现GRPC.主要需求是windows内核实现GRPC的交互

GRPC是由三个部分组成的,继承了谷歌丧心病狂的设计.要实现GRPC,就要依次实现

TLS 1.2
HTTP2
Protobuf payload

(内核WSK我就不说了,烂大街了)

今天来介绍第一部分,TLS1.2握手
https://www.ietf.org/rfc/rfc5246.txt

手搓https协议 - client hello

!!注意!!此图不完整,虽然在网上疯传,但是真的不完整.后面会说

这部分感谢@heromantf 的聪明的大脑帮助,没有它圆锥曲线计算部分就很痛苦.

0x1 Client Hello

这个跟客户端说 hello 一样.数据结构如下,依次告诉客户端如下信息:

协议版本
客户端随机数据(稍后在握手中使用)
要恢复的可选会话 ID
密码套件列表
压缩方法列表
扩展列表

buff如下:

16 03 01 00 a5 01 00 00 a1 03 03 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 00 00 20 cc a8 cc a9 c0 2f c0 30 c0 2b c0 2c c0 13 c0 09 c0 14 c0 0a 00 9c 00 9d 00 2f 00 35 c0 12 00 0a 01 00 00 58 00 00 00 18 00 16 00 00 13 65 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 00 05 00 05 01 00 00 00 00 00 0a 00 0a 00 08 00 1d 00 17 00 18 00 19 00 0b 00 02 01 00 00 0d 00 12 00 10 04 01 04 03 05 01 05 03 06 01 06 03 02 01 02 03 ff 01 00 01 00 00 12 00 00

让我们来拆分他:

16 03 01 00 a5

TLS 会话分为”记录”的发送和接收,”记录”是具有类型、协议版本和长度的数据块

16 - 类型是 0x16 (handshake record)
03 01 协议版本为 3.1(也称为 TLS 1.0)
00 a5 - 0xA5 (165) 握手消息的字节长度

注意协议版本为什么是3.1 在golang的crypto库中的原因如下:

if vers == 0 {
// Some TLS servers fail if the record version is
// greater than TLS 1.0 for the initial ClientHello.
vers = VersionTLS10
}
01 00 00 a1

这个是握手头,每个头都有
每个握手消息都以类型和长度开头

01 本消息类型是 0x01 (client hello)
00 00 a1 - 0xA1 (161) 长度
03 03

客户端版本
这里给出了”3,3”(意思是 TLS 1.2)的协议版本。
不寻常的版本号(”3,3”代表 TLS 1.2)是由于 TLS 1.0 是 SSL 3.0 协议的次要修订版。因此 TLS 1.0 表示为”3,1”,TLS 1.1 表示为”3,2”,依此类推

00 01 02 … 1e 1f

客户端提供 32 个字节的随机数据
TLS 1.2 规范说前 4 个字节应该是当前时间(以秒为单位,自 1970 年以来),但现在建议不要这样做,因为它可以对主机和服务器进行指纹识别
https://datatracker.ietf.org/doc/html/draft-mathewson-no-gmtunixtime-00

00

会话ID
客户端可以提供它能够恢复的针对该服务器的先前 TLS 会话的 ID。为此,服务器和客户端都将记住内存中先前连接的关键信息。恢复连接可以节省大量计算和网络往返时间,因此只要有可能就会执行

00 20 cc … 12 00 0a

加密套件(重要)
客户端提供了一个有序列表,其中列出了它将支持哪些加密方法来进行密钥交换、使用该交换密钥进行加密以及消息身份验证。该列表按照客户偏好的顺序排列,优先级最高的优先

00 20 - 0x20 (32) bytes of cipher suite data
cc a8 - assigned value for TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
cc a9 - assigned value for TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
c0 2f - assigned value for TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
c0 30 - assigned value for TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
c0 2b - assigned value for TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
c0 2c - assigned value for TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
c0 13 - assigned value for TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
c0 09 - assigned value for TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
c0 14 - assigned value for TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
c0 0a - assigned value for TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
00 9c - assigned value for TLS_RSA_WITH_AES_128_GCM_SHA256
00 9d - assigned value for TLS_RSA_WITH_AES_256_GCM_SHA384
00 2f - assigned value for TLS_RSA_WITH_AES_128_CBC_SHA
00 35 - assigned value for TLS_RSA_WITH_AES_256_CBC_SHA
c0 12 - assigned value for TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
00 0a - assigned value for TLS_RSA_WITH_3DES_EDE_CBC_SHA
01 00

压缩方法
客户端提供它将支持的压缩方法的有序列表。这种压缩将在加密之前应用(因为加密数据通常是不可压缩的)。
01 00代表不压缩.因为最新标准确认启用压缩会让加密变得不那么可靠
https://en.wikipedia.org/wiki/CRIME

00 58

扩展长度
客户端提供了一个可选扩展列表,服务器可以使用这些扩展来采取行动或启用新功能。
00 58代表这个区域最长0x58字节
这个区域也是放DNS信息的地方,所谓通过TLS知道DOMAIN也是从这个地方解码的
当然这个地方信息对于中间人来说不可靠 因为可以给任意的域名!

00 00 00 …. 6e 65 74

扩展信息 - 服务端名字
客户端提供了它正在联系的服务器的名称,也称为 SNI -服务器名称指示
如果没有此扩展,HTTPS 服务器将无法为单个 IP 地址(虚拟主机)上的多个主机名提供服务,因为在协商 TLS 会话并发出 HTTP 请求之前,它无法知道要发送哪个主机名的证书

00 00 - 扩展“服务器名称”的值
00 18 - 底下有24字节长度
00 16 - 底下有22字节长度
00 - type 0x0代表是 "DNS主机名"
00 13 - 0x13个字节的长度
65 78 61 ... 6e 65 74 - "example.ulfheim.net"
00 05 … 00

客户端允许服务器在其响应中提供 OCSP 信息。OCSP 可用于检查证书是否已被吊销。
客户端必须发送空扩展名,因为服务器使用客户端未先提供的扩展名进行回复会导致致命错误.因此,客户端发送一个空形式的扩展,服务器用填充了数据的扩展进行回复

00 0a 00 … 18 00 19

客户表示它支持 4 条曲线的椭圆曲线 (EC) 加密。此扩展最初名为“椭圆曲线”,但已重命名为“受支持的组”以通用于其他密码类型。

00 0a - 扩展类型: 支持的组
00 0a - 10字节使用
00 08 - 8字节使用
00 1d - "x25519"
00 17 - "secp256r1"
00 18 - "secp384r1"
00 19 - "secp521r1"
00 0b 00 02 01 00

EC Point Formats
在椭圆曲线 (EC) 加密期间,客户端和服务器将以压缩或未压缩的形式交换有关所选点的信息。该扩展表明客户端只能解析来自服务器的未压缩信息

00 …. 01 02 03

Signature Algorithms
此扩展指示客户端能够理解哪些签名算法,并可能影响服务器发送给客户端的证书选择

00 0d - 扩展类型: Signature Algorithms
00 12 - 18字节以下的内容
00 10 - 16字节以下的内容
04 01 - RSA/PKCS1/SHA256
04 03 - ECDSA/SECP256r1/SHA256
05 01 - RSA/PKCS1/SHA384
05 03 - ECDSA/SECP384r1/SHA384
06 01 - RSA/PKCS1/SHA512
06 03 - ECDSA/SECP521r1/SHA512
02 01 - RSA/PKCS1/SHA1
02 03 - ECDSA/SHA1
ff 01 00 01 00

重新协商信息,这个扩展存在漏洞,已经不用
https://kryptera.se/Renegotiating%20TLS.pdf

00 12 00 00

SCT 用于时间戳校验.保持不变即可

0x2 包构造

这边为了演示临时写的python脚本,用的是AES加密方法(因为python不想搞太复杂了),但是GRPC是要求最低加密方法为圆锥曲线的,ECDH,所以你自己想办法,代码是不可能给你抄的.
由于只支持AES,所以必须让服务端认为我们只有TLS_RSA_WITH_AES_128_CBC_SHA1这个协议.所有包结构的长度都要变

client_hello_data_start = [0x16, 0x03, 0x01,
0x00, 0xCC, 0x01, 0x00, 0x00, 0xCC, 0x03, 0x03]
client_hello_data_rand_num = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f]
client_hello_data_session_id = [0x00]
# 默认全部支持
#client_hello_data_cipher_length = [0x00, 0x20]
# client_hello_data_cipher_support_list = [0xcc, 0xa8, 0xcc, 0xa9, 0xc0, 0x2f, 0xc0, 0x30, 0xc0, 0x2b, 0xc0, 0x2c, 0xc0,
# 0x13, 0xc0, 0x09, 0xc0, 0x14, 0xc0, 0x0a, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0xc0, 0x12, 0x00, 0x0a]
# 只支持TLS_RSA_WITH_AES_128_CBC_SHA1
client_hello_data_cipher_length = [0x00, 0x02]
client_hello_data_cipher_support_list = [0x00, 0x2f]
client_hello_data_start_1 = [0x01, 0x00, 0x00, 0xCC,
0x00, 0x00, 0x00, 0xCC, 0x00, 0xCC, 0x00, 0x00, 0xCC]
client_hello_data_end = [0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01,
0x00, 0x00, 0x0d, 0x00, 0x12, 0x00, 0x10, 0x04, 0x01, 0x04, 0x03, 0x05, 0x01, 0x05, 0x03, 0x06, 0x01, 0x06, 0x03, 0x02, 0x01, 0x02, 0x03, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00, 0x00]

注意: 所有包结构都要动态变化,因为支持的加密套件变了.
此外根据协议可以知道,扩展中的domain name最长是size(short).我这边由于是百度的www.baidu.com所以只占了一个字节

head_length_pos = 4
body_length_pos = 8
# 全密码长度是81 但是我们要自定义密码.所有要减掉一部分长度,基础长度是47
extern_length_pos = 47 + len(client_hello_data_cipher_length) +
len(client_hello_data_cipher_support_list)
# 全密码85
extern_1_length_pos = extern_length_pos + 4
# 全密码87
extern_2_length_pos = extern_1_length_pos + 2
# 全密码90
extern_3_length_pos = extern_2_length_pos + 3
domain_host_name = "www.baidu.com"
domain_host_name_binary = []
for iter in range(len(domain_host_name)):
domain_host_name_binary.append(ord(domain_host_name[iter]))
domain_host_name_length = len(domain_host_name_binary)
full_client_hello = []
full_client_hello.extend(client_hello_data_start)
# client_hello_data_rand_num要自己生成
full_client_hello.extend(client_hello_data_rand_num)
full_client_hello.extend(client_hello_data_session_id)
full_client_hello.extend(client_hello_data_cipher_length)
full_client_hello.extend(client_hello_data_cipher_support_list)
full_client_hello.extend(client_hello_data_start_1)
full_client_hello.extend(domain_host_name_binary)
full_client_hello.extend(client_hello_data_end)
full_client_hello[extern_3_length_pos] = domain_host_name_length
full_client_hello[extern_2_length_pos] = domain_host_name_length + 3
full_client_hello[extern_1_length_pos] = domain_host_name_length + 5
full_client_hello[extern_length_pos] = len(
client_hello_data_end) + domain_host_name_length + 9
# 8是头大小,如果太大则需要占两位.记得做长度校验!
full_client_hello[body_length_pos] = len(full_client_hello) - 9
# 4是头大小,如果太大则需要占两位.记得做长度校验!
full_client_hello[head_length_pos] = len(full_client_hello) - 5
print("client hello:")
for iter in range(len(full_client_hello)):
print("%02x" % full_client_hello[iter], end=" ")
# client hello
all_hand_shake_message += bytes(full_client_hello[record_layer_head_size:])
socket_client.send(bytes(full_client_hello))

让我们看看:

手搓https协议 - client hello

这样我们第一个client hello world就完成了

原文始发于微信公众号(冲鸭安全):手搓https协议 - client hello

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月21日13:39:23
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   手搓https协议 - client hellohttps://cn-sec.com/archives/3399223.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息