CVE-2026-48846
Description
In Roundcube Webmail 1.6.x before 1.6.16 and 1.7.x before 1.7.1, the remote image blocking feature can be bypassed via a crafted CSS var() value in an e-mail message, which may lead to information disclosure or access-control bypass.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In Roundcube Webmail 1.6.x before 1.6.16 and 1.7.x before 1.7.1, remote image blocking can be bypassed via a crafted CSS var() value in an email, potentially leading to information disclosure.
Vulnerability
In Roundcube Webmail versions 1.6.x before 1.6.16 and 1.7.x before 1.7.1, the remote image blocking feature is bypassed via a crafted CSS var() value in an email message [1][2][4]. The HTML sanitizer fails to properly filter var() CSS functions that include external URLs, allowing an attacker to embed a tracking image that the browser will load despite the blocking feature [3]. This vulnerability was reported by Geame [1][2].
Exploitation
An attacker can send a specially crafted HTML email containing a CSS style that uses the var() function with an external URL, such as background-image: var(--x, url(http://evil.com/1.gif)) [3]. The victim must open the email in Roundcube Webmail. No authentication or user interaction beyond opening the email is required, as the bypass occurs during HTML sanitization [4]. The test cases in the fix commit demonstrate multiple variations of the bypass [3].
Impact
Successful exploitation allows an attacker to bypass the remote image blocking feature, causing the victim's email client to make a request to an external server controlled by the attacker [1][2]. This can lead to information disclosure, such as the victim's IP address, and can be used for tracking or phishing attacks. The vulnerability does not directly allow code execution or privilege escalation, but it compromises privacy and access controls [4].
Mitigation
The issue is fixed in Roundcube Webmail versions 1.6.16 and 1.7.1, released on 24 May 2026 [1][2][4]. All productive installations of Roundcube 1.6.x and 1.7.x should be updated to these versions. No workaround is available for unpatched versions. The vulnerability is not known to be listed on CISA's Known Exploited Vulnerabilities (KEV) catalog as of the publication date.
AI Insight generated on May 25, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
259cca80908a6Fix bypass of remote image blocking via CSS var()
4 files changed · +24 −5
CHANGELOG.md+1 −0 modified@@ -18,6 +18,7 @@ This file includes only changes we consider noteworthy for users, admins and plu - Security: Fix CSS injection bypass in HTML sanitizer via SVG `<animate attributeName="style">` - Security: Fix pre-auth SQL injection in `virtuser_query` plugin via preg_replace backslash escape bypass - Security: Fix SSRF bypass via specific local address URLs +- Security: Fix bypass of remote image blocking via CSS var() ## Release 1.7.0
program/lib/Roundcube/rcube_utils.php+11 −4 modified@@ -641,10 +641,17 @@ public static function sanitize_css_block($styles, $url_callback = null) } else { $value = ''; foreach (self::explode_css_property_block($rule[1]) as $val) { - if ($url_callback && preg_match('/^url\s*\(/i', $val)) { - if (preg_match('/^url\s*\(\s*[\'"]?([^\'"\)]*)[\'"]?\s*\)/iu', $val, $match)) { - if ($url = $url_callback($match[1])) { - $value .= ' url(' . $url . ')'; + if ($url_callback && preg_match('/\burl\s*\(/i', $val)) { + if (preg_match_all('/(\b)url\s*\(\s*[\'"]?([^\'"\)]*)[\'"]?\s*\)/iu', $val, $matches)) { + foreach ($matches[2] as $idx => $url) { + if ($url = $url_callback($url)) { + $val = str_replace($matches[0][$idx], $matches[1][$idx] . "url({$url})", $val); + } else { + $val = ''; + } + } + if (strlen($val)) { + $value .= ' ' . $val; } } } elseif (preg_match('/;.+/', $val)) {
tests/Framework/UtilsTest.php+5 −0 modified@@ -359,6 +359,11 @@ public function test_mod_css_styles_xss() $mod = \rcube_utils::mod_css_styles("p { background: url('//data:image&leak'); }", 'rcmbody'); $this->assertSame('#rcmbody p {}', $mod); + $mod = \rcube_utils::mod_css_styles("p { background-image: var(--x, url(http://evil.com/1.gif)) }", 'rcmbody'); + $this->assertSame('#rcmbody p {}', $mod); + $mod = \rcube_utils::mod_css_styles("p { background-image: var(--x, url(http://evil.com/1.gif)) }", 'rcmbody', true); + $this->assertSame('#rcmbody p { background-image: var(--x, url(http://evil.com/1.gif)); }', $mod); + // Note: This looks to me like a bug in browsers, for now we don't allow image-set at all $mod = \rcube_utils::mod_css_styles("p { background: image-set('//evil.com/img.png' 1x); }", 'rcmbody'); $this->assertSame('#rcmbody p {}', $mod);
tests/Framework/WashtmlTest.php+7 −1 modified@@ -719,13 +719,19 @@ public function test_extlinks() ['<img src="http://TRACKING_URL/">', true], ['<img src="data:image">', false], ['<p style="backgr\ound-image: \ur\l(\'http://TRACKING_URL\')"></p>', true], + ['<p style="background-image: var(--x, url(http://evil.com/1.gif))"></p>', true], + ['<p style="cursor: var(--x, url(http://evil.com/5.gif), auto)"></p>', true], + ['<p style="background: var(--a, var(--b, url(http://evil.com/6.gif)))"></p>', true], + ['<p style="color: red; background-image: var(--x, url(http://evil.com/7.gif)); font-size: 12px"></p>', true], + ['<p style="background: image(url(http://evil.com/8.gif))"></p>', true], + ['<p style="background: cross-fade(url(http://evil.com/9a.gif), url(http://evil.com/9b.gif), 50%)"></p>', true], ]; foreach ($html as $item) { $washer = new \rcube_washtml(); $washed = $washer->wash($item[0]); - $this->assertSame($item[1], $washer->extlinks); + $this->assertSame($item[1], $washer->extlinks, "Failed on: {$item[0]}"); } foreach ($html as $item) {
852350486b88Fix bypass of remote image blocking via CSS var()
4 files changed · +29 −10
CHANGELOG.md+1 −0 modified@@ -7,6 +7,7 @@ - Security: Fix CSS injection bypass in HTML sanitizer via SVG `<animate attributeName="style">` - Security: Fix pre-auth SQL injection in `virtuser_query` plugin via preg_replace backslash escape bypass - Security: Fix SSRF bypass via specific local address URLs +- Security: Fix bypass of remote image blocking via CSS var() ## Release 1.6.15
program/lib/Roundcube/rcube_utils.php+11 −4 modified@@ -627,10 +627,17 @@ public static function sanitize_css_block($styles, $url_callback = null) } else { $value = ''; foreach (self::explode_css_property_block($rule[1]) as $val) { - if ($url_callback && preg_match('/^url\s*\(/i', $val)) { - if (preg_match('/^url\s*\(\s*[\'"]?([^\'"\)]*)[\'"]?\s*\)/iu', $val, $match)) { - if ($url = $url_callback($match[1])) { - $value .= ' url(' . $url . ')'; + if ($url_callback && preg_match('/\burl\s*\(/i', $val)) { + if (preg_match_all('/(\b)url\s*\(\s*[\'"]?([^\'"\)]*)[\'"]?\s*\)/iu', $val, $matches)) { + foreach ($matches[2] as $idx => $url) { + if ($url = $url_callback($url)) { + $val = str_replace($matches[0][$idx], $matches[1][$idx] . "url({$url})", $val); + } else { + $val = ''; + } + } + if (strlen($val)) { + $value .= ' ' . $val; } } } elseif (preg_match('/;.+/', $val)) {
tests/Framework/Utils.php+5 −0 modified@@ -253,6 +253,11 @@ function test_mod_css_styles_xss() $mod = \rcube_utils::mod_css_styles("p { background: url('//data:image&leak'); }", 'rcmbody'); $this->assertSame('#rcmbody p {}', $mod); + $mod = \rcube_utils::mod_css_styles("p { background-image: var(--x, url(http://evil.com/1.gif)) }", 'rcmbody'); + $this->assertSame('#rcmbody p {}', $mod); + $mod = \rcube_utils::mod_css_styles("p { background-image: var(--x, url(http://evil.com/1.gif)) }", 'rcmbody', true); + $this->assertSame('#rcmbody p { background-image: var(--x, url(http://evil.com/1.gif)); }', $mod); + // Note: This looks to me like a bug in browsers, for now we don't allow image-set at all $mod = \rcube_utils::mod_css_styles("p { background: image-set('//evil.com/img.png' 1x); }", 'rcmbody'); $this->assertSame('#rcmbody p {}', $mod);
tests/Framework/Washtml.php+12 −6 modified@@ -709,18 +709,24 @@ function test_src_wash() function test_extlinks() { $html = [ - ["<link href=\"http://TRACKING_URL/\">", true], - ["<link href=\"src:abc\">", false], - ["<img src=\"http://TRACKING_URL/\">", true], - ["<img src=\"data:image\">", false], - ['<p style="backgr\\ound-image: \\ur\\l(\'http://TRACKING_URL\')"></p>', true], + ['<link href="http://TRACKING_URL/">', true], + ['<link href="src:abc">', false], + ['<img src="http://TRACKING_URL/">', true], + ['<img src="data:image">', false], + ['<p style="backgr\ound-image: \ur\l(\'http://TRACKING_URL\')"></p>', true], + ['<p style="background-image: var(--x, url(http://evil.com/1.gif))"></p>', true], + ['<p style="cursor: var(--x, url(http://evil.com/5.gif), auto)"></p>', true], + ['<p style="background: var(--a, var(--b, url(http://evil.com/6.gif)))"></p>', true], + ['<p style="color: red; background-image: var(--x, url(http://evil.com/7.gif)); font-size: 12px"></p>', true], + ['<p style="background: image(url(http://evil.com/8.gif))"></p>', true], + ['<p style="background: cross-fade(url(http://evil.com/9a.gif), url(http://evil.com/9b.gif), 50%)"></p>', true], ]; foreach ($html as $item) { $washer = new rcube_washtml; $washed = $washer->wash($item[0]); - $this->assertSame($item[1], $washer->extlinks); + $this->assertSame($item[1], $washer->extlinks, "Failed on: {$item[0]}"); } foreach ($html as $item) {
Vulnerability mechanics
Root cause
"Insufficient CSS value parsing: the regex anchor `^` prevented detection of `url()` when it appeared inside a `var()` wrapper."
Attack vector
An attacker sends a specially crafted HTML e-mail containing an inline `style` attribute that uses the CSS `var()` function with a fallback URL, e.g. `background-image: var(--x, url(http://evil.com/1.gif))`. Because the old regex required the value to start with `url(`, the `var()` wrapper caused the entire value to bypass the remote-image-blocking check. When the recipient views the message, the browser resolves the `var()` fallback and loads the attacker-controlled URL, leaking the recipient's IP address and potentially bypassing access controls that rely on blocking external image loads.
Affected code
The vulnerability resides in `program/lib/Roundcube/rcube_utils.php` within the `sanitize_css_block()` method. The original regex `preg_match('/^url\s*\(/i', $val)` only matched CSS values that started with `url(`, so a value like `var(--x, url(http://evil.com/1.gif))` was not recognized as containing a remote URL and was passed through without sanitization [patch_id=2473665][patch_id=2473666].
What the fix does
The patch changes the regex from `preg_match('/^url\s*\(/i', $val)` (anchored at start) to `preg_match('/\burl\s*\(/i', $val)` (word-boundary match), so `url(` is detected anywhere inside the value, including inside `var()`. It also switches from `preg_match` (single match) to `preg_match_all` and iterates over every `url()` found, replacing each matched token individually. If any `url()` is rejected by the callback, the entire value is cleared. This closes the bypass for `var()`, nested `var()`, `image()`, and `cross-fade()` CSS functions [patch_id=2473665][patch_id=2473666].
Preconditions
- inputThe attacker must be able to send an HTML-formatted e-mail to a Roundcube user.
- configThe recipient must view the message in Roundcube with remote image blocking enabled (default).
Generated on May 25, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/roundcube/roundcubemail/commit/59cca80908a61e662c5f81741449e9aeb91e8abemitre
- github.com/roundcube/roundcubemail/commit/852350486b88b35b8544e8a630fad89e99e2150amitre
- github.com/roundcube/roundcubemail/releases/tag/1.6.16mitre
- github.com/roundcube/roundcubemail/releases/tag/1.7.1mitre
- roundcube.net/news/2026/05/24/security-updates-1.6.16-and-1.7.1mitre
News mentions
0No linked articles in our index yet.