Prevent Malicious File Upload in ASP.NET

 

A very few day ago i had to face the situation where hacker uploaded the malicious ASP script to the web server by changing its extension to .JPG through user interface which allow user to upload image file. Although developer team had put the validation on extension. But unfortunately extension of ASP script was .JPG and hence it is allowed extension. So hacker could upload that malicious script.

After this situation i just checking only extension for uploaded file is not the sufficient. We need to check content as well of the uploaded file.
Image File Header Information Table

Image File Header Information Table

File Format

Offset

Length

Value

JPG / JPEG

0

4

0xFF, 0xD8, 0xFF, 0xE0

PNG

0

4

0x89, 0x50, 0x4E, 0x47

TIF / TIFF

0

4

0x49, 0x49, 0x2A, 0x00

GIF

0

4

0x47, 0x49, 0x46, 0x38

BMP

0

2

0x42, 0x4D

ICO

0

4

0x00, 0x00, 0x01, 0x00

Instead of checking only header, we could also check whole file content against its file format. But checking only header could serve our purpose and it is also speedy process than checking whole file content so i am not checking whole file content.
Following code snippet validate header of known image types (JPG, PNG, TIFF, GIF, BMP, ICO) Please do let me know if i have missed any image types.

NOTE: In code snippet fuImage refer to ASP.NET file upload control
protected void btnUpload_Click(object sender, EventArgs e)
{
    // DICTIONARY OF ALL IMAGE FILE HEADER
    Dictionary<string, byte[]> imageHeader = new Dictionary<string, byte[]>();
    imageHeader.Add("JPG", new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 });
    imageHeader.Add("JPEG", new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 });
    imageHeader.Add("PNG", new byte[] { 0x89, 0x50, 0x4E, 0x47 });
    imageHeader.Add("TIF", new byte[] { 0x49, 0x49, 0x2A, 0x00 });
    imageHeader.Add("TIFF", new byte[] { 0x49, 0x49, 0x2A, 0x00 });
    imageHeader.Add("GIF", new byte[] { 0x47, 0x49, 0x46, 0x38 });
    imageHeader.Add("BMP", new byte[] { 0x42, 0x4D });
    imageHeader.Add("ICO", new byte[] { 0x00, 0x00, 0x01, 0x00 });
    byte[] header;
    if (fuImage.HasFile)
    {
        // GET FILE EXTENSION
        string fileExt;
        fileExt = fuImage.FileName.Substring(fuImage.FileName.LastIndexOf('.') + 1).ToUpper(); 
        // CUSTOM VALIDATION GOES HERE BASED ON FILE EXTENSION IF ANY      
        byte[] tmp = imageHeader[fileExt];
        header = new byte[tmp.Length];
        // GET HEADER INFORMATION OF UPLOADED FILE
        fuImage.FileContent.Read(header, 0, header.Length);
        if (CompareArray(tmp, header))
        {
            lblMessage.Text = "Valid ." + fileExt + " file.";
            // VALID HEADER INFORMATION 
            // CODE TO PROCESS FILE
        }
        else
        {
            lblMessage.Text = "Invalid ." + fileExt + " file.";
            // INVALID HEADER INFORMATION
        }
    }
    else
    {
        lblMessage.Text = "Please select image file.";
    }
} 
private bool CompareArray(byte[] a1, byte[] a2)
{
    if (a1.Length != a2.Length)
        return false;
    for (int i = 0; i < a1.Length; i++)
    {
        if (a1[i] != a2[i])
            return false;
    }
    return true;
}
Any input on above is greatly appreciated...

Post a Comment

0 Comments