---
# System prepended metadata

title: Upload Vulnerabilities

---

# Upload Vulnerabilities

:::info 
Tutorial room exploring some basic file-upload vulnerabilities in websites
:::


## Overwriting Existing Files

- When a file is uploaded to a server, proper safeguards must be implemented to prevent overwriting existing files. Common defenses include renaming uploaded files (randomized or timestamped), checking for filename collisions, and *enforcing strict file permissions so that web‑accessible files cannot be overwritten*. If these protections are missing, an attacker may upload a file using the name of an existing one and overwrite it.:
- Let's just take a look at the source code of the page:
 ![image](https://hackmd.io/_uploads/SyrJjKBgZl.png)
-  We see the code that's responsible for displaying the image that we saw on the page. It's being sourced from a file called "mountains.jpg", inside a directory called "images".
-  Now we know where the image is being pulled from -- can we overwrite it?
- Let's download another image from the internet and call it mountains.jpg. We'll then upload it to the site and see if we can overwrite the existing image:
![image](https://hackmd.io/_uploads/S1bPiYBxZe.png)
![image](https://hackmd.io/_uploads/rJg9oKSgbx.png)
- And our attack was successful! We managed to overwrite the original images/spaniel.jpg with our own copy.
## Remote Code Execution
- Overwriting files on a server is inconvenient, but the real danger is achieving Remote Code Execution (RCE), which allows an attacker to run arbitrary code on the web server (usually as a low‑privileged account like www-data, but still extremely serious).
- RCE through file upload is typically done by uploading a script in a language the backend can execute (e.g., PHP, Python, Node.js).
-  **Modern routed frameworks make this harder because uploaded files are not directly accessible as executable paths.**
- There are two main methods for achieving RCE through a file upload vulnerability:
    -    Webshells – small scripts that allow executing commands via a browser.
    - Reverse/Bind shells – a fully interactive shell; preferred, but may be restricted by file size limits or firewalls.

-> General approach: upload a shell → trigger it either by directly accessing the file (in non-routed apps) or forcing the web application to execute it (in routed apps).
### Webshell
![image](https://hackmd.io/_uploads/Syz1e5rgbl.png)
- Use this shell with the demo web to conduct system command
![image](https://hackmd.io/_uploads/rkpHQcHe-g.png)
![image](https://hackmd.io/_uploads/HyqXmqHxbl.png)
![image](https://hackmd.io/_uploads/Hy3O85rgWx.png)
### Reverse shell
![image](https://hackmd.io/_uploads/BJFEDcrebx.png)
https://github.com/pentestmonkey/php-reverse-shell

![image](https://hackmd.io/_uploads/BJx5D9rlZg.png)

![image](https://hackmd.io/_uploads/rkNtDqre-l.png)

## Filtering
Up until now, the examples focused on websites with no upload protections. In reality, developers use various defences to block malicious file uploads, and attackers need to understand these to bypass them.
- The first distinction is between client-side and server-side filtering:
- Client-side filtering happens in the user's browser (usually with JavaScript).
It checks the file before uploading it to the server.
Although this sounds useful, it is trivially easy to bypass because the attacker controls their own browser and can disable or modify these checks.
→ Client-side filtering alone is insecure.
- Server-side filtering happens on the server (PHP, ASP, C#, Node.js, Python, Ruby, etc.).
Because the code runs on the server and cannot be modified by the attacker, server‑side checks are much harder to bypass.

Attackers typically cannot avoid server-side filters entirely; instead, they must craft payloads that satisfy the filtering rules while still remaining malicious.

*With that in mind, let's take a look at some different kinds of filtering.*

#### Extension Validation:
File extensions are intended to indicate a file’s type, but they are trivial to change and are not a reliable security measure. Windows still uses extensions to identify file types, while Unix-based systems rely on other methods.
- Extension filters generally work in two ways:

    -   Blacklist – block specific disallowed extensions.
    - Whitelist – allow only specific extensions and reject all others.

Both methods are easy to bypass if not combined with stronger server-side checks.
#### File Type Filtering:
- Similar to Extension validation, but more intensive, file type filtering looks, once again, to verify that the contents of a file are acceptable to upload. We'll be looking at two types of file type validation:

    -  MIME validation: MIME (Multipurpose Internet Mail Extension) types are used as an identifier for files -- originally when transfered as attachments over email, but now also when files are being transferred over HTTP(S). The MIME type for a file upload is attached in the header of the request, and looks something like this:
     ![image](https://hackmd.io/_uploads/BJ-docHeWx.png)
     The MIME type for a file can be checked client-side and/or server-side; however, as MIME is based on the extension of the file, this is extremely easy to bypass.
     -    Magic Number validation: Magic numbers are the more accurate way of determining the contents of a file; although, they are by no means impossible to fake. The "magic number" of a file is a string of bytes at the very beginning of the file content which identify the content. For example, a PNG file would have these bytes at the very top of the file: 89 50 4E 47 0D 0A 1A 0A
![image](https://hackmd.io/_uploads/Sykk2cBgWg.png)
Unlike Windows, Unix systems use magic numbers for identifying files; however, when dealing with file uploads, it is possible to check the magic number of the uploaded file to ensure that it is safe to accept. This is by no means a guaranteed solution, but it's more effective than checking the extension of a file.
#### File Length Filtering
Servers may restrict the maximum allowed file size to prevent resource exhaustion.
Small webshells usually pass, but large shells (e.g., >2 KB) may be rejected depending on the limit.

→ Attackers may need to use smaller shells when strict size limits apply.
#### File Name Filtering
Servers often ensure filenames are unique by:

- renaming files on upload
- rejecting uploads with duplicate names.

Filenames may also be sanitized to remove dangerous characters (null bytes, slashes, control characters, etc.).

→ Uploaded shells may end up with a different name, so attackers might need to search for the file after bypassing filters.
#### File Content Filtering
Advanced systems inspect the entire file content to confirm:
- extension matches the content
- MIME type is valid
- Magic Number is correct
This type of filtering is more complex and not covered in detail.

Different frameworks may introduce unique bypasses. Examples include:
- Pre–PHP 5 null byte trick → bypass extension filters(.php%00.jpg)
- EXIF payload injection → placing PHP code inside image metadata and executing it 
**(exiftool -Comment="<?php system($_GET['cmd']); ?>" image.jpg -o shell.jpg
)**
## Bypassing Client-Side Filtering
**Four Easy Ways to Bypass Client-Side File Upload Filters**

Client-side file validation (JavaScript checks, HTML attributes, browser-side MIME checks) should never be trusted, as it can be bypassed easily. Below are four common techniques:

**1. Disable JavaScript in the Browser**
 
- Client-side validation almost always relies on JavaScript.
    - If JavaScript is disabled, the upload form will no longer run any file-type checks.
    - This works as long as the website does not strictly require JavaScript for core functions.
![image](https://hackmd.io/_uploads/rJHpBldlbg.png)
![image](https://hackmd.io/_uploads/rkfABx_gWx.png)

**2.Intercept & Modify the Incoming Page (BurpSuite → Response Tampering)**

- Instead of disabling JavaScript entirely, you can:
    - Intercept the server’s response with BurpSuite
    - Remove or modify the JavaScript that implements the filter
    - Forward the modified page to your browser

**3. Intercept & Modify the File Upload (BurpSuite → Request Tampering)**
- In this method the webpage loads normally with the filter active, but:
    - You select a “valid” file (e.g., .jpg)
    - Browser sends upload request
    - You intercept the request in BurpSuite
    - Modify the filename or content (e.g., change shell.jpg → shell.php)
    - Forward the altered request
    
➡️ Effect: The client-side filter already accepted the file, but the server receives the tampered payload.

**4. Upload Directly to the Endpoint (cURL / HTTP Client)**
- Skip the upload page entirely:

    - Capture a legitimate upload request once (via Burp or DevTools → Network tab)
    -    Reconstruct the request manually using curl:
    
    ```powershell
    curl -X POST  -F "submit=<value>"  -F "<file-parameter>=@<path-to-file>"  <target-url>
    ```
    Since client-side checks only happen in the browser:
    
Example:
![image](https://hackmd.io/_uploads/ryTSUlOgWx.png)
![image](https://hackmd.io/_uploads/Syq88gOgbe.png)
As you can see it will accept only the files with .png extension.

Change the name of the file :
![image](https://hackmd.io/_uploads/B1IF8xuebl.png)

Observe that the MIME type of our PHP shell is currently image/jpeg. We'll change this to text/x-php, and the file extension from .jpg to .php, then forward the request to the server:
![image](https://hackmd.io/_uploads/SyS08gdlWl.png)

## Bypassing Server-Side Filtering: File Extensions
![image](https://hackmd.io/_uploads/BkQKPlOe-x.png)
We can see that the code is filtering out the .php and .phtml extensions, so if we want to upload a PHP script we're going to have to find another extension. The wikipedia page for PHP gives us a few common extensions that we can try; however, there are actually a variety of other more rarely used extensions available that webservers may nonetheless still recognise. These include: **.php3, .php4, .php5, .php7, .phps, .php-s, .pht and .phar**. Many of these bypass the filter (which only blocks.php and .phtml),
 but it appears that the server is configured not to recognise them as PHP files, as in the below example:
 ![image](https://hackmd.io/_uploads/Bk6xux_gZl.png)
This is actually the default for Apache2 servers, at the time of writing; however, the sysadmin may have changed the default configuration (or the server may be out of date), so it's well worth trying.

Eventually we find that the .phar extension bypasses the filter -- and works -- thus giving us our shell:
![image](https://hackmd.io/_uploads/ry-NdedxWg.png)
Another example:

![image](https://hackmd.io/_uploads/SJbmFeuxWx.png)

## Bypassing Server-Side Filtering: Magic Numbers
- Magic numbers are the first bytes of a file, used to identify its real type (more reliable than file extensions).
- A server can read these first bytes during upload and compare them to a whitelist or blacklist of allowed file types.
- This makes file-type validation much harder to bypass compared to checking extensions or MIME types — especially on PHP servers.
However, on some other server types/frameworks, magic‑number checks may be less reliable or not implemented consistently.


- Uploading shell.php is blocked because the server is using magic number filtering 
![image](https://hackmd.io/_uploads/B17cZjrlZe.png)
- Server just eccepted GIFS file 
 ![image](https://hackmd.io/_uploads/SyQ6NjSeZx.png)
![image](https://hackmd.io/_uploads/ryeNHiHxZl.png)

- Use `hexeditor` Replace 41414141(AAAAAA) by (47 49 46 38 39 61)(gifs) and check by `file` command
 ![image](https://hackmd.io/_uploads/rJ9rrjSx-e.png)
 ![image](https://hackmd.io/_uploads/Bk7FHiSgZl.png)
![image](https://hackmd.io/_uploads/BkL2HsBxZl.png)

- Try to upload in the web and accepted by server
![image](https://hackmd.io/_uploads/SJ_yIoBebl.png)
![image](https://hackmd.io/_uploads/HJqz5iSxWl.png)
![image](https://hackmd.io/_uploads/r14EqsHebg.png)
- Use gobusster to find hidden dicrectory which contain shell
![image](https://hackmd.io/_uploads/SywRqoBlWe.png)
- But you can't access these pages beacase:
- Trên webserver, nếu directory indexing = ON, bạn có thể truy cập thư mục và xem danh sách file trong đó như:
```
http://site.com/uploads/
```
- Nhưng khi indexing = OFF, bạn sẽ thấy 403 Forbidden hoặc trang trắng.
Tức là không xem được danh sách file bên trong thư mục.
- Khi bạn upload shell (ví dụ: shell.php, hoặc shell.gif.php), bạn không thể vào thư mục uploads/ để xem nó, vì:

-> Tự đoán hoặc suy luận URL chính xác của file:
## Example Methodology
![image](https://hackmd.io/_uploads/ByrPyorgWx.png)
## Challange
![image](https://hackmd.io/_uploads/Sk4yTq_ebx.png)
![image](https://hackmd.io/_uploads/Bk6La5dxWx.png)
- Picture contain in content dic and the form of name have 3 character +jpg
![image](https://hackmd.io/_uploads/HkiZe2_g-e.png)
- We use gobuster to bruceforce with extension -x to add extension .jpg, list all of the picture in folder , and after update shell successful, we will know the name of rest file and shell file 
![image](https://hackmd.io/_uploads/BJooQhdxbe.png)


Read js file upload
![image](https://hackmd.io/_uploads/BJq6Gs_gbg.png)
```

$(document).ready(function(){let errorTimeout;const fadeSpeed=1000;function setResponseMsg(responseTxt,colour){$("#responseMsg").text(responseTxt);if(!$("#responseMsg").is(":visible")){$("#responseMsg").css({"color":colour}).fadeIn(fadeSpeed)}else{$("#responseMsg").animate({color:colour},fadeSpeed)}clearTimeout(errorTimeout);errorTimeout=setTimeout(()=>{$("#responseMsg").fadeOut(fadeSpeed)},5000)}$("#uploadBtn").click(function(){$("#fileSelect").click()});$("#fileSelect").change(function(){const fileBox=document.getElementById("fileSelect").files[0];const reader=new FileReader();reader.readAsDataURL(fileBox);reader.onload=function(event){

			//Check File Size
			if (event.target.result.length > 50 * 8 * 1024){
				setResponseMsg("File too big", "red");			
				return;
			}
			//Check Magic Number
			if (atob(event.target.result.split(",")[1]).slice(0,3) != "ÿØÿ"){
				setResponseMsg("Invalid file format", "red");
				return;	
			}
			//Check File Extension
			const extension = fileBox.name.split(".")[1].toLowerCase();
			if (extension != "jpg" && extension != "jpeg"){
				setResponseMsg("Invalid file format", "red");
				return;
			}


const text={success:"File successfully uploaded",failure:"No file selected",invalid:"Invalid file type"};$.ajax("/",{data:JSON.stringify({name:fileBox.name,type:fileBox.type,file:event.target.result}),contentType:"application/json",type:"POST",success:function(data){let colour="";switch(data){case "success":colour="green";break;case "failure":case "invalid":colour="red";break}setResponseMsg(text[data],colour)}})}})});
```
- From  Wappalyser, we know backend written from Express js , find shell suit for this technology
 ![image](https://hackmd.io/_uploads/HygO43ug-g.png)

- Download and replace ip 
![image](https://hackmd.io/_uploads/BJJxB3deZl.png)

- Bypass frontend filtering
![image](https://hackmd.io/_uploads/BkxTIn_gbl.png)
- Delete ^^js$ to intercept  js file
![image](https://hackmd.io/_uploads/H1nWwnOlbl.png)
- Delete these code if you meet status 304 lets disable cache in f12/network/disable cache
![image](https://hackmd.io/_uploads/SybXu2Ogbg.png)

![image](https://hackmd.io/_uploads/HJ38H3OxZe.png)

![image](https://hackmd.io/_uploads/ByayF3uxWx.png)
- COmpare with old result=>SPR is the shell
![image](https://hackmd.io/_uploads/Hk3LtnOebe.png)
![image](https://hackmd.io/_uploads/HJJJ93_gWg.png)
![image](https://hackmd.io/_uploads/Byzu9hdlWx.png)
