This is a booby-trapped side door, not the front gate
CVE-2026-48842 is a pre-authentication SQL injection in Roundcube Webmail's virtuser_query plugin, fixed in 1.6.16 and 1.7.1. The bug sits in the plugin's username/email lookup path, where user-controlled login data is substituted into administrator-defined SQL templates using preg_replace(); the fix swaps that logic to str_replace(). The vulnerable population is not all Roundcube: it is Roundcube 1.6.x < 1.6.16 and 1.7.0 with virtuser_query explicitly enabled.
The vendor's HIGH 8.1 score is fair in a lab because the flaw is pre-auth, remote, and can hit a backend SQL store before login. In production, though, the real-world population is much narrower: virtuser_query is a bundled but not automatically loaded plugin, typically used only in DB-backed virtual-user mail setups. That plugin gate is the decisive friction point, so this lands lower for enterprise patch prioritization than the raw CVSS suggests.
4 steps from start to impact.
Find exposed Roundcube that actually uses the plugin
virtuser_query login-mapping plugin. Exposure of Roundcube itself is common; exposure of this specific plugin path is not externally obvious and usually requires either error behavior, config leakage, or prior environmental knowledge.- Roundcube is reachable from the internet or attacker-accessible network
- Instance runs
1.6.x < 1.6.16or1.7.0 virtuser_queryis enabled inconfig/config.inc.php
virtuser_queryis a bundled plugin but not loaded until enabled- Version fingerprinting does not prove the vulnerable plugin is active
- Many enterprises do not use DB-backed virtual-user mapping in Roundcube at all
Reach the pre-auth lookup path
%u or %m placeholders.- Login flow invokes
virtuser_querylookups for the submitted identifier - Webmail accepts direct access to the login endpoint
- Some deployments use SSO, reverse proxies, or VPN gating that limit anonymous reachability
- Not every login workflow exercises the vulnerable lookup functions the same way
Bypass the escaping assumption
sqlmap-style tamper logic to abuse the preg_replace() replacement behavior with backslashes, defeating the developer's assumption that database escaping alone was sufficient. This is why the CVSS vector carries AC:H: exploitation is not just 'append quote, win' and depends on the exact query template and database behavior.- Attacker can craft identifiers that survive web, PHP, and DB parsing layers
- Administrator-defined SQL template is injectable in practice
- Exploitability depends on the site-specific SQL template configured by the admin
- Backslash handling and query semantics vary across DB engines and templates
- No reputable public PoC was located in primary sources during this assessment
Turn SQL control into useful impact
- Injection is viable against the configured query
- Database account used by Roundcube has meaningful read or write privileges
- Least-privilege DB accounts can significantly limit blast radius
- Some deployments separate Roundcube metadata from mailbox identity stores
- Turning SQLi into full host compromise is not inherent to this CVE
The supporting signals.
| In-the-wild status | No evidence of active exploitation found in the Roundcube advisory, NVD references, or CISA KEV at assessment time. |
|---|---|
| KEV status | Not listed in CISA KEV; the CISA Roundcube search page shows older Roundcube entries but not CVE-2026-48842. |
| PoC availability | No reputable public PoC or Exploit-DB entry was located in primary sources. The exploit path is understandable from the patch diff, but public weaponization was not confirmed. |
| EPSS | 0.00076 from FIRST-sourced telemetry in the user intel; that is very low expected near-term exploitation probability. |
| CVSS interpretation | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H correctly reflects *remote pre-auth* reachability, but the high attack complexity matters here because exploitability depends on plugin state and query-template specifics. |
| Affected versions | Roundcube Webmail 1.6.x before 1.6.16 and 1.7.0 before 1.7.1, only when virtuser_query is enabled. |
| Fixed versions | Upstream fixed in 1.6.16 and 1.7.1. Debian also backported fixes to bullseye 1.4.15+dfsg.1-1+deb11u9, bookworm 1.6.5+dfsg-1+deb12u9, and trixie 1.6.16+dfsg-0+deb13u1. |
| Exposure reality | Roundcube as a product is broadly exposed on the internet; Censys reported 2,473,116 exposed Roundcube instances in a 2025 advisory. That amplifies attention, but it does not mean those hosts use virtuser_query. |
| Disclosure timeline | Upstream advisory published 2026-05-24; CVE/NVD record published 2026-05-25. |
| Reporter | Roundcube credits the issue to researcher skull in the upstream security advisory. |
noisgate verdict.
The decisive factor is plugin gating: this is not a bug in default Roundcube login flow, it is a bug in a bundled plugin that admins must explicitly enable for DB-backed virtual-user lookups. That sharply reduces the reachable population, so despite the ugly pre-auth SQLi label, this is not a fleet-wide fire drill unless you know you run virtuser_query.
Why this verdict
- Down from vendor HIGH for plugin gating:
virtuser_queryis bundled, but Roundcube's own plugin docs state a plugin is not used until added to thepluginsconfig array. That turns 'all exposed Roundcube' into a much smaller subset. - Down again for compounded prerequisites: attacker needs anonymous network reachability, a vulnerable Roundcube build, the specific plugin enabled, and a site-specific SQL template that is exploitable through the backslash/preg-replace behavior. Each prerequisite narrows population and raises operational friction.
- Held at MEDIUM because the entry point is still pre-auth remote: if you do use this plugin on an internet-facing host, the attacker hits SQL before login and can potentially extract or manipulate mail-user data without credentials.
Why not higher?
This is not higher because the vulnerable code is behind a non-default plugin and a specific deployment model. There is also no KEV listing, no confirmed active exploitation, and no reputable public PoC located during the assessment, so there is no external signal that attackers are mass-operationalizing it right now.
Why not lower?
This is not lower because the bug is still pre-authentication and network-reachable on affected systems. For organizations that do run virtuser_query on exposed webmail, the blast radius can include sensitive backend mail-user data and authentication-adjacent mappings, which is too much impact to relegate to backlog-only hygiene.
What to do — in priority order.
- Disable
virtuser_querywhere unused — If business logic does not require DB-backed virtual-user lookups, removevirtuser_queryfrom the Roundcubepluginsarray. For aMEDIUMverdict there is no noisgate mitigation SLA; do this in the next normal change window and then patch within the remediation window. - Restrict anonymous reachability to webmail — Put Roundcube behind VPN, SSO access control, IP allowlists, or reverse-proxy authentication where practical. This does not fix the bug, but it removes the unauthenticated internet path and is worth doing during routine hardening since there is no mitigation SLA for MEDIUM.
- Review DB account privileges — Check the database account used by Roundcube and the mail-user lookup path; keep it read-only where possible and scoped to only required tables. That reduces impact if SQLi is achieved, and it should be folded into normal hardening before the
365-dayremediation deadline. - Increase telemetry on login anomalies — Log and alert on unusual pre-auth login identifiers, repeated failures with escaping characters, and DB slow-query spikes against lookup tables. This is detective control only, but it gives you a chance to spot exploit development while you work through normal patch cycles.
- A generic version-only scan does not tell you whether
virtuser_queryis enabled, so it will inflate exposure counts and create false urgency. - EDR on the Linux host does not meaningfully stop a pre-auth SQL parser bug by itself; the relevant choke points are web access controls, plugin state, and DB privilege scope.
- Relying on a WAF alone is weak here because the issue involves backslash handling and application-specific substitution behavior, not just obvious
' OR 1=1 --payloads.
Crowdsourced verification payload.
Run this on the Roundcube host as a user who can read the Roundcube install directory and config/config.inc.php—root is helpful but not strictly required. Invoke it with the install root, for example: bash verify_cve_2026_48842.sh /var/www/roundcube; it checks version evidence plus whether virtuser_query is enabled and prints VULNERABLE, PATCHED, or UNKNOWN.
#!/usr/bin/env bash
# verify_cve_2026_48842.sh
# Check Roundcube CVE-2026-48842 exposure.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
TARGET="${1:-}"
find_root() {
local candidates=()
if [[ -n "$TARGET" ]]; then
candidates+=("$TARGET")
fi
candidates+=(
"/var/www/roundcube"
"/var/lib/roundcube"
"/usr/share/roundcube"
"/usr/share/roundcubemail"
"/opt/roundcube"
"/srv/roundcube"
)
for d in "${candidates[@]}"; do
[[ -d "$d" ]] || continue
if [[ -f "$d/index.php" && -d "$d/plugins" ]]; then
echo "$d"
return 0
fi
done
return 1
}
version_ge() {
# returns 0 if $1 >= $2
if command -v dpkg >/dev/null 2>&1; then
dpkg --compare-versions "$1" ge "$2"
return $?
fi
[[ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -n1)" == "$2" ]]
}
version_lt() {
# returns 0 if $1 < $2
if command -v dpkg >/dev/null 2>&1; then
dpkg --compare-versions "$1" lt "$2"
return $?
fi
[[ "$1" != "$2" ]] && [[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" == "$1" ]]
}
extract_version() {
local root="$1"
local v=""
# Debian/RPM package versions first
if command -v dpkg-query >/dev/null 2>&1; then
v="$(dpkg-query -W -f='${Version}' roundcube 2>/dev/null || true)"
if [[ -n "$v" ]]; then
echo "$v"
return 0
fi
v="$(dpkg-query -W -f='${Version}' roundcube-core 2>/dev/null || true)"
if [[ -n "$v" ]]; then
echo "$v"
return 0
fi
fi
if command -v rpm >/dev/null 2>&1; then
v="$(rpm -q --qf '%{VERSION}-%{RELEASE}' roundcubemail 2>/dev/null || true)"
if [[ -n "$v" && "$v" != *"not installed"* ]]; then
echo "$v"
return 0
fi
fi
# Source installs: try common metadata files
if [[ -f "$root/composer.json" ]]; then
v="$(grep -Eo '"version"[[:space:]]*:[[:space:]]*"[^"]+"' "$root/composer.json" 2>/dev/null | head -n1 | sed -E 's/.*"([0-9][^"]*)"/\1/')"
if [[ -n "$v" ]]; then
echo "$v"
return 0
fi
fi
if [[ -f "$root/program/include/iniset.php" ]]; then
v="$(grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' "$root/program/include/iniset.php" 2>/dev/null | head -n1)"
if [[ -n "$v" ]]; then
echo "$v"
return 0
fi
fi
if [[ -f "$root/CHANGELOG.md" ]]; then
v="$(grep -Eo 'Release[[:space:]]+[0-9]+\.[0-9]+\.[0-9]+' "$root/CHANGELOG.md" 2>/dev/null | head -n1 | awk '{print $2}')"
if [[ -n "$v" ]]; then
echo "$v"
return 0
fi
fi
return 1
}
plugin_enabled() {
local root="$1"
local cfg="$root/config/config.inc.php"
[[ -f "$cfg" ]] || return 2
# Best-effort parse: strip comments, then look for virtuser_query token.
local content
content="$(sed -E 's://.*$::; s:#.*$::' "$cfg" 2>/dev/null | tr '\n' ' ')"
if echo "$content" | grep -Eq "plugins[^;]*virtuser_query"; then
return 0
fi
return 1
}
ROOT="$(find_root || true)"
if [[ -z "$ROOT" ]]; then
echo "UNKNOWN - Roundcube root not found"
exit 2
fi
VER="$(extract_version "$ROOT" || true)"
if [[ -z "$VER" ]]; then
echo "UNKNOWN - Could not determine Roundcube version under $ROOT"
exit 2
fi
plugin_enabled "$ROOT"
PLUGIN_RC=$?
if [[ $PLUGIN_RC -eq 2 ]]; then
echo "UNKNOWN - Config file missing at $ROOT/config/config.inc.php"
exit 2
fi
if [[ $PLUGIN_RC -ne 0 ]]; then
echo "PATCHED - virtuser_query not enabled in $ROOT/config/config.inc.php (CVE-2026-48842 path not active)"
exit 0
fi
# Normalize upstream version prefix if package release suffixes exist.
UPSTREAM="$(echo "$VER" | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)"
if [[ -z "$UPSTREAM" ]]; then
echo "UNKNOWN - Parsed package version '$VER' but no upstream semantic version found"
exit 2
fi
# Vulnerable logic:
# 1.6.x before 1.6.16
# 1.7.x before 1.7.1 (practically 1.7.0)
if [[ "$UPSTREAM" =~ ^1\.6\.[0-9]+$ ]]; then
if version_lt "$UPSTREAM" "1.6.16"; then
echo "VULNERABLE - Roundcube $VER with virtuser_query enabled"
exit 1
else
echo "PATCHED - Roundcube $VER with virtuser_query enabled, but >= 1.6.16"
exit 0
fi
elif [[ "$UPSTREAM" =~ ^1\.7\.[0-9]+$ ]]; then
if version_lt "$UPSTREAM" "1.7.1"; then
echo "VULNERABLE - Roundcube $VER with virtuser_query enabled"
exit 1
else
echo "PATCHED - Roundcube $VER with virtuser_query enabled, but >= 1.7.1"
exit 0
fi
else
echo "PATCHED - Roundcube $VER is outside the stated affected upstream branches, though plugin state was checked"
exit 0
fi
If you remember one thing.
virtuser_query enabled, because that is the real exposure divider. For this MEDIUM verdict there is no noisgate mitigation SLA — go straight to the 365-day remediation window for the vendor fix, but in practice you should patch plugin-enabled, internet-facing Roundcube during the next routine webmail maintenance cycle and clean up plugin inventory now; per the noisgate remediation SLA, complete the actual upgrade/backport within <= 365 days.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.