Curl高危漏洞(CVE-2023-38545)分析报告及建议

时间 :  2023年10月11日  来源:  安天CERT

今日,针对Curl爆出的两个高危漏洞(CVE-2023- 38545、 CVE-2023-38546),安天攻防实验室对其进行分析跟进,提供检测方法和加固方法,安天将持续对该漏洞进行利用监测,提取相关特征,进行研究和监测。

1.漏洞背景


Curl是一个网络数据传输项目,通常说Curl是指Curl命令行工具,它支持DICT、FILE、FTP、FTPS、Gopher、HTTP、HTTPS、IMAP、IMAPS、LDAP、LDAPS、POP3、POP3S、RTMP、RTSP、SCP、SFTP、SMB、SMBS、SMTP、SMTPS、Telnet与TFTP等协议,而Curl的底层使用的是libcurl库,libcurl与Curl 组成了 cURL 项目。

Curl被广泛应用于应用功能组件和RPM依赖,Git、Rust、pycurl等均受此次高危漏洞影响,大多数操作系统同样集成libcurl,包括但不限于:Android、Chrome OS、Cisco IOS、iOS、ipadOS、Linux、macOS。

图片引用自github https://github.com/curl/curl/discussions/12026

Curl命令行工具作者bagder于10月11日06:00UTC发布8.4.0版本Curl,该版本Curl修复CVE-2023-38545、CVE-2023-38546两漏洞,由于该漏洞基于libcurl存在,影响范围巨大。同时发布者也确认,依赖libcurl的pycurl等同样受到影响。

图片引用自github https://github.com/curl/curl/discussions/12026

依据Curl发布的公告,本次漏洞主要源于SOCKS5的缓冲区溢出,在调用库的应用设置修改了较小的缓冲区大小或因服务器延迟原因都可能触发此漏洞。

1.1 影响范围

名称

版本号

libcurl

7.69.0 <= libcurl < 8.4.0

1.2 漏洞成因

该漏洞主要成因是在使用SOCKS5代理链接过程中造成的堆溢出。当Curl使用 SOCKS5代理以便它解析地址时,主机名的最大长度为255字节。如果检测到主机名超过255字节,Curl将切换到本地主机解析地址,并将解析后的地址传递给代理。然而,由于程序bug,在建立延迟较高的 SOCKS5 链接过程中,此时主机解析地址可能会获取错误的值,此时会把过长的主机名也复制到缓冲区中。

主机名来自于提供给Curl解析的url。目标缓冲区是libcurl 中基于堆的下载缓冲区,缓冲区的默认大小为16kB,但可以由应用程序设置为不同的大小。Curl 工具默认将其设置为102400字节,但如果 --limit-rate 设置低于102400字节/秒,则会将缓冲区大小设置为较小的值。libcurl提供了CURLOPT_BUFFERSIZE 选项来更改下载缓冲区的大小。libcurl 在 URL 中接受最多达65535字节的主机名。

要发生溢出,需要一个足够慢的SOCKS5连接来触发局部变量bug,并且客户端使用的主机名长于下载缓冲区。通过恶意的 HTTPS 服务器进行重定向到一个特别设计的 URL。某些较老的服务器延迟可能足以触发此漏洞。堆溢出仅可能在未设置 CURLOPT_BUFFERSIZE 或将其设置为小于 65541的情况下发生。

在libcurl中,导致使用远程主机名的SOCKS5的选项为:将CURLOPT_PROXYTYPE 设置为CURLPROXY_SOCKS5_HOSTNAME 类型,或者将CURLOPT_PROXY或 CURLOPT_PRE_PROXY 设置为使用socks5h:// 方案。其中一个代理配置环境变量可以设置为使用socks5h://方式,例如http_proxy、HTTPS_PROXY或 ALL_PROXY。在Curl工具中,导致使用远程主机名的SOCKS5的选项为:--socks5-hostname,或者将--proxy或--preproxy设置为使用socks5h:// 方式。

1.3 漏洞复现

安天攻防实验室已复现CVE-2023-38545漏洞。

