Advisories

A fix isn't fixed until it ships

By Will·
Affected
ProFTPD mod_sql - numeric %{n} log-tag (stable ≤ 1.3.8)
CVSS
Disclosed
August 20, 2026

The short version

ProFTPD is one of the most widely deployed FTP servers on the internet - the file-transfer layer under shared-hosting stacks, universities, and enterprises worldwide.

In 2026 ProFTPD dealt with a serious flaw class: unauthenticated SQL injection through the login handshake, no password required. The named-tag version was CVE-2026-42167 (CVSS 8.1). A sibling in the numeric log-tag path was fixed on the development branch in July - and then sat there. The fix landed in master and never got backported to the stable branch everyone actually runs. For a month the current stable release stayed exploitable to a bug the project had already fixed in its own tree.

We found that gap independently, proved it against real ProFTPD, and flagged the missing backport. The stable branch got the fix.

What was actually broken

ProFTPD's mod_sql module logs FTP activity into a database. Log lines are built from *tags* - %U for username, %f for file, and a raw numeric %{n} tag that captures exactly what the client typed. The SQL-injection hardening escaped the named tags. The numeric %{n} tag - untouched for years - never got routed through it.

So on the stable release, an attacker could open a TCP connection, send one line, and never send a password:

USER dummy '||(SELECT(passwd)FROM(users))||'

ProFTPD treats any username as "successful" for logging, so the malicious log write executes *before* authentication. Pre-auth. Unauthenticated. One line, and the server runs your SQL against its own database.

What we proved

We built the current stable ProFTPD from public source in a sealed sandbox and ran the attack end to end:

rowid  entry
-----  -----------------------------
1      42                              <- 40+2 evaluated as SQL, not stored as text
2      SECRET-ADMIN-HASH-DO-NOT-LEAK   <- read from an unrelated credentials table

Line 1: the server *executed* attacker SQL. Line 2: the payoff - reading secrets out of tables the FTP login should never touch. Confirmed against both SQLite and a real PostgreSQL backend.

What it is: unauthenticated, pre-auth database read - credential and data exfiltration. What it is not: we tested the code-execution question honestly. RCE is reachable only on PostgreSQL, and only under a non-default, over-privileged grant real deployments are advised against - and not at all on MySQL/MariaDB. We reported it as exactly what it is, not what sounds scariest.

The part most scanners miss

This bug was already fixed. A researcher found it, the maintainer patched it, the commit landed. By every tracker's definition, "handled." But the patch only lived on the development branch. The stable release - the artifact actually running in production - never received it. A patch that hasn't shipped protects nobody.

The gap between "fixed in a commit" and "fixed on your server" is where a huge share of real exposure lives - and it's invisible unless something is actively diffing what shipped against what was patched.

Outcome

Reported to the maintainer with the reproduction. The fix was backported to the stable branch. Disclosed here after remediation.

The maintainer confirmed the catch and credited it publicly on the backport commit:

Will at Zauth rightly pointed out that this had already been fixed in master via commit cbcc9b4 - but that that commit hadn't been backported to the 1.3.9 branch, and thus hadn't been released.

TJ Saunders (@Castaglia), ProFTPD maintainer · view the comment