← Back to Feed CACHED · 2026-09-25 16:18:24 · CACHE_KEY CVE-2026-48842
CVE-2026-48842 · CWE-89 · Disclosed 2026-05-25

Roundcube Webmail Pre-Authentication SQL Injection via virtuser_query Plugin preg_replace Backslash Bypass

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Someone left a skeleton key in the mail room's optional lock, and burglars just figured out it works

CVE-2026-48842 is a pre-authentication SQL injection in Roundcube Webmail's virtuser_query plugin, affecting versions 1.6.x before 1.6.16 and 1.7.x before 1.7.1. The plugin maps login usernames to email addresses using a configurable SQL template. The vulnerable code used preg_replace('%u', $dbh->escape($user), $query) to substitute user input into the SQL string — but PHP's preg_replace() interprets backslash sequences in the replacement argument, meaning a crafted backslash payload can *undo* the database driver's escaping and inject arbitrary SQL. The injection point is the login form's _user field at ?_task=login, requiring zero authentication. The fix was a one-line change: swap preg_replace() for str_replace(), which does not interpret backslashes.

The vendor's HIGH / 8.1 rating with AC:H is *close to correct* but slightly overstates the population-wide risk. The critical friction is that virtuser_query is not enabled by default — it must be explicitly added to the $config['plugins'] array in config.inc.php. This means vanilla Roundcube installations are not vulnerable. However, the plugin is heavily used by ISPs and shared-hosting providers running multi-domain mail, which are exactly the deployments most likely to be internet-facing. With active exploitation confirmed by Canada's CCCS as of September 21, 2026, a public Python PoC on GitHub, and 523,000+ Roundcube instances on the internet, the vendor's HIGH is directionally right — but the non-default plugin requirement justifies a modest downgrade to 7.5 rather than a full endorsement of 8.1.

"Pre-auth SQLi in an opt-in Roundcube plugin, now actively exploited — patch or disable immediately."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Identify exposed Roundcube instance

The attacker scans for internet-facing Roundcube installations. Shodan/Censys/FOFA queries and Shadowserver data reveal 523,000+ exposed instances. The attacker fingerprints the version from the login page's HTML (roundcube_version meta tag, CSS/JS asset paths, or the _token generation pattern). The murrez/CVE-2026-48842 PoC automates this with python poc.py -u <target>.
Conditions required:
  • Target Roundcube is internet-facing
  • Attacker can reach the login page
Where this breaks in practice:
  • Version fingerprinting is not always reliable if admins strip version info
Detection/coverage: Shadowserver tracks exposed instances. WAF rules can detect automated scanning patterns.
STEP 02

Probe for virtuser_query plugin

The attacker sends a crafted login request with a diagnostic _user value to determine whether the virtuser_query plugin is loaded. The PoC's --verify flag sends backslash-laden usernames and analyzes response timing and error behavior to confirm the plugin is active. If the plugin is not enabled, the attack chain stops here.
Conditions required:
  • virtuser_query plugin must be explicitly enabled in config.inc.php
  • Plugin must be configured with a SQL query template containing %u
Where this breaks in practice:
  • Plugin is not enabled by default — only deployments with multi-domain virtual user lookups (ISPs, shared hosting) enable it
  • No public data on what fraction of 523K instances have this plugin active — likely a minority
Detection/coverage: Login endpoint anomaly detection (repeated failed logins with unusual _user patterns). IDS signatures for backslash-heavy POST payloads to ?_task=login.
STEP 03

Exploit preg_replace backslash bypass for SQL injection

The attacker submits a _user value containing carefully crafted backslash sequences. When preg_replace() processes the replacement string, PHP re-interprets the backslashes, stripping the escaping applied by $dbh->escape(). This allows the attacker to break out of the SQL string context and inject arbitrary SQL. The injection is blind (no direct output) but can be exploited via time-based or error-based techniques. The murrez PoC and Proofpoint-documented UNK_MassTraction campaign both weaponize this step.
Conditions required:
  • Vulnerable Roundcube version (< 1.6.16 or < 1.7.1)
  • virtuser_query active (from step 2)
Where this breaks in practice:
  • CVSS rates AC:H — the backslash bypass requires precise payload crafting depending on the database backend (MySQL vs PostgreSQL vs SQLite)
  • WAF or mod_security rules may block the payloads
Detection/coverage: SQL injection signatures in WAF/IDS. Database query logging for anomalous statements from the Roundcube application user.
STEP 04

Exfiltrate credentials and email data

