大佬的wp:WEB:Wife_wife-CSDN博客
知识点:
- prototype是new class 的一个属性,即__proto__指向new class 的prototype属性
- __proto__如果作为json代码解析的话会被当成键名处理,但是如果是在类中的话则会被当成子类的原型
- 如let os=json.parse('{"a":"nihao","__proto__":{"isAdmin":true}}');
- json.parse相当于创建一个对象,解析过程中__proto__作为键名处理;相当于os对象有两个键:a和__proto__
- let os={"a":"nihao","__proto__":{"isAdmin":true}};
- 这里__proto__相当于os本身,内部键名:a和isAdmin
- 如let os=json.parse('{"a":"nihao","__proto__":{"isAdmin":true}}');
- Object.assign()
- 利用该函数可以拷贝资源属性的所有键名和值到目标中
-
Object.assign(target, ...sources)
- 部分源码分析
-
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 }) })
- 源码中需要绕过一个点:if (user.isAdmin && user.inviteCode != INVITE_CODE);他只要检测到isAdmin=true就会强制改为false,除非给出了正确的inviteCode,而想要拿到真正的flag必须保证isAdmin=true才行,下面有一个Object.assign()函数,是将源类的键名和值都拷贝到目标中,即新建的对象中拷贝过程中__proto__会表示子类原型,不作为键名处理,真实拷贝键名为isAdmin值为true,json.parse()中__proto__作为键名处理,导致后面直接绕过了isAdmin的判断,相当于没有该键名,默认为false
-
- 原链污染详解:深入理解 JavaScript Prototype 污染攻击 | 离别歌 (leavesongs.com)
- 污染代码必须在json解析之前输入,即__proto__必须作为键名处理才能实现污染攻击
- 留意merge clone 等函数
通关载荷:
抓取注册is_Admin=true的数据包,构造对应载荷:
payload:{"username":"ren2","password":"123","__proto__":{"isAdmin":true},"inviteCode":"1"}
成功注册了账号,这里直接登进去就拿到flag了
如果只是注册了普通账号的话,只能看到虚假的flag和一张图片,没有任何信息
这题可太整人了,普通账号进入后,我还以为图片中会有一些信息呢,做了半天的杂项发现图片就真的是图片呀,扫了一下上面的那张小图nilou1.jpg,发现竟然是个jphide加密的,欸,难道有什么隐藏的信息???搞了半天找不到密码,无法进行解码,看了大佬的wp才发现我是彻底被作者给打败了,思路被诱导了,原来是要绕过isAdmin检测,即使知道了也不会,基础太差了,原链污染的概念也是第一次听,总的来说还是有所收获的,一边被虐一边学习,痛并快乐着
用一张图来表达我的心情
10