深入解析cURL schannel.c整数溢出漏洞与TLS传输安全

本文详细分析了cURL库中schannel.c文件存在的整数溢出漏洞,该漏洞在TLS加密数据传输过程中可能导致数据大小计算错误和安全问题,包含完整的漏洞原理、PoC代码和影响评估。

整数溢出在schannel.c TLS数据传输中的漏洞分析

漏洞概述

该漏洞允许在加密数据传输过程中添加TLS缓冲区大小时发生整数溢出,这可能导致发送错误的数据大小和TLS安全问题。

在Windows 10环境测试中,Windows的Schannel拒绝了构造的畸形TLS握手(SEC_E_INVALID_TOKEN),这是预期的,因为漏洞代码位于:./lib/vtls/schannel.c

1
2
/* send the encrypted message including header, data and trailer */
len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;

漏洞详情

在TLS连接建立后的规则中也会发生这种情况。这发生在数据传输阶段,而不是握手本身,Windows会验证TLS记录并拒绝明显的畸形记录。

cURL添加了三个缓冲区:outbuf[0]是TLS头部,outbuf[1]是加密数据,outbuf[2]是TLS尾部。cURL在没有检查整数溢出的情况下将这三个值相加,在32位测试环境中,如果大小足够大,加法可能会溢出,导致len值变小。

这可能导致不正确的数据传输大小、TLS协议违规、内存安全问题,最坏情况下,如果溢出导致cURL发送的数据少于预期,可能会绕过安全机制。

概念验证

可以通过附带的PoC进行测试,其中运行恶意HTTP服务器,任何连接到此类恶意服务器的Windows用户都会受到影响。实际利用的唯一可行方法需要绕过Windows Schannel的有效性验证,因为会收到SEC_E_INVALID_TOKEN拒绝畸形TLS。这也在32位Windows系统上进行了测试,其中回绕是可能的。

日志记录

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
=== Schannel Vulnerability PoC Server (Windows) ===
This server attempts to trigger the integer overflow
in curl's schannel.c by sending crafted TLS responses

[*] Initializing Winsock...
[*] Malicious server listening on port 4433
[*] Test with: curl -k https://localhost:4433
[*] Client connected
[*] Received 1815 bytes (ClientHello)
[*] Sending malicious TLS record...
[*] Sent malicious TLS records
[*] Connection closed

数学PoC代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#define SECURITY_WIN32 1
#include <windows.h>
#include <security.h>
#include <schannel.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    printf("Curl Schannel Integer Overflow PoC\n");
    printf("==================================\n\n");

    SYSTEM_INFO si;
    GetSystemInfo(&si);
    printf("Architecture: %s\n", si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? "64-bit" : "32-bit");
    printf("sizeof(size_t): %llu bytes\n\n", (unsigned long long)sizeof(size_t));

    SecBuffer outbuf[3];
    outbuf[0].cbBuffer = 0x80000000;
    outbuf[1].cbBuffer = 0x80000001;
    outbuf[2].cbBuffer = 0x00000000;

    size_t len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;

    printf("Vulnerable code: len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer\n\n");
    printf("0x%08lX + 0x%08lX + 0x%08lX = 0x%llX\n", 
           (unsigned long)outbuf[0].cbBuffer,
           (unsigned long)outbuf[1].cbBuffer, 
           (unsigned long)outbuf[2].cbBuffer,
           (unsigned long long)len);

    unsigned long long expected = 0x100000001ULL;

    if(sizeof(size_t) == 4 && len != expected) {
        printf("\n*** INTEGER OVERFLOW on 32-bit! ***\n");
        printf("Expected: %llu bytes\n", expected);
        printf("Got: %llu bytes\n", (unsigned long long)len);
    }

    return 0;
}

复现方法

在Windows上编译恶意TLS服务器并运行32位版本:

1
i686-w64-mingw32-gcc -Wall -Wextra -g3 malicious_server.c -o malicious_server.exe -lws2_32

执行:

1
curl -k https://localhost:4433/

受影响版本

  • 8.15.0: 发布版本
  • 8.16.0: 开发版本

影响评估

摘要: 如果凭据通过TLS传输,攻击者可以实现身份验证绕过,例如发送4GB的加密认证数据,但实际上由于溢出只发送一个字节,导致服务器收到不完整的凭据。

此外,可能通过再次操纵TLS握手来触发大缓冲区大小,导致相同的溢出,使应用程序接收的数据少于预期,从而阻止数据泄露。

这还可能导致DoS,因为当len非常小时可能出现无限循环,或者由于协议违规或重复连接尝试耗尽资源而导致连接完全挂起。

现实世界: 攻击者需要控制或破坏TLS服务器,操纵Windows的EncryptMessage()返回特定的缓冲区大小,并在数据传输期间触发溢出,因此这可能类似于传输不完整的安全补丁、显示完成但实际上被截断的文件传输或截断的API请求。

讨论记录

研究人员还发现了./lib/vauth/krb5_sspi.c中的类似整数溢出漏洞,由于攻击向量和严重性不同,将作为单独报告提交。

cURL开发团队对漏洞的实用性表示怀疑,询问是否可能在实际环境中使总和超过2^31。研究人员确认可以通过恶意TLS服务器故意构造超大的无效TLS缓冲区来触发,但需要单独的漏洞来绕过Windows的验证。

最终,cURL团队认为这不是安全问题,因为在实际Windows环境中不会被触发,Schannel会阻止尝试,但按照项目透明政策要求披露此报告。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计