← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-42167 · CWE-89 · Disclosed 2026-04-28

mod_sql in ProFTPD before 1

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is a loaded trapdoor, not an open front door

CVE-2026-42167 is an SQL injection flaw in ProFTPD mod_sql affecting upstream releases before 1.3.9a. The bug sits in how attacker-controlled values are treated as already-escaped text when SQLNamedQuery expands log variables such as %U, %r, %f, or similar. The nastiest pre-auth path is when admins log failed USER events via SQLLog ERR_* or broad SQLLog * rules and include %U; then an unauthenticated client can feed SQL into the logging path before login succeeds.

The vendor's HIGH 8.1 baseline is directionally right because this can become auth bypass or even DB-host RCE. But real-world exploitability is much narrower than raw CVSS implies: you need ProFTPD exposed, mod_sql enabled, vulnerable SQLLog/SQLNamedQuery usage, and usually a cooperative backend such as PostgreSQL or SQLite for the cleanest abuse. That config dependency keeps this out of CRITICAL for most enterprises, but if you run shared-hosting-style ProFTPD with SQL auth/logging, it deserves urgent attention.

"Dangerous when present, but the exploit chain is narrower than the headline suggests"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Find reachable ProFTPD targets

Attackers use internet scanning, Shodan, or a nuclei workflow to locate FTP services presenting ProFTPD banners. Public tooling already references this CVE, so discovery is cheap. The problem is not every ProFTPD host is actually exploitable; version and banner checks only identify candidates.
Conditions required:
  • Internet-reachable FTP service
  • ProFTPD deployed on the target
Where this breaks in practice:
  • Many enterprises do not expose ProFTPD to the public internet
  • Banner/version checks cannot prove mod_sql or vulnerable logging is enabled
Detection/coverage: External attack-surface tools and banner scanners will find candidates, but they overcount because exploitability is configuration-dependent.
STEP 02

Trigger the pre-auth SQL injection via USER

Weaponized PoCs such as the ZeroPathAI repository send crafted usernames that abuse mod_sql variable expansion in SQLNamedQuery. The clean pre-auth case requires SQLLog ERR_* or similarly broad logging plus an attacker-controlled token like %U so the failed USER request is written into SQL before authentication. ProjectDiscovery nuclei has also published a detection template, which lowers attacker effort.
Conditions required:
  • mod_sql enabled
  • A vulnerable SQLNamedQuery uses attacker-controlled fields
  • SQLLog ERR_* or SQLLog * reaches the vulnerable query
  • The target value is inserted through the flawed escaping path
Where this breaks in practice:
  • mod_sql is an extension, not a universal default
  • Many deployments do not log failed USER requests into SQL
  • Admins may use SQL auth without the vulnerable logging pattern
Detection/coverage: Watch ProFTPD auth-failure logs for malformed usernames containing quotes, concatenation operators, or stacked-query markers; NIDS coverage can flag suspicious FTP USER payloads, but benign username oddities create noise.
STEP 03

Turn SQL injection into auth bypass

If the same SQL backend stores authentication data, attackers can use the logging primitive to insert a backdoor FTP account and then log in normally. The public PoC documents this path against PostgreSQL and notes equivalent write-oriented abuse on SQLite. This is the most enterprise-relevant outcome because it bypasses the need for DB-host command execution entirely.
Conditions required:
  • SQL backend is also used for ProFTPD authentication
  • Backend/query path supports writes useful to the attacker
  • DB account used by ProFTPD can modify the relevant auth tables
Where this breaks in practice:
  • Separate logging and auth databases break the neatest abuse chain
  • MySQL is harder here because CLIENT_MULTI_STATEMENTS is not enabled in the described path
  • Least-privileged DB roles can limit table writes
Detection/coverage: Database audit logs can catch unexpected INSERT statements into user/auth tables; most package scanners will miss this because the risk depends on live config and DB privileges.
STEP 04

Escalate to database-host RCE

The highest-impact chain uses PostgreSQL COPY ... TO PROGRAM after the SQL injection lands. That produces OS command execution on the database host as the postgres user, which can then be parlayed into credential theft, persistence, or lateral movement. This is real, but it is the narrowest branch of the tree.
Conditions required:
  • PostgreSQL backend in use
  • ProFTPD DB role has superuser-equivalent rights needed for COPY TO PROGRAM
  • Backend allows the injected statement form to execute
