CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-0895
5.20.02%

Zombie Code: The TYPO3 Mailqueue Deserialization Disaster

Alon Barad
Alon Barad
Software Engineer

Feb 16, 2026·6 min read·5 visits

No Known Exploit

Executive Summary (TL;DR)

The 'mailqueue' extension for TYPO3 ignored the golden rule: don't roll your own crypto, and definitely don't roll your own serialization handling if you can avoid it. By duplicating Core logic, it missed critical security updates. If an attacker can drop a file in your spool directory, they own your server when the cron job runs.

A classic case of 'copy-paste' development resurrecting a dead vulnerability. The TYPO3 'mailqueue' extension maintained a distinct copy of the Core's FileSpool logic, failing to inherit upstream security patches. This reintroduced a critical Insecure Deserialization flaw allowing attackers with file-write access to trigger Remote Code Execution via the application's scheduler.

The Hook: Code That Refuses to Die

There is a special place in purgatory for code that gets copied, pasted, and then forgotten. We tell developers to follow the DRY (Don't Repeat Yourself) principle, not just to save keystrokes, but because when you duplicate code, you duplicate debt. CVE-2026-0895 is the perfect, painful example of this architectural sin.

Here is the setup: TYPO3 Core has a component called FileSpool. It takes email objects, serializes them, and dumps them onto the disk to be sent later. It's a standard feature for high-traffic sites that don't want to block the user while waiting for an SMTP server handshake. In early 2026, TYPO3 Core realized that trusting disk content blindly is a bad idea and patched a deserialization vulnerability (CVE-2026-0859).

Enter the cpsit/typo3-mailqueue extension. Instead of simply wrapping the Core functionality, it seemingly maintained its own fork or implementation of that logic. So, while the Core developers were busy locking the front door by hardening FileSpool, the mailqueue extension left the back window wide open, effectively re-introducing the exact same vulnerability into patched systems. It is the zombie vulnerability—you shot it in the head in the Core, but it walked right back in through the extension.

The Flaw: PHP's Pandora's Box

The vulnerability is a textbook case of CWE-502: Insecure Deserialization. In the PHP world, unserialize() is roughly equivalent to eval() if you have the right classes loaded (and TYPO3, being a massive enterprise CMS, has plenty of classes loaded).

The logic in QueueableFileTransport::restoreItem was dangerously naive. It was designed to read a file from the spool directory and turn it back into a PHP object. The code assumed that any file found in that directory was put there by the application itself. That is a fatal assumption.

In security engineering, we call this a 'trust boundary violation.' The filesystem is a shared state. If I, as an attacker, can find a way to write a file—any file—to that directory (perhaps via a separate unrestricted file upload bug, a compromised editor account, or a directory traversal issue), I don't just clutter your disk. I provide the payload. The mailqueue extension then obligingly reads my payload and executes it via unserialize(), triggering whatever Property-Oriented Programming (POP) chain I've crafted.

The Smoking Gun

Let's look at the diff. It’s rare to see a fix so clearly illustrate the problem. The developers had to rip out the native PHP function and replace it with TYPO3's hardened infrastructure.

The Vulnerable Code:

// QueueableFileTransport.php (Pre-patch)
// Look at this. No checks. No verification. Just raw trust.
public function restoreItem(string $path): ?Message
{
    $content = file_get_contents($path);
    // The killer line:
    $message = unserialize($content);
    return $message;
}

The Fix:

// QueueableFileTransport.php (Patched)
// Strict typing and a limited allowlist of classes.
public function restoreItem(string $path): ?Message
{
    try {
        // PolymorphicDeserializer is the bouncer at the club.
        $message = $this->deserializer->deserialize(
            (string)file_get_contents($path),
            [
                Mailer\SentMessage::class,
                Mime\RawMessage::class,
                Mailer\Envelope::class,
                // ... specific mime parts ...
            ],
        );
    } catch (\Throwable $exception) {
        // If it looks like a bomb, swallow the explosion.
        $message = null;
    }
    return $message;
}

The patch introduces PolymorphicDeserializer. This isn't just a wrapper; it likely verifies the structure and, crucially, accepts an allowlist of classes. If the serialized data tries to instantiate a GuzzleHttp\Handler\Proxy (a common gadget), the deserializer throws a tantrum and the exploit fails.

The Exploit: Setting the Trap

To exploit this, we are playing the long game. This isn't a vulnerability you trigger by sending a single HTTP request to a public endpoint. It requires a 'chain' of events. We need Write Access + Time.

Step 1: The Setup First, we need to get a file into the spool directory. This directory is usually inside var/transports/ or typo3temp/. We might leverage a minor Local File Inclusion (LFI) that allows writing, or perhaps a misconfigured FTP account. Even a 'low severity' arbitrary file write becomes Critical here.

Step 2: The Payload We use a tool like PHPGGC (PHP Generic Gadget Chains). TYPO3 runs on Composer, so it pulls in libraries like Guzzle, Monolog, or Doctrine. These are ripe with 'gadgets'—classes with __destruct or __wakeup methods that do dangerous things.

# generating a payload using a common Guzzle gadget
phpggc Guzzle/RCE1 system "id > /tmp/pwned" --phar phar.phar
# We serialize this into the format mailqueue expects

Step 3: The Trigger We save our payload as evil.mail in the spool folder. Now, we wait. TYPO3 uses a Scheduler (cron job) to process the mail queue. Every minute (or hour), the system wakes up, looks into that directory, and asks: 'Do I have mail to send?'

It picks up evil.mail, calls restoreItem(), and BOOM. The unserialize() triggers the gadget chain. The system executes id > /tmp/pwned running as the web user (or worse, the user running the cron job).

The Impact: Why Severity Scores Lie

You might notice the CVSS score is only 5.2 (Medium). Do not let that lull you into a false sense of security. The score is suppressed because the Attack Vector is marked as 'Local' (AV:L). The scoring algorithm assumes the attacker needs prior access to the filesystem.

However, in the real world of messy web hosting, 'Local' access is often one bad upload form away. If you chain this with a directory traversal vulnerability that lets you write files outside the webroot, this 5.2 becomes a functional 9.8.

The impact is full Remote Code Execution (RCE). Once the code runs, the attacker can install a webshell, dump the database (GDPR nightmare), or pivot to other servers on the internal network. Because this often runs via the CLI scheduler, it might bypass PHP execution time limits or disable_functions restrictions enforced on the web server process.

The Fix: Remediation & Mitigation

The remediation is straightforward but urgent. You need to align the extension with the Core's security standards.

1. Update the Extension Run composer update cpsit/typo3-mailqueue. You need version 0.5.1 or 0.4.3. If you are stuck on an older branch, you are vulnerable.

2. Verify TYPO3 Core The fix relies on the PolymorphicDeserializer class being available and robust. This means your underlying TYPO3 instance must also be patched (v12.4.41+, v13.4.23+). Updating the extension without updating the Core might result in a broken site if the class interfaces don't match.

3. Hardening the Filesystem Regardless of the patch, why can users write to your spool directory? Check your permissions. The var/ directory should typically not be writable by the public-facing web server user if you run your scheduler as a separate user (though in many setups, they are sadly the same).

4. Monitoring Check your spool directory for files that don't look like standard serialized email objects. If you see files with strange binary headers or unexpected PHP class names inside, you might have already been probed.

Official Patches

TYPO3 ExtensionsOfficial Extension Page

Fix Analysis (1)

Technical Appendix

CVSS Score
5.2/ 10
CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:N/VI:L/VA:N/SC:H/SI:H/SA:H
EPSS Probability
0.02%
Top 96% most exploited

Affected Systems

TYPO3 CMS with cpsit/typo3-mailqueue extensionComposer package cpsit/typo3-mailqueue < 0.5.1

Affected Versions Detail

Product
Affected Versions
Fixed Version
cpsit/typo3-mailqueue
CPS-IT
< 0.4.30.4.3
cpsit/typo3-mailqueue
CPS-IT
>= 0.5.0, < 0.5.10.5.1
AttributeDetail
CWE IDCWE-502
Attack VectorLocal (File Write Required)
CVSS v4.05.2 (Medium)
ImpactRemote Code Execution (RCE)
Exploit StatusNo Public Exploit (POC)
VendorCPS-IT

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1059.003Command and Scripting Interpreter: Windows Command Shell
Execution
T1204.002User Execution: Malicious File
Execution
CWE-502
Insecure Deserialization

Deserialization of Untrusted Data

Vulnerability Timeline

TYPO3 Core patches FileSpool vulnerability
2026-01-13
Fix committed to mailqueue extension
2026-01-15
Public Disclosure (TYPO3-EXT-SA-2026-001)
2026-01-20

References & Sources

  • [1]TYPO3-EXT-SA-2026-001 Advisory
  • [2]Related Core Advisory (TYPO3-CORE-SA-2026-004)
Related Vulnerabilities
CVE-2026-0859

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.