AoC 2022 Day 15: Secure coding

Input Validation

One of the major security problems for web applications is insufficient input validation. When user input is implicitly trusted by the application, a problem arises. Given that user input can potentially be manipulated by an attacker, it is clear how this inherent trust might result in a wide range of issues. The problem of inadequate user input validation leads to a number of web application vulnerabilities, including SQL Injection, Cross Site Scripting, and Unrestricted File Upload.

The Unrestricted in Unrestricted File Uploads

The ability to upload files to a server has become integral to how we interact with web applications. Just think of file uploads like a profile picture for a social media website, a report being uploaded to cloud storage, or saving a project on GitHub; the applications for file upload features are limitless.

Unfortunately, poorly managed file uploads could put the system at considerable risk. If an attacker uploads and runs a shell, the results could range from relatively small annoyances to complete Remote Code Execution (RCE). If an attacker had unrestricted upload access to a server, they might alter or alter existing content (and the freedom to retrieve data whenever they wanted). Additionally, they might insert harmful URLs to create new security gaps like Cross-Site Scripting (XSS) or Cross-Site Request Forgery (CSRF). By uploading arbitrary files, an attacker might use the server to host and/or offer illegal content, reveal sensitive information, or both. In all likelihood, an attacker with access to your server could upload any file they wanted, with no restrictions is very dangerous indeed.

Unrestricted File Uploads usually have two main exploitation paths:

  • If the attacker can retrieve the uploaded file, it could lead to code execution if the attacker uploads a file such as a web shell.
  • If the file is viewed by a user, think of a CV application, then an attacker could embed malware in the uploaded file that would execute on the user’s workstation once they view the file.


As we can see, the website allows us to upload our CVs to apply for a job in Santa’s security team. Before we start the actual testing, let’s first try to use the website as intended to get a better understanding of what is happening. Using your favorite word editor, create a simple new PDF and upload it to the application. Once uploaded, we can see the following message:


Interesting! The message also tells us that there will be human (or elf, more specifically) interaction with the file. This calls for further investigation!

Let’s see what happens when we try to upload something other than a PDF, like an executable. You can rename the file extension of your CV to EXE:


That seems to work! It seems like the freelance developer has attempted to implement some security controls to prevent naughty elves. However, not all controls were implemented. But can we actually do something with this that will be malicious?

Why the fuss over the Web Root?

So why would the developer take care to store the file outside the web root? Web servers are fairly simple things. You request a resource and the web server then responds with the resource. The magic happens when a request resource has a specific file type. Since this is a .NET application, the following resource types would be considered special:

  • ASP
  • ASPX

When a resource is requested with one of these file types, the web server will first execute some instructions found in these pages before sending the compiled response back to the user. Here is an example of an ASPX instruction that would be executed by the server:

<form id="Form1" method="post" runat="server" EncType="multipart/form-data" action="Upload.aspx">

This tells the server that the HTML element first requires some formatting before being added to the HTML in the response, as can be seen by therunat="server" directive.

If we could upload any file that we wanted, we could upload one of these special types of pages, like an ASPX webshell. If this file was stored in the web root, we could request the file from the server, forcing the server to execute the code within our file before sending the response. It would be possible to get remote code execution on the web server. However, as the file is stored outside of the web root, we cannot make a request that would retrieve our uploaded file for execution. However, this protection is not sufficient for two main reasons:

  • Other vulnerabilities, such as local file inclusion, may exist that allow us to force the web server itself to recover the file that was stored outside the web root. If the web server recovers the file, the code within the file will again be executed, allowing for RCE.
  • While we cannot perhaps get RCE using this vulnerability, we know with certainty that actual users would interact with the files that we upload. Rather than targeting the web server directly, we could target the users that would interact with these files. If we were to upload a file that had malware embedded, it would execute when the user interacted with the file, still allowing us to breach Santa’s perimeter!


File Content Validation

We can validate the content of the file by reviewing the ContentType header that is sent by the browser when a file is uploaded.

If the file content type is not PDF, we will reject the file. While this is a good control, it should be noted that since the browser sets this header, an attacker could bypass this control by intercepting the request and changing the header.


File Extension Validation

We can also verify the file extension. This will allow us to limit the type of files that can be uploaded.

This will limit the file types to only PDFs. If a user’s CV is in a different format, they will have to convert their CV first. This control should ideally be implemented with an allowlist, as shown above, since a blocklist can still be bypassed in certain cases.


File Size Validation

Attackers can also try to upload files that are so large it consumes so much disk space that other potential candidates are not able to upload their CVs. To prevent this, we can implement file size validation controls.

This will only allow file sizes smaller than the specified amount.


File Renaming

As mentioned before, even though our uploads are stored outside the web root, an attacker could leverage an additional vulnerability, such as file inclusion, to execute the file. To counter these attempts, we can look to rename uploaded files to random names, making it almost impossible for an attacker to recover their file by name.

Guid id = Guid.NewGuid();
var filePath = Path.Combine(fullPath, id + ".pdf");

Malware Scanning

Even with all of the above controls implemented, there is still the risk of an attacker uploading a malicious file that targets the elves that will review the CVs. Since Santa is a high-value individual, some nation-states might even use specialised exploits found in PDF readers to upload a malicious PDF in the hopes of getting access to remove themselves from Santa’s naughty list! In order to combat these types of malicious files, we can scan uploaded files for malware.


Putting it all Together

Combining all of the above techniques, we can implement a secure file upload function, as shown below:

public IActionResult OnPostUpload(FileUpload fileUpload)
        var allowed = True;

        //Store file outside the web root   
        var fullPath = "D:\CVUploads\"

        var formFile = fileUpload.FormFile;

        //Create a GUID for the file name
        Guid id = Guid.NewGuid();
        var filePath = Path.Combine(fullPath, id + ".pdf");

        //Validate the content type
        string contentType = fileUpload.ContentType.Split('/')[1].ToLower();
        if !(contentType.equals("ContentType=PDF")
                allowed = False;

       //Validate the content extension
       string contentExtension = Path.GetExtension(fileUpload);
       if !(contentExtension.equals("PDF"))
               allowed = False;

       //Validate the content size
       int contentSize = fileUpload.ContentLength;
       //10Mb max file size
       int maxFileSize = 10 * 1024 * 1024
       if (contentSize > maxFileSize)
               allowed = False;

       //Scan the content for malware
       var clam = new ClamClient(this._configuration["ClamAVServer:URL"],Convert.ToInt32(this._configuration["ClamAVServer:Port"])); 
       var scanResult = await clam.SendAndScanFileAsync(fileBytes);  
       if (scanResult.Result == ClamScanResults.VirusDetected)
                allowed = False;

       //Only upload if all checks are passed
       if (allowed)
            using (var stream = System.IO.File.Create(filePath))

All of these controls are required for the simple reason that we cannot inherently trust user input. As such, user input must be validated and controlled! Sending this feedback to the freelance developer will allow them to secure the file upload feature!

~ Source: Tryhackme.

Challenge Solution