# Code Review: bWAPP Unrestricted File Upload

## Source Code

As an example, we are going to do a code review on bWAPP "Unrestricted File Upload" challenges:

{% embed url="<https://github.com/ret2basic/bWAPP-Source-Code/blob/master/bWAPP/unrestricted_file_upload.php>" %}
bWAPP Source Code
{% endembed %}

## Security Level 0

In this level, there is no filter at all:

```php
        case "0" : 
            
            move_uploaded_file($_FILES["file"]["tmp_name"], "images/" . $_FILES["file"]["name"]);
            
            break;
```

We can simply upload a PHP webshell with names like `webshell.php` and this webshell can be found at `images/`.&#x20;

## Security Level 1

In this level, the filter used is called `file_upload_check_1`:

```php
        case "1" :
            
            $file_error = file_upload_check_1($_FILES["file"]);
            
            if(!$file_error)
            {
                
                move_uploaded_file($_FILES["file"]["tmp_name"], "images/" . $_FILES["file"]["name"]);
    
            }            
            
            break;
```

This function is defined in `functions_external.php`:

```php
function file_upload_check_1($file, $file_extensions  = array("asp", "aspx", "dll", "exe", "jsp", "php"), $directory = "images")
{
    
    $file_error = "";
    
    // Checks if the input field is empty
    if($file["name"] == "")
    {
        
        $file_error = "Please select a file...";
        
        return $file_error;
        
    }
    
    // Checks if there is an error with the file
    switch($file["error"])
    
    // URL: http://php.net/manual/en/features.file-upload.errors.php
    
    {
        
        case 1 : $file_error = "Sorry, the file is too large. Please try again...";
                 break;
             
        case 2 : $file_error = "Sorry, the file is too large. Please try again...";
                 break;
             
        case 3 : $file_error = "Sorry, the file was only partially uploaded. Please try again...";
                 break;
             
        case 6 : $file_error = "Sorry, a temporary folder is missing. Please try again...";
                 break;
             
        case 7 : $file_error = "Sorry, the file could not be written. Please try again...";
                 break;
             
        case 8 : $file_error = "Sorry, a PHP extension stopped the file upload. Please try again...";
                 break;
             
    }
    
    if($file_error)
    {
        
        return $file_error;
        
    }
    
    // Breaks the file in pieces (.) All pieces are put in an array
    $file_array = explode(".", $file["name"]);
    
    // Puts the last part of the array (= the file extension) in a new variabele
    // Converts the characters to lower case
    $file_extension = strtolower($file_array[count($file_array) - 1]);
    
    // Searches if the file extension exists in the 'allowed' file extensions array   
    if(in_array($file_extension, $file_extensions))
    {
        
       $file_error = "Sorry, the file extension is not allowed. The following extensions are blocked: <b>" . join(", ", $file_extensions) . "</b>";
       
       return $file_error;
       
    }
    
    // Checks if the file already exists in the directory
    if(is_file("$directory/" . $file["name"]))
    {
        
        $file_error = "Sorry, the file already exists. Please rename the file...";      
        
    }
    
    return $file_error;
    
}
```

This implementation uses **blacklist** to filter out unwanted file extensions. Specifically, the following file extensions are blocked:

* asp
* aspx
* dll
* exe
* jsp
* php

Of course this implementation is not sufficient at all. For example, we can try things like php3/php4/php5 as file extension and bypass the check.

## Security Level 2

In this level, the filter used is called `file_upload_check_2`:

```php
        case "2" :            
                       
            $file_error = file_upload_check_2($_FILES["file"], array("jpg","png"));
            
            if(!$file_error)
            {
                
                move_uploaded_file($_FILES["file"]["tmp_name"], "images/" . $_FILES["file"]["name"]);
    
            }            
            
            break;
```

This function is defined in `functions_external.php`:

```php
function file_upload_check_2($file, $file_extensions  = array("jpeg", "jpg", "png", "gif"), $directory = "images")
{
    
    $file_error = "";
    
    // Checks if the input field is empty
    if($file["name"] == "")
    {
        
        $file_error = "Please select a file...";
        
        return $file_error;
        
    }
    
    // Checks if there is an error with the file
    switch($file["error"])
    
    // URL: http://php.net/manual/en/features.file-upload.errors.php
    
    {
        
        case 1 : $file_error = "Sorry, the file is too large. Please try again...";
                 break;
             
        case 2 : $file_error = "Sorry, the file is too large. Please try again...";
                 break;
             
        case 3 : $file_error = "Sorry, the file was only partially uploaded. Please try again...";
                 break;
             
        case 6 : $file_error = "Sorry, a temporary folder is missing. Please try again...";
                 break;
             
        case 7 : $file_error = "Sorry, the file could not be written. Please try again...";
                 break;
             
        case 8 : $file_error = "Sorry, a PHP extension stopped the file upload. Please try again...";
                 break;
             
    }
    
    if($file_error)
    {
        
        return $file_error;
        
    }
    
    // Breaks the file in pieces (.) All pieces are put in an array
    $file_array = explode(".", $file["name"]);
    
    // Puts the last part of the array (= the file extension) in a new variabele
    // Converts the characters to lower case
    $file_extension = strtolower($file_array[count($file_array) - 1]);
    
    // Searches if the file extension exists in the 'allowed' file extensions array   
    if(!in_array($file_extension, $file_extensions))
    {
        
       $file_error = "Sorry, the file extension is not allowed. Only the following extensions are allowed: <b>" . join(", ", $file_extensions) . "</b>";
       
       return $file_error;
       
    }
    
    // Checks if the file already exists in the directory
    if(is_file("$directory/" . $file["name"]))
    {
        
        $file_error = "Sorry, the file already exists. Please rename the file...";      
        
    }
    
    return $file_error;
    
}
```

This implementation uses **whitelist** to only allow wanted file extensions. Specifically, the following file extensions are accepted:

* jpeg
* jpg
* png
* gif

This implementation looks sufficient at first, but if you dig deeper into the function calls, you will find this line of code:

```php
if(!in_array($file_extension, $file_extensions))
{
    ...
}
```

Take a look at the PHP manual:

{% embed url="<https://www.php.net/manual/en/function.in-array.php>" %}
in\_array - PHP Manual
{% endembed %}

The nuance is that the `in_array()` function takes 3 arguments but this code only used 2. The third argument `bool $strict` is set to `false` by default, which indicates **loose comparison**. PHP loose comparison is a weird "feature" and it is the source of many dumb vulnerabilities. For example:

```php
$values = array("apple", "orange", "pear", "grape");
var_dump(in_array(0, $values));
```

You may think the output will be `bool(false)`, but no, the output is `bool(true)`! This is because in loose comparison, we have `0 == "apple"`. In fact, every string started with letter is evalated to 0 in loose comparison. I am speechless.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ret2basic.gitbook.io/ctfnote/web/file-upload/code-review-bwapp-unrestricted-file-upload.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