With SQL injection established, the attacker dumps user credentials from Roundcube's users table, session tokens from session, and potentially pivots to the mail server's virtual user tables if Roundcube's database user has cross-schema access. Extracted credentials enable mailbox access via IMAP/SMTP directly, bypassing Roundcube entirely. In espionage campaigns (UNK_MassTraction), the goal is harvesting email contents from high-value targets.
Conditions required:
  • Successful SQL injection from step 3
  • Roundcube DB user has read access to relevant tables
Where this breaks in practice:
  • Database permissions may limit lateral movement beyond Roundcube's own schema
  • Credential hashing (if passwords are stored hashed) slows credential reuse
Detection/coverage: Database audit logging for bulk SELECT operations. SIEM correlation of unusual IMAP logins following webmail access patterns.
STEP 05

Potential RCE via database write primitives

Depending on the database backend and permissions, the attacker may escalate from SQLi to remote code execution using SELECT INTO OUTFILE (MySQL), COPY TO (PostgreSQL), or UDF injection. This would give shell access on the Roundcube server, enabling web shell deployment (as seen in the UNK_MassTraction VShell campaign) and lateral movement into the mail infrastructure.
Conditions required:
  • Database user has FILE privilege (MySQL) or superuser-level access (PostgreSQL)
  • Writable web directory path is known
Where this breaks in practice:
  • Modern database hardening disables FILE privilege and restricts COPY TO
  • AppArmor/SELinux on the DB process prevents filesystem writes
  • Many Roundcube deployments use restricted DB users without write-to-disk capabilities
Detection/coverage: File integrity monitoring on web root directories. EDR detection of unexpected child processes from the database daemon.
03 · Compensating Control

