【web | CTF】攻防世界 wife_wife

目录

步骤1:峰回路转

步骤2:JavaScript原型链污染

步骤3:fuzz测试

总结:题目意义


步骤1:峰回路转

【web | CTF】攻防世界 wife_wife_第1张图片

对英语无感的我一开始没看到有个注册的提示,还以为暴力破解,看了一下题目才知道不需要暴力破解

来到注册页面 ,就发现有点吊了,is admin可以勾选,居然能注册管理员,有点搞笑,不切实际,正经实战的话哪来这功能?

那么怀疑点其实就应该放在这里,毕竟有管理员权限相当于无敌,通常逻辑都是要靠管理员权限才能拿到flag

【web | CTF】攻防世界 wife_wife_第2张图片

 第一次没勾选,直接注册,然后登录,就拿到了flag,去试了试flag

【web | CTF】攻防世界 wife_wife_第3张图片

 【web | CTF】攻防世界 wife_wife_第4张图片

 flag是假的,被骗了,果然需要去怼admin才行

步骤2:JavaScript原型链污染

后来也没做出来,去看了别人的wp才知道是JavaScript原型链污染攻击

造成这个漏洞的有一个很重要的前提,这题目的后端是node.js来写的,也就是说后端不是php服务器,不是java服务器,而是JavaScript服务器

因为是JavaScript语言来处理后端+用JavaScript的那个函数来处理,所以才有这个漏洞产生

JavaScript原型链污染详细学习链接在此:JavaScript 原型链污染 | Drunkbaby's Blog

我们来看最重要的部分,如果是在代码中正常生成的对象,是没有污染了

baseUser = {
    a:1
}
user = {
    a:2,
    b:1,
    __proto__:{
        c:3
    }
}

// 这个函数的作用:浅复制一个对象,第一个参数位是对象的内容,后面的参数位是多个对象内容叠加进去,进行复制出一个全新的对象
let newUser = Object.assign({}, baseUser, user)  
// 无污染,结果正常
console.log(newUser)  // {a: 2, b: 1}  
// 无污染,结果正常
console.log(newUser.__proto__)  // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}

可问题是,后端服务器是JavaScript,我们通过post发送过去的 json是字符串,JavaScript需要通过JSON.parse()函数才能把 json字符串转成对象

baseUser = {
    a:1
}

// 这次使用函数把json字符串转成对象,就出问题了,
user = JSON.parse('  {"a" : 2 , "b" : 3 , "__proto__" : { "c" : 4 }}  ')
// 这个函数的作用:浅复制一个对象,第一个参数位是对象的内容,后面的参数位是多个对象内容叠加进去,进行复制出一个全新的对象
let newUser = Object.assign({}, baseUser, user)


console.log(newUser)  // {a: 2, b: 1}  ,__proto__是隐藏属性,是不会直接显示的
console.log(newUser.__proto__) // {c: 4}  ,但是一打印就出来了
console.log(newUser.c) // 4  ,已经被污染了,这个属性是一直存在的

大家可以自行复制粘贴去试试

步骤3:fuzz测试

看wp源代码才知道,这个后端是没有数据库的,sql注入就别想了

我们来看看代码讲什么,每一行我都写注释了

// 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里,如果相同的username就是重复用户名了
    if (users.filter(u => u.username == user.username).length) {  
        return res.json({ msg: 'username already exists', err: true })
    }
    // isAdmin是否true 与 邀请码是不是等于这个常量,所以sql注入没用,邀请码是个常量
    if (user.isAdmin && user.inviteCode != INVITE_CODE) {
        user.isAdmin = false
        return 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,那就证明是一个管理员,如果是普通注册的用户,isAdmin属性是false

那么直接污染即可

【web | CTF】攻防世界 wife_wife_第5张图片

【web | CTF】攻防世界 wife_wife_第6张图片

 注册了管理员账号后,进去之后的flag也变了,这次flag才是真的

 但这在比赛的时候是没有源码给你看的,所以应该是相当的难,需要fuzz测试

总结:题目意义

我暂时没搞明白这题目意义何在,好像跟实战有点偏离的太远了,用JavaScript服务器来处理登录?账号密码不存放数据库?存缓存里?能让所有人注册管理员账号?太扯淡了

你可能感兴趣的:(CTF,前端)