TL;DR
Two vulnerabilities in HestiaCP, when combined, allow an unauthenticated attacker to obtain a root shell on any instance with the web terminal enabled, without leaving traces in any log files. The RCE requires two HTTP requests and the IP spoofing allows the attacker's real address to never appear in authentication logs, fail2ban, or audit trails.
As of the date of this advisory, both fixes have been merged into the main branch but no release has been published. Approximately 200,000 HestiaCP instances are publicly accessible, with an estimated 10–15% running the vulnerable web terminal feature.
If you are running HestiaCP version 1.9.4 or lower, disable the web terminal immediately with the command v-delete-sys-web-terminal and restrict access to port 8083 to trusted IPs at the firewall level. These mitigations should be applied now, do not wait for a release.
| CVE-2026-43633 - Remote Code Execution | CVE-2026-43634 - IP Spoofing | |
|---|---|---|
| CVSSv3 | 10.0 Critical | 7.5 High |
| Affected | 1.9.0 to 1.9.4 | 1.2.0 to 1.9.4 |
Background
HestiaCP is an open source web server control panel forked from VestaCP, used to manage web hosting environments on Debian and Ubuntu systems. It provides a web interface on port 8083 for managing domains, email, databases, DNS, and an interactive web terminal.
CVE-2026-43633: Unauthenticated Remote Code Execution via Session Deserialisation
The Bug
Two components share session files on disk but parse them with fundamentally incompatible parsers. PHP writes attacker-controlled data into the session using its length-prefixed serialisation format. Node.js reads the same file using naive string splitting that has no concept of serialisation boundaries.
On every page load, web/inc/main.php concatenates client-controlled HTTP headers directly into the session without sanitisation:
$user_combined_ip = "";
if (isset($_SERVER["REMOTE_ADDR"])) {
$user_combined_ip = $_SERVER["REMOTE_ADDR"];
}
if (isset($_SERVER["HTTP_X_FORWARDED_FOR"])) {
$user_combined_ip .= "|" . $_SERVER["HTTP_X_FORWARDED_FOR"];
}
if (!isset($_SESSION["user_combined_ip"])) {
$_SESSION["user_combined_ip"] = $user_combined_ip;
}Nginx passes X-Forwarded-For from the client to PHP-FPM without stripping or overriding it.
The Mismatch
PHP's session serialiser writes data in the format key|s:LENGTH:"VALUE"; where VALUE is bounded by a byte-count length prefix. An attacker sending X-Forwarded-For: user|s:4:"root" causes user_combined_ip to contain that payload as literal bytes. PHP handles this correctly because it counts bytes. The resulting session file looks like:
...user_combined_ip|s:27:"172.17.0.1|user|s:4:"root"";look|s:0:"";...The Node.js web terminal extracts the username from the raw session file using plain text operations:
const file = readFileSync(`${process.env.HESTIA}/data/sessions/sess_${sessionID}`);
const session = file.toString();
const login = session.split('user|s:')[1].split('"')[1];
const impersonating = session.split('look|s:')[1].split('"')[1];
const username = impersonating.length > 0 ? impersonating : login;split('user|s:') finds the injected user|s:4:"root" inside the user_combined_ip value, extracts root, and uses it as the username. There is no check that the session is authenticated — the terminal only verifies that the session file exists and that a username can be extracted.
CVE-2026-43634: IP Address Spoofing via CF-Connecting-IP
The Bug
HestiaCP's login handler and session tracking trust the CF-Connecting-IP HTTP header without verifying that the request originated from Cloudflare's network. The only validation is a syntax check via FILTER_VALIDATE_IP:
$ip = $_SERVER["REMOTE_ADDR"];
if (
!empty($_SERVER["HTTP_CF_CONNECTING_IP"]) &&
filter_var(
$_SERVER["HTTP_CF_CONNECTING_IP"],
FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6,
)
) {
$ip = $_SERVER["HTTP_CF_CONNECTING_IP"];
}Since nginx passes all client headers to PHP-FPM, any client connecting directly to port 8083 can set this header to any IP address. The same pattern exists in web/inc/main.php, which runs on every page load and replaces the combined IP entirely.
On authentication failure, v-check-user-hash logs the spoofed IP to the auth log. Fail2ban watches this log and bans IPs with 5 failures. However, v-add-firewall-ban explicitly refuses to ban 127.0.0.1 or the server's own addresses.
Immediate Mitigations
For the RCE: Disable the web terminal
v-delete-sys-web-terminalFor the IP spoofing: Restrict access to port 8083 to trusted IP ranges at the firewall level.
Vendor Fix
Both issues have patches merged into the main branch, however, no release has been published as of 19 May 2026. Users who wish to apply the fixes immediately must build from the main branch.
Disclosure Timeline
| Date | Event |
|---|---|
| 16 Jan 2026 | Unrelated researcher (012git012) reports a separate vulnerability to HestiaCP. |
| 19 Feb 2026 | Mercury submits report covering both vulnerabilities via [email protected], per the project's SECURITY.md. |
| 26 Feb 2026 | No acknowledgment. Mercury confirms on public GitHub issue #5229. |
| 28 Feb 2026 | HestiaCP Member publicly acknowledges reports were received. No direct communication with Mercury via yet. |
| 06 Mar 2026 | Mercury posts CERT/CC escalation notice after 14 days of silence. |
| 06 Mar 2026 | A HestiaCP maintainer discloses the vulnerable RCE code by linking to a proposed fix on the public GitHub issue, before any patch is released. |
| 06 Mar 2026 | Mercury files with CERT/CC. |
| 08 Mar 2026 | RCE fix merged to main. |
| 11 Mar 2026 | CVE requested from MITRE. |
| 22 Mar 2026 | Private vulnerability reporting enabled on HestiaCP GitHub repository at Mercury's request. Advisories submitted and accepted. |
| 24 Mar 2026 | CERT/CC responds, does not take on coordination. |
| 24 Mar 2026 | IP spoofing fix opened as a public PR. |
| 24 Mar 2026 | IP spoofing fix merged to main. |
| 7 May 2026 | CVEs assigned via VulnCheck. |
| 19 May 2026 | Advisory published by Mercury. |
Credit
Mercury would like to thank divinity76 for developing the patches, sahsanu for escalating internally, AlecKinnear for advocating for the HestiaCP user community throughout and VulnCheck for being so prompt with CVE assignments.