1
HIGH 7.5→IGNORE 0.0
SEVERITY REDUCED
Disable or remove the virtuser_query plugin immediately — Edit config/config.inc.php and remove 'virtuser_query' from the $config['plugins'] array, then restart Apache/PHP-FPM. This completely eliminates the attack surface regardless of Roundcube version. If virtual user lookups are needed, switch to the virtuser_file plugin or implement lookups at the MTA (Postfix virtual_alias_maps) layer instead. Deploy within hours given active exploitation — this is a zero-downtime change that only affects how login usernames map to email addresses.
2
HIGH 7.5→IGNORE 0.0
SEVERITY REDUCED
Patch to Roundcube 1.6.16 or 1.7.1 — The definitive fix replaces preg_replace() with str_replace() in the plugin code, eliminating the backslash interpretation vulnerability. Per the noisgate mitigation SLA for HIGH with active exploitation, patch immediately (within hours). Test in staging first — the update is a point release with minimal functional changes.
3
HIGH 7.5→MEDIUM 5.5
SEVERITY REDUCED
Deploy WAF rules blocking backslash injection in login POST — Add a WAF rule (ModSecurity, Cloudflare, AWS WAF) that blocks or flags POST requests to ?_task=login where the _user parameter contains backslash sequences (\\, \', \", \0). This is a defense-in-depth measure — not a substitute for patching — but buys time if immediate patching isn't feasible. Deploy within 24 hours as a stopgap. Note: overly aggressive rules may block legitimate usernames containing backslashes (rare but possible).
4
HIGH 7.5→HIGH 6.5
Restrict Roundcube database user privileges — Ensure the MySQL/PostgreSQL user configured in Roundcube's db_dsnw has only SELECT, INSERT, UPDATE, DELETE on Roundcube's own schema — no FILE, SUPER, CREATE, or cross-schema privileges. This limits post-exploitation impact by preventing RCE escalation via INTO OUTFILE or COPY TO. Deploy within the 30-day noisgate mitigation SLA for HIGH.
5
HIGH 7.5→LOW 3.0
SEVERITY REDUCED
Place Roundcube behind authentication gateway or VPN — If Roundcube does not need to be publicly accessible (internal org use), place it behind a reverse proxy with SSO/MFA (e.g., Authelia, Keycloak, Cloudflare Access). This converts the pre-auth attack to a post-auth one, dramatically raising the bar. Deploy within 30 days.
What doesn't work
  • Database-level parameterized queries won't help without code change — the vulnerability is in how PHP's preg_replace() interprets the replacement string *after* escaping. The escaping itself is correct; PHP undoes it. Only changing to str_replace() (the vendor fix) or removing the plugin resolves this.
  • Roundcube's built-in rate limiting does not prevent exploitation — the SQLi succeeds on a single crafted request, not a brute-force attack. Rate limiting only slows scanning, not exploitation.
  • TLS/HTTPS alone provides no protection — the injection is in the application layer POST body, fully within the encrypted channel.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationActive. Canada's CCCS confirmed exploitation on September 21, 2026, four months post-patch. UNK_MassTraction (suspected China-aligned) exploited Roundcube flaws in a July 2026 campaign targeting universities with ties to national security, astrophysics, and particle physics research (Proofpoint).
Proof of ConceptPublic. murrez/CVE-2026-48842 — Python PoC with version detection, plugin probing, and active SQLi verification via --verify flag. Researcher: skull (original reporter to Roundcube).
EPSS Score0.00891 (~89th percentile). Moderate exploitation probability, likely to rise given confirmed in-the-wild activity.
KEV StatusNot listed as of 2026-09-26. Given active exploitation confirmation, KEV addition is plausible in coming weeks.
CVSS VectorCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H — Network-accessible, no auth, no user interaction, but high attack complexity (requires virtuser_query enabled + precise backslash crafting). Full CIA impact on the database.
Affected VersionsRoundcube 1.6.0 – 1.6.15 and 1.7.0. The 1.5.x branch is EOL and *may* be affected if virtuser_query is enabled (unconfirmed by vendor).
Fixed Versions1.6.16 and 1.7.1, released May 24, 2026. Distro backports: check Debian roundcube package changelogs; Ubuntu USN tracking pending.
Internet Exposure523,000+ Roundcube instances exposed per Shadowserver (September 2026). Unknown fraction have virtuser_query enabled — likely concentrated in ISP/shared-hosting deployments.
Disclosure DateMay 25, 2026 (public). Patches released one day prior on May 24.
Reporting Researcherskull — reported directly to Roundcube security team.

Sources.

  1. Roundcube Security Update 1.6.16 / 1.7.1
  2. The Hacker News — Active Exploitation Report
  3. BleepingComputer — Critical Roundcube Flaw Exploited
  4. murrez/CVE-2026-48842 — Python PoC
  5. Roundcube virtuser_query Plugin Source (patched)
  6. Proofpoint — 2026 Vulnerability Exploitation Playbook
  7. SecurityWeek — Roundcube Vulnerability in Attackers' Crosshairs
  8. NVD — CVE-2026-48842 Detail
05 · The Call

Final Verdict
= UNCHANGED to HIGH (7.5/10)

Why this verdict

  • Non-default plugin is the primary friction: virtuser_query must be explicitly enabled in config.inc.php. Vanilla Roundcube installs are *not vulnerable*. This narrows the exploitable population significantly below the 523K exposed instances, justifying a downward adjustment from the vendor's 8.1.
  • Active exploitation overrides theoretical friction: CCCS confirmed in-the-wild exploitation on September 21, 2026. UNK_MassTraction is already targeting Roundcube infrastructure for espionage. The attack is pre-auth and has a public PoC — the complexity barrier is lower than AC:H suggests for a motivated adversary with the murrez tool.
  • Role multiplier: Roundcube occupies a *typical-role* position as a webmail frontend. It is not in the canonical high-value-role catalog (not a DC, hypervisor, IdP, PAM, or network edge). However, shared-hosting ISP deployments with virtuser_query enabled serve hundreds to thousands of mail users per instance — a successful SQLi compromises *all* user credentials on that instance (tenant-level blast radius). For government and academic deployments targeted by UNK_MassTraction, the data sensitivity is espionage-grade. This keeps the floor at HIGH but does not push to CRITICAL.
  • Pre-auth network attack surface: No credentials needed. The injection point is the public login form. Any scanner can reach it. This is the strongest upward pressure and prevents dropping below HIGH despite the plugin prerequisite.
  • Downward adjustment (-0.6): The non-default plugin requirement is a real-world narrowing not fully captured by AC:H. We estimate <30% of exposed Roundcube instances run virtuser_query, reducing effective exposure from 523K to roughly 100-150K instances. This justifies the modest score reduction from 8.1 to 7.5.

Why not higher?

CRITICAL would require either a canonically high-value-role component (Roundcube is a webmail frontend, not infrastructure-defining software) or a default-vulnerable condition affecting the full installed base. The virtuser_query plugin must be explicitly opted into, which caps the blast radius. The attack complexity is genuinely higher than a typical pre-auth SQLi because the backslash bypass requires database-backend-specific payload tuning. No evidence of automated worm-like propagation.

Why not lower?

Active exploitation is confirmed by a national CERT. A public PoC exists with batch-scanning capability. The vulnerability is pre-auth and network-accessible with full CIA impact on the database. UNK_MassTraction demonstrates that nation-state actors are actively targeting Roundcube infrastructure. Dropping to MEDIUM would understate the urgency for the subset of deployments running virtuser_query — for those instances, the risk is immediate and severe.

06 · Verification

Crowdsourced verification payload.

Run this script on each Roundcube server as a user with read access to the Roundcube installation directory (typically www-data or root). Invoke with the Roundcube install path as argument: sudo bash check_cve_2026_48842.sh /var/www/roundcube. No network access or database credentials required.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_48842.sh — Detect CVE-2026-48842 (Roundcube virtuser_query pre-auth SQLi)
# Usage: bash check_cve_2026_48842.sh /path/to/roundcube
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -euo pipefail

RC_DIR="${1:-/var/www/roundcube}"
STATUS="UNKNOWN"

# Locate version file
VERSION_FILE=""
for candidate in "$RC_DIR/program/include/iniset.php" "$RC_DIR/index.php" "$RC_DIR/program/lib/Roundcube/rcube.php"; do
  if [[ -f "$candidate" ]]; then
    VERSION_FILE="$candidate"
    break
  fi
done

if [[ -z "$VERSION_FILE" ]]; then
  echo "[!] Cannot find Roundcube installation at $RC_DIR"
  echo "UNKNOWN"
  exit 2
fi

# Extract version
VERSION=$(grep -oP "define\\('RCMAIL_VERSION',\\s*'\\K[^']+" "$RC_DIR/program/include/iniset.php" 2>/dev/null || \
          grep -oP "'version'\\s*=>\\s*'\\K[^']+" "$RC_DIR/composer.json" 2>/dev/null || \
          echo "")

if [[ -z "$VERSION" ]]; then
  echo "[!] Could not determine Roundcube version"
  echo "UNKNOWN"
  exit 2
fi

echo "[*] Roundcube version: $VERSION"

# Check if virtuser_query plugin is enabled
CONFIG_FILE="$RC_DIR/config/config.inc.php"
PLUGIN_ENABLED=false
if [[ -f "$CONFIG_FILE" ]]; then
  if grep -qP "['\"]virtuser_query['\"]" "$CONFIG_FILE" 2>/dev/null; then
    PLUGIN_ENABLED=true
    echo "[!] virtuser_query plugin is ENABLED in config"
  else
    echo "[*] virtuser_query plugin is NOT enabled in config"
  fi
else
  echo "[!] Config file not found at $CONFIG_FILE"
fi

# Check if the vulnerable preg_replace pattern exists in the plugin
PLUGIN_FILE="$RC_DIR/plugins/virtuser_query/virtuser_query.php"
USES_PREG=false
if [[ -f "$PLUGIN_FILE" ]]; then
  if grep -q 'preg_replace' "$PLUGIN_FILE" 2>/dev/null; then
    USES_PREG=true
    echo "[!] Plugin code uses vulnerable preg_replace() pattern"
  else
    echo "[*] Plugin code uses safe str_replace() pattern (patched)"
  fi
else
  echo "[*] virtuser_query plugin file not found (plugin not installed)"
  echo "PATCHED"
  exit 0
fi

# Version comparison
version_gte() {
  printf '%s\n%s' "$2" "$1" | sort -V -C
}

# Determine vulnerability status
if [[ "$VERSION" == 1.6.* ]]; then
  if version_gte "$VERSION" "1.6.16"; then
    STATUS="PATCHED"
  else
    STATUS="VULNERABLE"
  fi
elif [[ "$VERSION" == 1.7.* ]]; then
  if version_gte "$VERSION" "1.7.1"; then
    STATUS="PATCHED"
  else
    STATUS="VULNERABLE"
  fi
elif [[ "$VERSION" == 1.5.* ]] || [[ "$VERSION" == 1.4.* ]]; then
  STATUS="VULNERABLE"
  echo "[!] EOL version detected — likely vulnerable if plugin is present"
else
  # 1.8+ or unknown
  STATUS="UNKNOWN"
fi

# Override: if plugin uses str_replace (patched code), mark patched regardless
if [[ "$USES_PREG" == false ]]; then
  STATUS="PATCHED"
fi

# Final output
if [[ "$STATUS" == "VULNERABLE" ]]; then
  if [[ "$PLUGIN_ENABLED" == true ]]; then
    echo "[!!!] VULNERABLE — version $VERSION with virtuser_query ENABLED and unpatched preg_replace()"
    echo "VULNERABLE"
    exit 1
  else
    echo "[!] VULNERABLE (code) but plugin not enabled — exploitability requires config change"
    echo "VULNERABLE"
    exit 1
  fi
elif [[ "$STATUS" == "PATCHED" ]]; then
  echo "[*] PATCHED — version $VERSION or plugin code uses safe str_replace()"
  echo "PATCHED"
  exit 0
else
  echo "[?] UNKNOWN — could not conclusively determine status"
  echo "UNKNOWN"
  exit 2
fi