2.1 手动版本检测
2.1.1 Curl版本检测

Curl可通过命令直接检测版本。

curl -V

curl --version

2.1.2 libcurl版本检测

依赖libcurl的应用通常会在系统lib目录下以动态链接库形式存放,也可能被集成打包于应用目录下。版本对比检测方案通常有以下两个思路,对目录下的文件进行检测和对运行中进程打开的文件进行检测。

strings libcurl.so | egrep -o 'libcurl[ -/]([678]+\.[0-9]+\.[0-9]+)'

lsof | egrep -o '/.*\.so.*' | xargs -I {} bash -c "strings {} | egrep -o 'libcurl[ -/]([678]+\.[0-9]+\.[0-9]+)'"

ldconfig会在默认的共享库路径(如 /lib、/usr/lib 等)中搜索共享库文件,并创建缓存,将这些共享库的信息存储在缓存中,以便系统在运行时快速找到和加载这些共享库。

ldconfig -p | grep libcurl

dpkg用于管理软件包的基本工具,列出所有已安装软件包的信息。

dpkg -l | grep libcurl

通过下面的python脚本,可以输出Curl的版本信息和libcurl库的路径和版本信息,用于比对是否在漏洞影响范围。后续我们会对脚本进行完善,直接输出受影响的文件全路径。

执行方法,python info.py

import platform

import subprocess

import os


def get_curl_version():

operating_system = platform.system().lower()

if "windows" in operating_system:

return get_windows_curl_version()

elif "linux" in operating_system:

return get_linux_curl_version()

elif "darwin" in operating_system:

return "NA"

else:

return "Unsupported operating system"


def get_libcurl():

uname_output = subprocess.run(['uname', '-a'], capture_output=True, text=True)

uname_output = uname_output.stdout.lower()


if "linux" in uname_output:

return get_linux_libcurl()

elif "darwin" in uname_output:

return get_macos_libcurl()

else:

return "Unsupported operating system"

def get_windows_curl_version():

try:

result = subprocess.run(['curl', '--version'], capture_output=True, text=True)

lines = result.stdout.strip().splitlines()

return lines[0]

except FileNotFoundError:

return "curl is not installed on Windows"


def get_linux_curl_version():

try:

result = subprocess.run(['curl', '--version'], capture_output=True, text=True)

lines = result.stdout.strip().splitlines()

return lines[0]

except FileNotFoundError:

2.1.3 相关第三方模块 libcurl版本检测

import pycurl

print(pycurl.version)

print(pycurl.__file__)


输出:

windows:

PycURL/7.43.0.5 libcurl/7.68.0 OpenSSL/1.1.1d zlib/1.2.11 c-ares/1.15.0 WinIDN libssh2/1.9.0 nghttp2/1.40.0

C:\Python38\lib\site-packages\pycurl.cp38-win_amd64.pyd

linux:

libcurl/7.29.0 NSS/3.53.1 zlib/1.2.7 libidn/1.28 libssh2/1.8.0

/usr/lib64/python2.7/site-packages/pycurl.so

2.2 漏洞批量检测
2.2.1 批量检测方法1

检测逻辑:

通过罗列系统内所有的 curl工具和libcurl.dll文件的版本,方便进行版本比对,检测是否受漏洞影响。Windows/Linux平台,libcurl版本检测python脚本,Windows需搭配everyting软件,Linux 需配合自带命令使用。

使用说明:

1. 复制curl和libcurl的完整路径。

Windows:

在everything中搜索"curl.exe | libcurl.dll",选中需要检测的文件,右键->复制完整路径和文件名。

Linux:

输入以下命令复制结果。

sudo find / -name "curl" -o -name "*libcurl*"

2. 打开path.txt,将复制的内容粘贴进去。

运行python curl_detect.py,输出curl和libcurl.dll的版本信息。

具体python脚本如下:

import re


# 定义正则表达式模式

pattern = re.compile(r'([678]+\.[0-9]+\.[0-9]+)')


# 从 path.txt 文件中读取文件路径

with open('path.txt', 'r') as path_file:

for line in path_file:

file_path = line.strip('"\n').strip("'\n") # 删除双引号、单引号、换行、空白

try:

with open(file_path, 'r', encoding='ascii', errors='ignore') as file:

contents = file.read()

match = pattern.search(contents)

if match:

print(f"{file_path}\n版本:{match.group(1)}")

else:

print(f"{file_path}\n检测版本失败,请确认文件无误")

except:

print(f"{file_path}\n检测版本失败,请确认路径及文件权限")

2.2.批量检测方法2

检测逻辑:

在windows平台下,通过罗列系统内所有的curl.exe 、libcurl.lib和pycurl,进行版本比对,检测是否受漏洞影响。需搭配同目录下精简后的everything工具es.exe使用,其中-f参数为检测模式,默认全部检测(dll、exe、pycurl)。

下面为检测python脚本源码:

import win32api

import subprocess

import pefile

import argparse


def is_16_bit_exe(file_path):

with open(file_path, 'rb') as f:

# Read the MZ header

mz_header = f.read(2)

# Check if it's a valid MZ header

if mz_header != b'MZ':

return False

f.seek(0x3C)

pe_header_addr = int.from_bytes(f.read(4), byteorder='little')

return pe_header_addr < 1024


def run_command(cmd):

try:

process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW)

out, err = process.communicate()

if process.returncode != 0:

return None

return out.decode(encoding="Gbk")

except Exception as e:

print(f"Error: {e}")

return None


def comparaV(v1):

lower_bound = [7, 69, 0]

upper_bound = [8, 4, 0]

input_version = list(map(int, v1.split('.')))

return lower_bound <= input_version < upper_bound


class EXE:

def __init__(self) -> None:

self.dllout = run_command("es.exe curl.exe")

self.dllpath = self.dllout.splitlines() # splitlines()将字符串分割成行的列表

print(len(self.dllpath))

3.安天建议


虽然上述两个漏洞单独对系统的影响有限,但基于如今攻击者通常利用多个漏洞的组合来实施攻击的严峻形势,对其可能造成损失并不能掉以轻心。安天高度关注软件供应链安全,建议用户积极使用代码安全检测工具进行软件物料清单分析,对自身使用的软件成分更加明晰化,主动发现和排查此类漏洞。

安天融川代码安全检测系统 Antiy SCS (以下简称“融川”)提供了组件成分分析(SCA)功能,用户可以通过上传源代码文件或制品文件,快速检查和分析组件清单和漏洞分布情况,及时检测、预防和修复漏洞,保障安全。

在此次Libcurl爆出高危漏洞的第一时间,融川即对客户相关程序进行了深度的组件成分分析与跟踪,及时发现了相关风险,并提供了安全防护措施,有效地保障了客户的相关资产安全,帮助客户业务免受本次漏洞事件的影响。(相关截图已进行脱敏)。

4.修复方案


官方公告影响范围7.69.0 <= libcurl <8.4.0,10月11日发布的curl 8.4.0版本修复了该漏洞。可根据自己的操作系统信息,使用下面对应的命令进行升级安装。

Vulnerabilities in curl 7.69.0

Ubuntu/Debian:

sudo apt-get update && sudo apt-get install curl

Fedora:

sudo dnf upgrade curl

CentOS/RedHat:

sudo yum update curl

macOS:使用Homebrew安装最新版curl:

brew install curl

如未安装Homebrew通过此命令安装:

/bin/bash -c “$(curl -

fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)”

更新PATH来默认使用新版本的Curl:

echo ‘export PATH=”/usr/local/opt/curl/bin:$PATH”‘ >> ~/.bash_profile

Source your updated ~/.bash_profile:

.~/.bash_profile

通过命令确认Curl版本:

curl --version

此外,官方给出的修复建议如下:

1. 勿将CURLPROXY_SOCKS5_HOSTNAME代理与curl一起使用

2. 勿将代理环境变量设置为socks5h://

5.参考信息


https://curl.se/docs/CVE-2023-38545.html https://github.com/curl/curl/discussions/12026