Where this breaks in practice:
  • Many sane PostgreSQL deployments do not grant ProFTPD a superuser role
  • Managed DB services often block dangerous program-execution features
  • RCE lands on the DB host, not necessarily the FTP host
Detection/coverage: PostgreSQL logs, EDR on the DB server, and process creation telemetry are the best detection points; version scanners alone cannot validate this branch.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo confirmed CISA KEV listing and no public source found showing a named mass-exploitation campaign. VulnCheck says no public threat-intel feed had tagged a dedicated campaign at publication time, but judged opportunistic exploitation likely because a working PoC was already public.
PoC availabilityPublic exploit material is available: ZeroPathAI/proftpd-CVE-2026-42167-poc documents pre-auth backdoor-user and PostgreSQL RCE paths, and ProjectDiscovery nuclei-templates added a template for this CVE.
EPSSEPSS from your intel block is 0.0699. That's not top-tier internet fire, but it is high enough that defenders should expect commoditized testing once exposure is identified.
KEV statusNot KEV-listed based on the user-supplied intel and no CISA search result for this CVE. That removes the strongest current exploitation amplifier.
CVSS vector meaningCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H means unauthenticated network reachability with high attack complexity. That AC:H is doing real work here because exploitability depends on a specific logging/query/backend design, not just a vulnerable binary.
Affected versionsUpstream ProFTPD before 1.3.9a is affected. Debian also tracked the issue as affecting stable and oldstable package lines, including 1.3.8.c+dfsg-4+deb12u4 and 1.3.7a+dfsg-12+deb11u5 before distro backports.
Fixed versionsUpstream fix is 1.3.9a; the first release-candidate line 1.3.10rc1 also announced a fix. Debian proposed/backported fixes include 1.3.8.c+dfsg-4+deb13u2 for trixie.
Exposure dataVulnCheck reported a Shodan query returning roughly 615,000 ProFTPD banners on port 21 globally, while citing ZeroPath's estimate that at least ~1% of publicly accessible ProFTPD instances may be vulnerable to the pre-auth path. That suggests the truly exploitable population is likely in the low thousands, not hundreds of thousands.
Disclosure timelinePublic disclosure landed on 2026-04-28. The openwall summary cites report date 2026-03-28, fix commit on 2026-04-27, and release of 1.3.9a on 2026-04-27.
Researcher / reportingThe issue discussion and PoC trail point to ZeroPath Research and GitHub issue #2052 (LeftenantZero) as the public technical reporting path.
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to HIGH (7.2/10)

The decisive factor is configuration reachability: exploitation usually requires mod_sql plus a vulnerable SQLLog/SQLNamedQuery pattern, which sharply reduces the exposed population versus a generic pre-auth daemon RCE. It stays in HIGH because the reachable branch is still unauthenticated remote abuse with public PoCs, and the resulting impact can be auth bypass or RCE on the database host.

HIGH Technical root cause and affected upstream version range
MEDIUM Real-world prevalence of exploitable `mod_sql` logging configurations
MEDIUM Current exploitation status in public reporting

Why this verdict

  • Downward pressure: requires more than a version match — attackers need ProFTPD, mod_sql, and a vulnerable SQLLog/SQLNamedQuery design using attacker-controlled substitutions such as %U or %f.
  • Downward pressure: pre-auth reachability is a special case — the headline pre-auth path specifically hinges on logging failed USER requests with ERR_* or broad wildcard logging, which many deployments will not do.
  • Downward pressure: RCE is not the default outcome — the clean RCE branch depends on PostgreSQL plus a role powerful enough for COPY TO PROGRAM; many environments will top out at SQL abuse or auth bypass, not OS command execution.
  • Upward pressure: attacker position is excellent when the config exists — no credentials, no user interaction, public PoC, and raw FTP exposure mean low operational cost for adversaries once they find the right target.
  • Upward pressure: blast radius can still be ugly — if SQL auth and logging share the same backend, attackers can plant an account and convert a logging bug into durable file-system access through the daemon.

Why not higher?

This is not a broad-spectrum internet RCE against all ProFTPD instances. The exploit chain compounds multiple prerequisites: external exposure, mod_sql, vulnerable SQL logging, and sometimes backend-specific privileges. Those are real narrowing factors, so calling it CRITICAL would overstate the population actually one-packet-away from compromise.

Why not lower?

Once the vulnerable config exists, this is still unauthenticated remote abuse with working public exploit material. The auth-bypass path is operationally meaningful even without PostgreSQL COPY TO PROGRAM, and internet-facing FTP services tend to attract automated testing quickly after disclosure.

