ZeroPathAI/proftpd-CVE-2026-42167-poc
GitHub: ZeroPathAI/proftpd-CVE-2026-42167-poc
针对 ProFTPD mod_sql SQL 注入漏洞(CVE-2026-42167)的概念验证工具集,演示了从预认证 SQL 注入到后门用户植入和远程代码执行的完整攻击链。
Stars: 11 | Forks: 1
# ProFTPD 漏洞 POC
针对 CVE-2026-42167 的概念验证,这是 ProFTPD 的 `mod_sql` 日志管道中的一个 SQL 注入漏洞,允许未经身份验证的攻击者执行任意 SQL —— 并向 FTP 身份验证数据库注入后门用户,或在数据库主机上实现远程代码执行。
所有 POC 均针对 PostgreSQL,但后门用户类的 POC 在经过对注入查询的某些修改后(这留给读者作为练习),也适用于 MySQL 和 sqlite 后端。
- 该漏洞由 ZeroPath Research 发现。[我们的技术博客包含更多细节。](https://zeropath.com/blog/proftpd-cve-2026-42167-auth-bypass-privesc-rce)
## 漏洞详情
### `mod_sql` SQLLog 中的 `is_escaped_text()` 绕过 (CVE-2026-42167, CWE-89)
ProFTPD 的 `mod_sql` 通过 `SQLLog` / `SQLNamedQuery` 机制记录每一条 FTP 命令。当解析像 `%U`(原始用户名)或 `%{basename}`(文件名部分)这样的格式变量时,框架会调用 `is_escaped_text()` 来决定是否需要转义 —— 并且对于任何以单引号开头和结尾且内部不包含单引号的值(例如 `'|| (SELECT 1) ||'`),它会完全跳过转义。这个检查纯粹是语法层面的,并且运行在来自 FTP 会话的、由攻击者控制的原始输入上,因此它无法区分“由可信代码预先转义”还是“由攻击者伪造得看起来像预先转义”。
标准文档中的模式将格式变量用单引号括起来以保证 SQL 安全性:
```
SQLNamedQuery log_activity INSERT "'%U', '%r', '%m'" activity_log
SQLLog * log_activity
SQLLog ERR_* log_activity
```
当攻击者提供形如 `''` 的值时,替换操作会在最终的 SQL 中生成 `''''` —— 空字符串字面量闭合了外部的引号,使得 payload 作为原始 SQL 执行。借助 PostgreSQL (`PQexec`) 或 SQLite (`sqlite3_exec`) 对堆叠查询的支持,payload 可以是一条完整的 `INSERT`、`UPDATE`、`CREATE TABLE` 或 `COPY TO PROGRAM` 语句。
### 服务器何时可被利用?
漏洞代码位于 `contrib/mod_sql.c`(共享 SQL 框架)中,而不在任何特定的后端中,因此所有 SQL 后端都受影响 —— 但攻击面取决于管理员如何配置日志记录。当以下两个条件**同时**满足时,服务器即存在被利用的风险:
1. 管理员定义了一个 `SQLNamedQuery INSERT`(或 `UPDATE`),其格式字符串插值了这些由攻击者可控的变量并用单引号括起来 —— 例如 `"'%U', '%m'"`。来自攻击者输入的变量包括:
| 变量 | 含义 |
|----|----|
| `%A` | 匿名登录密码字符串 |
| `%J` | 命令参数(动词之后的所有内容) |
| `%S` | 响应消息字符串(可能包含在错误中回显的攻击者输入) |
| `%U` | 来自 `USER` 的原始用户名(在身份验证之前设置,甚至可用于失败的登录) |
| `%d` | 目录名(路径的最后部分) |
| `%l` | RFC 1413 ident 响应(如果攻击者运行 identd,则受其控制) |
| `%m` | FTP 方法/动词(攻击者选择发送哪个命令) |
| `%r` | 完整的 FTP 命令(动词 + 参数) |
| `%u` | 已认证的用户名 |
| `%{basename}` | 路径参数的文件名部分,无目录前缀 |
`%f`、`%F` 和 `%D` 看似受攻击者控制,但它们总是解析为以 `/` 开头的绝对路径,因此它们无法满足 `is_escaped_text()` 以 `'` 开头的要求。
2. 管理员已将该 `SQLNamedQuery` 绑定到一条 `SQLLog` 指令上,用于攻击者可以访问的 FTP 命令(或命令类别)。被广泛文档化的 `SQLLog *` 和 `SQLLog ERR_*` 通配符是覆盖范围最广的情况,这也正是 `%U` 路径能在预认证阶段触发的原因:`ERR_*` 在 `USER` 失败时触发,因此不需要任何凭据。针对特定命令的指令(如 `SQLLog STOR`)则适用于任何已认证用户。
### 攻击者能做什么?
此绕过将日志记录路径转变为后端上的任意 SQL 原语。影响最大的两种场景是:
- **注入具有任意权限的后门用户(身份验证绕过)。**
ProFTPD 的 SQL 认证后端会从日志 INSERT 操作现在可以写入的同一张 `users` 表中读取用户名、密码哈希、uid、gid、homedir 和 shell。堆叠的 `INSERT INTO users` 会植入一个由攻击者指定的账户 —— `uid=0`、`homedir=/`、明文密码 —— 随后攻击者可以通过 FTP 守护程序正常登录并获得完整的文件系统访问权限。可通过 `%U` + `SQLLog ERR_*` 路径在预认证阶段触达,或者在认证后通过绑定到攻击者可发出命令的任何攻击者可控变量触达(例如 `%{basename}` + `SQLLog STOR`)。适用于 PostgreSQL 和 SQLite(两者均支持堆叠查询)。
- **通过 `COPY TO PROGRAM` 在数据库主机上实现远程代码执行。**
PostgreSQL 的 `COPY (SELECT …) TO PROGRAM ''` 会通过数据库服务器上的 shell 运行 ``。发出 `COPY TO PROGRAM` 的堆叠查询注入能够以 postgres OS 用户的身份执行任意操作系统命令,这足以用于凭据窃取、横向移动和权限持久化。可通过与后门案例相同的触发路径触达(预认证通过 `%U`,或认证后通过 `%{basename}` 等)。仅适用于 PostgreSQL,且要求 ProFTPD 数据库角色具有超级用户权限(这是 `COPY TO PROGRAM` 的先决条件) —— 在角色为数据库所有者的单租户部署中很常见。
### POC 选择
本仓库中的 POC 专门针对 **PostgreSQL** —— 这是最具影响力的场景,因为 `PQexec()` 支持堆叠查询,并且 `COPY TO PROGRAM` 提供了直接的操作系统命令执行能力。该绕过本身存在于共享 SQL 框架中并影响所有后端:
- **PostgreSQL**(此处涵盖):通过 `PQexec` 实现堆叠查询,通过 `COPY TO PROGRAM` 实现 RCE。
- **SQLite**:通过 `sqlite3_exec` 实现堆叠查询,在 proftpd worker 中以 `PRIVS_ROOT` 运行 —— 后门注入的工作方式相同;RCE 原语有所不同(没有等效于 `COPY TO PROGRAM` 的功能,但在以 root 进程运行的 SQL 后端上可写的 `users` 表已经足够了)。
- **MySQL**:绕过以相同的方式触发,但在单个日志 INSERT 内部触达 `users` 表或实现操作系统执行更加困难。单语句操作(在 VALUES 插槽中进行子查询数据窃取、基于时间的盲注、基于错误的盲注)很简单;要将其转化为对*不同*表的写入,需要克服以下几个障碍:
- `mod_sql_mysql` 调用 `mysql_real_query()` 时未带有 `CLIENT_MULTI_STATEMENTS`,因此末尾的 `; INSERT INTO users …` 会被连接拒绝。
- 攻击者必须弄清楚如何绕过此限制才能写入 `users` 表或磁盘上的文件。
在 PostgreSQL 设置中,POC 演示了两种具有代表性的触发路径。上表中的其他组合在原理上是等效的:
- **通过 `%U` + `USER` 预认证。** `SQLLog ERR_*` 使这成为完全无需认证的触发方式。
- **通过 `%{basename}` + `STOR` 认证后触发。** 需要任何 FTP 凭据,但除了上传文件的能力外不需要其他权限。
## 仓库内容
- **`setup/`** —— 自动化环境设置。克隆报告这些发现时所针对的特定 commit 的 ProFTPD 源码树,使用 `mod_sql` + `mod_sql_postgres` 构建守护程序,并启动一个包含种子数据和易受攻击的 SQLLog 配置的 Docker Compose 集群(ProFTPD + PostgreSQL)。
- **`pocs/`** —— 五个漏洞利用脚本。全部是自包含的 Python 脚本,仅需要标准库。
- `preauth_user_backdoor.py` —— 预认证 `%U` 触发器 → 后门用户(uid=0,homedir=`/`)被注入到认证 DB 中。无需凭据,无需 DB 超级用户。此 POC 针对 PostgreSQL,但该问题同样可以利用 mysql 或 sqlite 后端进行利用。
- `preauth_user_rce.py` —— 预认证 `%U` 触发器 → 通过 `COPY TO PROGRAM` 在 PostgreSQL 主机上实现 RCE。无需凭据。要求 ProFTPD DB 角色为 PostgreSQL 超级用户。
- `postauth_stor_backdoor.py` —— 认证后 `%{basename}` 触发器 → 注入后门用户。需要任何已认证的 FTP 用户。此 POC 针对 PostgreSQL,但该问题同样可以利用 mysql 或 sqlite 后端进行利用。
- `postauth_stor_rce.py` —— 认证后 `%{basename}` 触发器 → 在 PostgreSQL 主机上实现 RCE。需要任何已认证的 FTP 用户和 PostgreSQL 超级用户 DB 角色。
- `postgres_blind_dump.py` —— 预认证 `%U` 触发器 → 基于时间的盲注提取认证 `users` 表。不使用堆叠查询,因此适用于 proftpd DB 角色仅具有最低权限(仅对日志表具有 `INSERT` 权限)且上述后门/RCE POC 会失败关闭的部署。通过 `pg_sleep()` 一次一位地使用二分搜索提取每一列的每一个字节 —— 包括 `passwd` 列。反映了 sqlmap 会自动执行的操作,无需第三方依赖,纯手工打造。
## 使用说明
前置条件:Docker、Git、Python 3.10+ 以及 [uv](https://docs.astral.sh/uv/getting-started/installation/)。
```
cd setup
./setup.sh
```
首次运行会克隆 ProFTPD 源码并构建服务器(约 2-3 分钟)。随后的运行会重用构建缓存并在几秒钟内启动。
当设置完成时,它会打印完整的环境详细信息(FTP 和 DB 端点、测试凭据以及可直接粘贴运行的 POC 命令)。按照这些说明运行 POC。
若要拆除环境:
```
cd setup
./teardown.sh
```
标签:CISA项目, Cutter, CVE-2026-42167, CWE-89, FTP服务器, Maven, mod_sql, POC, PostgreSQL, ProFTPD, RCE, SQLite, ZeroPath Research, 后门植入, 提权, 未授权攻击, 测试用例, 漏洞验证, 编程工具, 请求拦截, 身份验证绕过, 远程代码执行, 逆向工具