Skip to main content
Vulnerability Database/CVE-2026-71981

CVE-2026-71981: Cypht PHP Object Injection RCE Vulnerability

CVE-2026-71981 is a PHP object injection vulnerability in Cypht that enables authenticated attackers to execute arbitrary system commands. This article covers the technical details, affected versions, and mitigation strategies.

Published:

CVE-2026-71981 Overview

Cypht before version 2.12.2 contains a PHP object injection vulnerability [CWE-502] in the logout handler. The flaw resides in the back_query GET parameter processed by modules/core/handler_modules.php. The application base64-decodes the attacker-supplied value and passes it directly to unserialize() without an allow-list, signature check, or type restriction. Authenticated attackers can supply a crafted PHP object graph to trigger gadget-chain exploitation and execute arbitrary operating system commands as the web server process.

Critical Impact

Authenticated remote code execution against Cypht webmail servers, leading to full compromise of the hosting web server account and any mailboxes or credentials stored on it.

Affected Products

  • Cypht webmail versions prior to 2.12.2
  • The vulnerable code path is the logout handler in modules/core/handler_modules.php
  • Fixed in Cypht release v2.12.2

Discovery Timeline

  • 2026-09-01 - CVE-2026-71981 published to NVD
  • 2026-09-02 - Last updated in NVD database

Technical Details for CVE-2026-71981

Vulnerability Analysis

Cypht is an open-source PHP webmail client. When a user visits the logout prompt, the handler reads the back_query GET parameter, base64-decodes it, and invokes unserialize() on the result. PHP's unserialize() reconstructs arbitrary object instances and invokes magic methods such as __wakeup(), __destruct(), and __toString() on them. When combined with a suitable gadget chain drawn from loaded classes or Composer dependencies, this behavior yields arbitrary code execution in the context of the PHP-FPM or Apache worker process. Exploitation requires an authenticated session because the logout handler is reachable only after login, but any valid low-privilege user account is sufficient.

Root Cause

The root cause is unsafe deserialization of attacker-controlled input. The original code called unserialize(base64_decode($this->request->get['back_query'])) with no integrity check on the payload, no class allow-list via the allowed_classes option, and no schema validation. Any serialized PHP object graph passed through the parameter was instantiated verbatim.

Attack Vector

An authenticated attacker crafts a serialized PHP object that triggers a known gadget chain, base64-encodes it, and sends it as the back_query value on a GET request to the logout handler with prompt=1. When the handler processes the request, unserialize() reconstructs the object graph and the gadget chain executes commands as the web server user.

php
// Vulnerable code (pre-patch) in modules/core/handler_modules.php
public function process() {
    if ($this->request->get['prompt'] ?? false) {
        $backQuery = isset($this->request->get['back_query'])
            ? unserialize(base64_decode($this->request->get['back_query']))
            : [];

        $this->out('cancel_logout_url', '?' . http_build_query($backQuery));
    }
}
// Source: https://github.com/cypht-org/cypht/commit/e4aa2f34e33f9328e8c93514cc3966fc3d99925e

Stop Remote Code Execution Before It Runs

Singularity™ Endpoint detects and blocks exploit attempts at machine speed, including zero-days and novel attack techniques.

Stop Exploit Attempts

Detection Methods for CVE-2026-71981

Indicators of Compromise

  • GET requests to the Cypht logout endpoint containing a back_query parameter with a long base64-encoded value, especially values that decode to strings beginning with O: or a: (PHP serialized object or array markers).
  • Web server or PHP-FPM worker processes spawning shell interpreters such as /bin/sh, bash, python, or perl following a request to the logout handler.
  • Outbound network connections initiated by the PHP process to attacker-controlled hosts shortly after a back_query request.

Detection Strategies

  • Inspect web server access logs and alert on back_query= parameter values whose base64 decoding yields PHP serialization tokens (O:<digit>: or a:<digit>:).
  • Deploy runtime protection that flags child-process creation from the PHP interpreter serving the Cypht virtual host.
  • Correlate authenticated session events with subsequent process, file, and network anomalies on the Cypht host to surface post-exploitation activity.

Monitoring Recommendations

  • Enable verbose HTTP request logging on the reverse proxy in front of Cypht and retain the full query string for at least 90 days.
  • Monitor the Cypht installation directory for unexpected file writes, new PHP files, or modifications to modules/core/*.php.
  • Track outbound egress from the webmail server and alert on connections to non-mail destinations.

How to Mitigate CVE-2026-71981

Immediate Actions Required

  • Upgrade Cypht to release v2.12.2 or later, which replaces unserialize() with a JSON-based encoder that cannot instantiate PHP objects.
  • Rotate any credentials, API tokens, and session secrets stored on or accessible from the Cypht host, since a successful exploit runs as the web server user.
  • Audit web server logs for prior requests containing the back_query parameter and investigate any hosts that issued suspicious values.

Patch Information

The fix is delivered in pull request #2073 and shipped in Cypht v2.12.2. The patch introduces encode_logout_back_query() and decode_logout_back_query() helpers that use json_encode and json_decode with a scalar-only allow-list, eliminating object instantiation from the back_query code path. See the VulnCheck advisory for the coordinated write-up and the patch commit for the exact code change.

php
// Patched code in modules/core/handler_modules.php
public function process() {
    if ($this->request->get['prompt'] ?? false) {
        $backQuery = isset($this->request->get['back_query'])
            ? decode_logout_back_query($this->request->get['back_query'])
            : [];

        $this->out('cancel_logout_url', '?' . http_build_query($backQuery));
    }
}
// Source: https://github.com/cypht-org/cypht/commit/e4aa2f34e33f9328e8c93514cc3966fc3d99925e

Workarounds

  • If immediate upgrade is not possible, block requests to the Cypht logout endpoint that include a back_query query parameter at the reverse proxy or web application firewall.
  • Restrict access to the Cypht instance to trusted networks or VPN clients to reduce the pool of authenticated attackers.
  • Run the PHP worker under a dedicated, unprivileged system account with no shell and no write access outside the Cypht data directory to limit blast radius.
bash
# Example nginx rule to drop logout requests containing back_query until patched
location = /?page=logout {
    if ($arg_back_query) {
        return 403;
    }
}

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.