第二届帕鲁杯web复现

参考文章:https://blog.csdn.net/giaogiaohuohua/article/details/148055593

CatBank

先注册一个账号a

image-20250521190819353

根据这个提示可知要余额为一百万,可以进行转账,另外注册一个账户b,用账户a给账户b转账一百万

image-20250521191105882

然后登录账户b得到flag,登录后可以再转账一次,就可以得到flag了

image-20250521191207690

猫猫的秘密

查看网页源码

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
<script>
let token = '';

document.getElementById('loginBtn').addEventListener('click', async () => {
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;

try {
const response = await fetch('/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username, password })
});

const data = await response.json();

if (response.ok) {
token = data.token;
document.getElementById('loginResult').textContent = `登录成功: ${data.message}`;
document.getElementById('loginResult').classList.remove('hidden');
document.getElementById('loginSection').classList.add('hidden');
document.getElementById('secretSection').classList.remove('hidden');
} else {
document.getElementById('loginResult').textContent = `错误: ${data.error}`;
document.getElementById('loginResult').classList.remove('hidden');
}
} catch (error) {
document.getElementById('loginResult').textContent = `发生错误: ${error.message}`;
document.getElementById('loginResult').classList.remove('hidden');
}
});

document.getElementById('getSecretBtn').addEventListener('click', async () => {
try {
const response = await fetch('/get_secret', {
method: 'GET',
headers: {
'Authorization': token
}
});

const data = await response.json();

if (response.ok) {
let resultText = `${data.message}\n\n`;

if (data.public) {
resultText += `${data.public}\n\n`;
}

if (data.confidential) {
resultText += `猫猫信息: ${data.confidential}\n\n`;
}

if (data.flag) {
resultText += `Flag: ${data.flag}`;
}

document.getElementById('secretResult').textContent = resultText;
} else {
document.getElementById('secretResult').textContent = `错误: ${data.error}`;
}

document.getElementById('secretResult').classList.remove('hidden');
} catch (error) {
document.getElementById('secretResult').textContent = `发生错误: ${error.message}`;
document.getElementById('secretResult').classList.remove('hidden');
}
});

document.getElementById('logoutBtn').addEventListener('click', () => {
token = '';
document.getElementById('username').value = '';
document.getElementById('password').value = '';
document.getElementById('loginResult').classList.add('hidden');
document.getElementById('secretResult').classList.add('hidden');
document.getElementById('secretSection').classList.add('hidden');
document.getElementById('loginSection').classList.remove('hidden');
});
</script>

发现存在一个/get_secret路由,存在有Authorization进行认证,测试后发现是JWT认证,伪造成admin去访问

image-20250522111905489

但是会得到Invalid token: Invalid algorithm报错

image-20250522111932989

JWT存在一个CVE漏洞

在JWT的Header中alg的值用于告诉服务器使用哪种算法对令牌进行签名,从而告诉服务器在验证签名时需要使用哪种算法,目前可以选择HS256,即HMAC和SHA256,JWT同时也支持将算法设定为”None”,如果”alg”字段设为”None”,则标识不签名,这样一来任何token都是有效的

所以这里可以将alg部分设置为None绕过

JWT网站:https://jwt.io/

image-20250522112129697

就会得到如下信息

image-20250522112159988

十六进制转化后是

1
2
3
4
{
"message":"欢迎 admin",
"public":"喵喵喵?你想看什么捏"
}

权限还是不够获得flag,继续构造jwt的payload部分,常见的权限字符串有:roleaccountisAdmin等,经过尝试后发现是role

image-20250522113032964

image-20250522113046029

CatNet

dirsearch可以扫出/admin路由

image-20250521193326629

访问会看到有IP,想到伪造XFF

image-20250521193641312

1
X-Forwarded-For: 127.0.0.1

image-20250521194020766

看源码,会发现有authentication 认证,是X-Internal-Authgetflag是访问/admin/flag

image-20250521194505342

直接bp抓包

image-20250521194926072

放到Intruder中爆破出正确的X-Internal-Auth,手动加上这个请求头与payload

image-20250521195049079

成功爆破出是123

image-20250521195228885

ezblog

使用反编译工具查看附件源码,在dashboard.class中看到有一个/backdoor路由,满足传参key等于正确的key即可获取flag

image-20250521201332033

然后是app.class文件,这里设置了assets的目录为/app/assets

image-20250521202235881

点击FileStaticRepository跳转到FileStaticRepository.class文件

image-20250521202631294

可以看到这段代码

1
2
3
4
5
6
7
8
9
public URL find(String relativePath) throws Exception {
File file = new File(location, relativePath);

if (file.exists()) {
return file.toURI().toURL();
} else {
return null;
}
}

在上一个文件知道了assets的目录为/app/assets,如果在后面添加../../etc/passwd,最后就会得到/etc/passwd

1
2
3
4
# 访问网站
http://challenge.qsnctf.com:31555/assets/../../etc/passwd
# 返回内容
http://challenge.qsnctf.com:31555/etc/passwd

访问/assets/..可以得到上级目录文件,会发现有个app.jar文件,这就是真正的app.jar文件

image-20250522104806394

通过访问/assets/../app.jar,就可以获取/app下的app.jar文件

先抓包修改成访问/assets/../app.jar

image-20250522105757109

改好之后直接放包,就可以下载到app.jar文件了

image-20250522105903757

将下载得到的文件用工具反编译打开,可以在dashboard.class找到realkeyNlRVANA@007

image-20250522105949967

最后就是访问/backdoor路由,然后GET传参keyNlRVANA@007

1
/backdoor?key=NlRVANA@007

image-20250522110141998

CatShell

我太菜了,不会做。。。


第二届帕鲁杯web复现
https://yschen20.github.io/2025/05/22/第二届帕鲁杯web复现/
作者
Suzen
发布于
2025年5月22日
更新于
2025年6月10日
许可协议