05 · Compensating Control

What to do — in priority order.

  1. Disable risky SQL logging paths — Remove or rewrite SQLLog ERR_*, SQLLog *, and any SQLNamedQuery that expands attacker-controlled tokens like %U, %u, %r, %f, %F, %J, or %S into SQL. This is the highest-value temporary control and should be deployed within 30 days for a HIGH verdict.
  2. Restrict FTP exposure — Put ProFTPD behind VPN, partner allowlists, or internal-only network boundaries so unauthenticated internet traffic cannot hit the vulnerable USER path. If the service must remain online, reduce source IPs aggressively within 30 days.
  3. Downgrade database privileges — Ensure the ProFTPD database account cannot execute dangerous backend features and cannot write to authentication tables unless absolutely required. This shrinks auth-bypass and PostgreSQL COPY TO PROGRAM impact and should be completed within 30 days.
  4. Separate auth and log backends — If ProFTPD must use SQL, split logging from authentication so a logging injection cannot directly plant login-capable accounts. That architectural mitigation is slower than a config change, but it is still a valid risk reducer within 30 days for exposed high-value hosts.
  5. Disable mod_sql where unused — Many estates carry optional modules they no longer need. If mod_sql is not required for business function, remove or stop loading it and collapse the attack surface within 30 days.
What doesn't work
  • A WAF does not meaningfully help on raw FTP control traffic unless you have a protocol-aware FTP security gateway inline.
  • Hiding or changing the FTP banner does not remove the vulnerable logging path; it only makes coarse fingerprinting slightly harder.
  • MFA for admins does not stop the unauthenticated USER-logging path, and it does nothing for SQL injection into the daemon's backend.
06 · Verification

Crowdsourced verification payload.

Run this on the target ProFTPD host as root or a user that can read ProFTPD config files. Save as check-cve-2026-42167.sh, then run sudo bash check-cve-2026-42167.sh /etc/proftpd/proftpd.conf; if no argument is supplied, it will inspect common config paths. It checks upstream version, whether mod_sql is loaded, and whether risky SQLLog/SQLNamedQuery patterns appear; because includes and distro packaging vary, ambiguous results return UNKNOWN.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check-cve-2026-42167.sh
# Detect likely exposure to CVE-2026-42167 on a ProFTPD host.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -u

TARGET_CONF="${1:-}"
TMPFILE="$(mktemp 2>/dev/null || echo /tmp/proftpd-cve-2026-42167.$$)"
trap 'rm -f "$TMPFILE"' EXIT

say() { printf '%s\n' "$*"; }
unknown() { say "UNKNOWN: $*"; exit 2; }
patched() { say "PATCHED: $*"; exit 0; }
vuln() { say "VULNERABLE: $*"; exit 1; }

have_cmd() { command -v "$1" >/dev/null 2>&1; }

ver_lt() {
  # returns 0 if $1 < $2 using sort -V
  [ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" != "$2" ] && [ "$1" != "$2" ]
}

collect_confs() {
  if [ -n "$TARGET_CONF" ] && [ -f "$TARGET_CONF" ]; then
    printf '%s\n' "$TARGET_CONF"
    return 0
  fi

  for p in \
    /etc/proftpd/proftpd.conf \
    /etc/proftpd.conf \
    /usr/local/etc/proftpd.conf; do
    [ -f "$p" ] && printf '%s\n' "$p"
  done
}

expand_includes() {
  local seed="$1"
  awk '
    BEGIN { print FILENAME }
    /^[[:space:]]*Include[[:space:]]+/ {
      for (i=2; i<=NF; i++) print $i
    }
  ' "$seed" 2>/dev/null | while read -r item; do
    case "$item" in
      *"*"*|*"?"*|*"["*)
        for f in $item; do [ -f "$f" ] && printf '%s\n' "$f"; done
        ;;
      *)
        [ -f "$item" ] && printf '%s\n' "$item"
        ;;
    esac
  done
}

get_version() {
  local v=""
  if have_cmd proftpd; then
    v="$(proftpd -v 2>/dev/null | sed -n 's/.*Version \([0-9][^ ]*\).*/\1/p' | head -n1)"
    [ -z "$v" ] && v="$(proftpd -vv 2>/dev/null | sed -n 's/.*Version \([0-9][^ ]*\).*/\1/p' | head -n1)"
  fi
  printf '%s' "$v"
}

