Sploitus

Exploit for Unrestricted Upload of File with Dangerous Type in Drupal

githubexploit Β· 2026-08-22

Exploit Code

README104 lines
## https://sploitus.com/exploit?id=E2E709C7-BDD3-5889-84D9-4F014B7C5300
# CVE-2020-13671

## RCE via File Upload β€” Drupal Core

**Software:** Drupal Core 8.7.5 (Affected: 7.x get('allow_insecure_uploads')
    && preg_match(FILE_INSECURE_EXTENSION_REGEX, $file->getFilename())
    && (substr($file->getFilename(), -4) != '.txt')) {
    $file->setMimeType('text/plain');
    $file->setFilename($file->getFilename() . '.txt');
}
```

If the filename matches the regex β†’ change MIME to `text/plain` and append `.txt` at the end.

With `shell.phtml`:
- `preg_match('/\.(phar|php|pl|py|cgi|asp|js)(\.|$)/i', 'shell.phtml')` returns `0`
- No match β†’ does not enter the if block β†’ file retains its original name
Thus, this section should have blocked dangerous files, but because the regex **does not know** `.phtml` is dangerous, it slips right through.

### Layer 3 β€” `.htaccess` in the upload directory

Drupal places a `.htaccess` file in `sites/default/files/`:

```apache
SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006

  SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003



  php_flag engine off

```

The `php_flag engine off` directive turns off the PHP engine for the entire directory, but it only applies to `mod_php5`. Drupal 8.7.5 runs on PHP 7, meaning `mod_php7` is active and **not disabled**.

And `.htaccess` also has 3 other weaknesses:
- **Nginx** does not read `.htaccess` β€” this file is completely ineffective on Nginx
- Apache configured with **`AllowOverride None`** β†’ `.htaccess` is ignored
- Servers using **PHP-FPM** instead of `mod_php` β†’ the `php_flag` directive has no effect

## **Exploitation**

### **Step 1 β€” Create webshell**

```bash
echo '' > webshell.phtml
```

### **Step 2 β€” Upload file**

Log in to Drupal with an account that has upload permissions β†’ Content β†’ Add content β†’ Article β†’ in the Image field, select the file `webshell.phtml` β†’ Upload.

Drupal accepts the file, does not rename it, and saves it with its original name into `sites/default/files/`.

![image.png](images/image%203.png)

### **Step 3 β€” Execute webshell**

After that, execute the shell call with `whoami`:

![image.png](images/image%204.png)

Thus, we have successfully achieved RCE as `www-data`.

Testing further to view credentials:

![image.png](images/image%205.png)

### **Result**

The attacker can read `settings.php` containing database credentials, dump the entire DB, install a reverse shell, or escalate privileges to root.

## Remediation

**Update regex** β€” add the 5 missing extensions:

```php
// Before:
'/\.(phar|php|pl|py|cgi|asp|js)(\.|$)/i'

// After:
'/\.(phar|php|pl|py|cgi|asp|js|phtml|php5|pht|phps|shtml)(\.|$)/i'
```

**Update `.htaccess`** β€” add disable for mod_php7:

```apache

  php_flag engine off

```

The patch works but still relies on a blacklist. If new extensions emerge in the future (`.php8`, `.phpt`), the regex will need updating again. Whitelisting β€” only allowing known safe extensions β€” would be a more thorough approach.

## Summary

| **Vulnerable File** | `core/modules/file/file.module` line 28 |
| --- | --- |
| **Root Cause** | Regex blacklist is missing `.phtml`, `.php5`, `.pht`, `.phps`, `.shtml` |
| **Impact** | Upload `.phtml` β†’ server executes β†’ RCE |
| **3 Bypassed Defense Layers** | `file_munge_filename()` β†’ `FILE_INSECURE_EXTENSION_REGEX` β†’ `.htaccess` |
| **Patch** | Add 5 extensions to regex + disable mod_php7 in `.htaccess` |