Media Library Upload Abuse

How Media Library Normally Works

By default:

  • WordPress allows uploads only of safe file types:

    • Images (.jpg, .jpeg, .png, .gif, .webp)

    • Documents (.pdf, .docx, .pptx)

    • Archives (.zip)

    • Media (.mp3, .mp4, .wav)

Uploads are stored in:

/wp-content/uploads/{year}/{month}/

Apache web server configuration.

  • It’s usually placed in .htaccess files inside folders to prevent PHP execution.

<FilesMatch "\.php$">
    deny from all
</FilesMatch>

So under normal configs, even if you uploaded shell.php, it won’t run, it will just sit there as a static file.


Basic Exploit Flow (Just for basic understading)

  1. Attacker uploads malicious file:

    • shell.php.jpg containing:

  2. Due to misconfigure, server parses it as PHP.

  3. Attacker visits:

  4. Boom β†’ Remote Code Execution (RCE).


Basic bypass upload

Bypass file extensions checks:

  1. All file types:

PHP: .php, .php2, .php3, .php4, .php5, .php6, .php7, .phps, .pht, .phtm, .phtml, .pgif, .shtml, .htaccess, .phar, .inc, .hphp, .ctp, .module

Working in PHPv8: .php, .php4, .php5, .phtml, .module, .inc, .hphp, .ctp

  1. Uppercase letters

If they not worked, test them using some uppercase letters: pHp, .pHP5, .PhAr

  1. Check adding a valid extension before the execution extension (use previous extensions also):

    • file.png.php

    • file.png.Php5

  1. Try adding special characters at the end. You could use Burp to bruteforce all the ascii and Unicode characters. (Note that you can also try to use the previously motioned extensions)

    • file.php%20

    • file.php%0a

    • file.php%00

    • file.php%0d%0a

    • file.php/

    • file.php.\

    • file.

    • file.php....

    • file.pHp5....

  1. Try to bypass the protections tricking the extension parser of the server-side with techniques like doubling the extension or adding junk data (null bytes) between extensions. You can also use the previous extensions to prepare a better payload.

    • file.png.php

    • file.png.pHp5

    • file.php#.png

    • file.php%00.png

    • file.php\x00.png

    • file.php%0a.png

    • file.php%0d%0a.png

    • file.phpJunk123png

  1. Add another layer of extensions to the previous check:

    • file.png.jpg.php

    • file.php%00.png%00.jpg

Bypassing Magic Number Checks

Some applications inspect the file's Magic Number (the first few bytes of a file) to verify its actual type, regardless of the file extension. Attackers can bypass this by appending the correct Magic Number at the beginning of a malicious file.

How to bypass Magic Number checks:

Method 1: Prepending a valid Magic Number

You can insert the Magic Number of a valid image at the beginning of a malicious file to trick the server into accepting it as an image:

Explanation:

  • \x89\x50\x4E\x47\x0D\x0A\x1A\x0A β†’ This is the Magic Number for a PNG file.

  • cat shell.php >> fake.png β†’ Appends a PHP shell to the image file.

Method 2: Embedding a backdoor inside metadata

Another approach is to hide a PHP shell inside the metadata of an image:

Explanation:

  • The PHP shell is stored in the image’s metadata, making it harder to detect.

  • The __halt_compiler(); function stops PHP from interpreting the rest of the image file, ensuring only the backdoor code executes.

Method 3: Directly injecting payload into an image

Alternatively, you can append a PHP payload directly inside an image file:

Explanation:

  • The system($_REQUEST["cmd"]); command executes any system command sent via HTTP request.

  • The image remains valid, but when executed as a .php file, it runs the attacker's commands.


Advanced bypass upload

πŸ’‘ Please note:

  1. In our lab running WordPress 6.8.2, most individual bypass methods failed. However, when combined strategically, they allowed the malicious file to be uploaded successfully β€” including this technique.

Create malicious file with magic-bits and ext .png:

Lets create malicious file that would be upload to media library using python code in the terminal:

Now lets upload it to our site:

Click on Select Files and upload our crafted file.

Two things happened:

First, the file was uploaded successfully. second, in the latest WordPress versions, if the server detects a disallowed extension like .php4, it automatically appends an underscore to prevent the file from executing.

So even if we browse to the URL of this file it will not run our PHP script.

Setup our lab:

In recent versions of Apache, a default security configuration has been implemented to prevent the execution of potentially dangerous files with certain extensions. The configuration uses a regular expression to match file extensions and applies the PHP handler only to specific, recognized extensions.

lets see an example:

  • <FilesMatch> Directive: This directive applies the enclosed settings to files whose names match the specified regular expression.

  • Regular Expression: The pattern ".+\.ph(?:ar|p|tml)$" matches filenames that end with .php, .phar, .phtml .

Misconfigured Servers and Security Gaps:

Some web servers are misconfigured and have an incomplete <FilesMatch> patternβ€”for example, missing the $ at the end of the regex.

Lets start setup:

In order to run PHP on our lab we need to change the file /etc/apache2/mods-enabled/php8.4.conf (Save it in other file to be able to return it after you done). Download the vulnerable config file from my repository.

Great lets Browse to our URL of the file:

πŸ’‘ Please remember to return to orignal file php8.4.conf .

Last updated