BOOK THIS SPACE FOR AD
ARTICLE ADAs red teamers, pentesters and bug hunters — we all love to see file upload functionalities in our targets. There are many things that can go wrong for the developer and we are here to put the finger on the exact issue.
But sometimes we have to deal with unusual apps that might be well protected from a “simple” webshell upload, so we need to play around in a bit more creative methodology.
Assuming that most of you are hackers that already familiar with testing upload functions in the “traditional ways” (file extensions, content-type, magic number, etc), this writeup will cover the less traditional upload vulnerabilities.
For more content like this, visit my Twitter/X page where I post more writeups and tips for offensive security experts.
Let’s start!
I know that it might be kind of basic, but I’ve seen so many cases that hackers managed to upload some PHP file to the target app, but they didn’t manage to use them. The reason is usually because of a PHP feature in the .ini file that simply disables “dangerous functions”, like system, exec, shell_exec and other functions that run direct OS commands. This case is very popular in hosting environments.
I usualy use Acunetix script that checks for PHP functions that are not disabled and can be used to serve our webshell:
<?phpprint_r(preg_grep("/^(system|exec|shell_exec|passthru|proc_open|popen|curl_exec|curl_multi_exec|parse_ini_file|show_source)$/", get_defined_functions(TRUE)["internal"]));
?>
This script gives us the best answers. Each function name that the script prints back on the screen is enabled and we can leverage for running OS commands or reading files (curl_exec) on the server.
Another vulnerability that sometimes get overlooked is path traversal through file upload. Unlike the classic path traversal that let us read files on the target server, in file upload we are aiming to upload files to locations that the server is not expecting to.
Why it’s important? Sometimes there are upload directories that contain “special rules”. For example, I’ve seen cases when the server’s owner decided to completely disable PHP support for a specific directory by configuring it in the .htaccess file. In a case like this one, we should try to add “../” to our request, just before the file name itself and try to upload our webshell to an unexpected location on the server.
In other cases, the uploaded files might not be accessible through the web, so we may try this method too.
One more thing that is important to pay attention to, is that even if we can’t upload a malicious file from any reason, it is still worth testing path traversal. If we can upload benign files (like images) to other locations, it means that we can try to overwrite existing files of the server!
Earlier here we mentioned a very important file: .htaccess. Here’s the file’s definition from apache.org: “.htaccess files provide a way to make configuration changes on a per-directory basis.”
In other words, this file is used for configuring specific properties for a given directory, without touching the entire web server’s configuration. For example, we can make only the /adimn path of our website accessible only from localhost, so it won’t be exposed for external logins.
You see where I’m going with this, right? If we encountered a website that has good protections against malicious files (PHP/ASPX/JSP/etc), we can try to upload our own configuration file for the uploads directory!
We can create a .htaccess file with the following directive:
AddType application/x-httpd-php .pngThis simple line tells the server to run .png files as PHP. So now we should be able to upload our “image” .png file and inject our payload inside. In many cases, the awareness for malicious .htaccess files is not strong as malicious file extensions.
If there is an existing .htaccess file in the upload directory, uploading our file will overwrite the existing one and might cause problems to the server. So don’t forget to check if everything works correctly after uploading your malicious configuration file.
Let’s say that we tried everything we can to upload a “classic” webshell and eventually we concluded that we target website knows well how to prevent malicious php/aspx/jsp/etc files. It only accepts whatever expected file types that the owner want you to upload.
Now we need to play (and win) with the website’s admin cards. Assuming that we suppose to upload only images, we can expect to use SVG files as legitimate files. And here’s come the magic: SVG is an XML based format. It has two major impacts for us:
XML formats may open a world of XXE attacksThis format supports Javascript, so we should test XSSLet’s start with the XSS part.
If the website want us to upload images, for example a profile picture, we can try SVG payload like this:
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300"><circle cx="150" cy="147.5" r="50" fill="#DA3A00" />
<script>console.log("@chux13786509 on X for more content!")</script>
</svg>
This payload will create a red circle and in the background prints (console.log) whatever we want with Javascript. Here’s the result:
Stored XSS by uploading SVG fileAnd now for the XXE part, which is a bit more complex.
We already said that SVG is XML based format and hence we should think about XXE attacks. This is also true for other formats that contain XML, like Office files, DOCX and XLSX.
Did you ever tried taking a DOCX file and chaging its extension from .docx to .zip? After opening the file it would look like this:
And after navigating through the directories you will find some XML files:
So know after we saw some XML formats that are common for legitimate uploads, let’s talk about their exploitation.
Our target needs to render our payload on the page or process it somehow. Two of the most common examples in the wild:
Uploading a profile picture that rendered on the profile pageUploading documents in a pattern (like CVs) and extracting specific details (like name) from their contentIf the website meets these conditions, it means that it processes the XML content and we can try to inject our external entity for testing.
There are many websites that offer us to upload a bunch of files, for example tax reports, in one single archive file. It’s not only ZIP files, but also other archiving formats like 7z, tar, war, jar, rar and apk.
The extraction of the files from the archive let us the opportunity to test for the vulnerability — saving a file with a directory traversal name and check if the file is extracted to a different location other than the expected upload directory. So if we make a ZIP file that contains a file with the name “../../../mal.php”, in the extraction process the file will be extracted to two directories above and the file name will be mal.php.
This is pretty similar to the path traversal vulnerability we mentioned earlier, just that this time we have to use another tool to make this archive with the special file name. Try evilarc for that purpose.
Also, here’s a Snyk blog post about the vulnerability with more details and even code snippets in different coding languages for finding the vulnerability in projects.
File upload functions are my favorites for testing. Except for the classic upload vulnerabilities which include playing with content-type or file extensions, there are some other techniques that less known to developers and chances are that you can exploit them on your engagements.
By understanding the full potential of file upload vulnerabilities we can achieve high severity impact that even if it’s not direct RCE with a webshell, it’s still can harm a server in other ways: reading files (curl_exec), XXE, XSS and overwriting existing files.
In most of my engagements, the simple webshell had to be replaced with some of the more complex attacks. So I believe that after reading these methods you will have some new vulnerabilities that you didn’t expect before :)
Good luck!