VERSION="$(get_version)"
[ -z "$VERSION" ] && unknown "Unable to determine ProFTPD version via proftpd -v"

CONF_LIST="$(collect_confs)"
[ -z "$CONF_LIST" ] && unknown "Could not find a ProFTPD configuration file"

: > "$TMPFILE"
for c in $CONF_LIST; do
  [ -f "$c" ] && printf '%s\n' "$c" >> "$TMPFILE"
  expand_includes "$c" >> "$TMPFILE"
done
sort -u "$TMPFILE" -o "$TMPFILE"

[ ! -s "$TMPFILE" ] && unknown "No readable configuration files found"

if ! ver_lt "$VERSION" "1.3.9a"; then
  patched "Upstream version $VERSION is not older than 1.3.9a; verify distro backport status separately if your package versioning differs"
fi

if ! grep -Eisq '^[[:space:]]*(LoadModule[[:space:]]+mod_sql|Include[[:space:]].*mod_sql|ModulePath|SQLAuthTypes|SQLEngine[[:space:]]+on|SQLBackend)' "$TMPFILE"; then
  patched "Version $VERSION is old, but no obvious mod_sql usage was found in the inspected configs"
fi

RISKY_TOKENS='%U|%u|%r|%f|%F|%J|%S|%A|%D|%d|%l'
HAS_SQLLOG_WIDE=0
HAS_RISKY_QUERY=0
HAS_POSTGRES=0
HAS_SQLITE=0
HAS_MYSQL=0

if grep -Eisq '^[[:space:]]*SQLLog[[:space:]]+([*]|ERR_\*|ALL)' "$TMPFILE"; then
  HAS_SQLLOG_WIDE=1
fi

if grep -Eisq "^[[:space:]]*SQLNamedQuery[[:space:]].*(${RISKY_TOKENS})" "$TMPFILE"; then
  HAS_RISKY_QUERY=1
fi

if grep -Eisq '^[[:space:]]*SQLBackend[[:space:]]+postgres' "$TMPFILE"; then HAS_POSTGRES=1; fi
if grep -Eisq '^[[:space:]]*SQLBackend[[:space:]]+sqlite' "$TMPFILE"; then HAS_SQLITE=1; fi
if grep -Eisq '^[[:space:]]*SQLBackend[[:space:]]+mysql' "$TMPFILE"; then HAS_MYSQL=1; fi

DETAILS="version=$VERSION mod_sql=present"
[ "$HAS_SQLLOG_WIDE" -eq 1 ] && DETAILS="$DETAILS sqllog=wide" || DETAILS="$DETAILS sqllog=not-wide"
[ "$HAS_RISKY_QUERY" -eq 1 ] && DETAILS="$DETAILS namedquery=risky" || DETAILS="$DETAILS namedquery=no-obvious-risky-token"
[ "$HAS_POSTGRES" -eq 1 ] && DETAILS="$DETAILS backend=postgres"
[ "$HAS_SQLITE" -eq 1 ] && DETAILS="$DETAILS backend=sqlite"
[ "$HAS_MYSQL" -eq 1 ] && DETAILS="$DETAILS backend=mysql"

if [ "$HAS_SQLLOG_WIDE" -eq 1 ] && [ "$HAS_RISKY_QUERY" -eq 1 ]; then
  vuln "$DETAILS"
fi

if [ "$HAS_RISKY_QUERY" -eq 1 ]; then
  unknown "$DETAILS; risky SQLNamedQuery found, but pre-auth reachability depends on which commands are bound to SQLLog"
fi

unknown "$DETAILS; old version with mod_sql present, but no decisive vulnerable pattern found in inspected configs"
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning, treat this as a targeted HIGH: inventory every ProFTPD host, then immediately separate the tiny set running mod_sql from the much smaller set using SQL logging with attacker-controlled substitutions. For anything internet-facing in that second bucket, apply compensating controls from the noisgate mitigation SLA within 30 days by removing risky SQLLog/SQLNamedQuery paths or restricting exposure, and complete the actual upgrade to 1.3.9a or the relevant distro backport under the noisgate remediation SLA within 180 days.

Sources

  1. NVD CVE record
  2. oss-security discussion
  3. Upstream issue #2052
  4. Upstream fix commit
  5. ProFTPD 1.3.10rc1 release announcement
  6. Public PoC repository
  7. VulnCheck exposure and exploit note
  8. Debian backport tracking
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.