网鼎杯第二场 wafUpload
题目
<?phpm
#$sandbox = '/var/www/html/upload/' . md5("phpIsBest" . $_SERVER['REMOTE_ADDR']);
$sandbox = '';
#@mkdir($sandbox);
#@chdir($sandbox);
if (!empty($_FILES['file'])) {
#mime check
if (!in_array($_FILES['file']['type'], ['image/jpeg', 'image/png', 'image/gif'])) {
die('This type is not allowed!');
}else{
echo "pass 1n";
}
#check filename
$file = empty($_POST['filename']) ? $_FILES['file']['name'] : $_POST['filename'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}
$ext = end($file);
if (!in_array($ext, ['jpg', 'png', 'gif'])) {
die('This file is not allowed!');
}else{
echo "pass 2n";
}
$filename = reset($file) . '.' . $file[count($file) - 1];
if (move_uploaded_file($_FILES['file']['tmp_name'], $sandbox . '/' . $filename)) {
echo 'Success!';
echo 'filepath:' . $sandbox . '/' . $filename;
} else {
echo 'Failed!';
}
}
show_source(__file__);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload Your Shell</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="text" name="filename"><br>
<input type="file" name="file" id="file" />
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>
思路
审计源码可以知道,代码中用 end 函数取到上传文件的后缀并判断,用 reset 函数返回的值作为文件名
根据题目,需要绕过两层判断。
1.第一层,直接抓包修改 MIME 为 image/png 就行了。
2.第二层,构造 filename 字段为数组
仔细看 html 代码中提供了一个 filename 字段,在下面这句代码的判断中,会先查看是否有直接 post 提交的 filename 字段,如果有的话就使用这个字段的值
$file = empty($_POST[‘filename’]) ? $_FILES[‘file’][‘name’] : $_POST[‘filename’];
如果没有POST该字段,$file变量取上传时的name,即** $_FILES[‘file’][‘name’] **
若$file文件名不是数组,就对字符串中的点号. 进行explode分割,分割成数组
如上传aa.bb.php会被切为
[0] = > ‘aa’
[1] => ‘bb’
[2] => ‘php’
这样的数组
获取扩展名代码
$ext = end($file);
利用了end函数,这个函数可以返回数组的最后一项。
也就返回了最后的php作为$ext,再经过判断ext是否是jpg、png、gif的一种。
绕过
利用end reset函数的缺陷
举个例子:
<?php
$arr = array();
$arr[0] = 'first';
$arr[1] = 'second';
$arr[2] = 'third';
var_dump($arr);
echo "the result of reset: ".reset($arr)."n";
echo "the result of end: ".end($arr);
?>
end 函数原本的作用就是返回数组的最后一个元素,在上面看的是正常的。但是如果我们这里把对数组赋值的顺序换一下(先给 arr[2] 赋值),可以看到结果就变了。
继续尝试会发现 reset 函数也是一样,第一个给数组赋值的值就是 reset 函数返回的值,并不一定是arr[0]。
所以构造playload
filename[1] = php
filename[0] = png
end取的是png能通过校验
在后面拼接 $filename 时候,再一次拼接到后缀名,即
$filename = reset($file) . ‘.’ . $file[count($file) - 1];
此时$file[count($file) - 1] 取到的就是$file[2-1]->$filename[1]
最后拼接出了 php.php,就达到了上传 shell 的目的。
https://www.anquanke.com/post/id/164561#h2-1