第三十八天(Node.JS)
#环境搭建-NodeJS-解析安装&库安装
0、文档参考:
Node.js 教程 | 菜鸟教程
1、Nodejs安装
Node.js — Run JavaScript Everywhere
确保输入 node 时 可以输出版本
输入npm时可以显示下面内容 就算安装完成了
2、三方库安装
express
Express是一个简洁而灵活的node.js Web应用框架
body-parser
node.js中间件,用于处理 JSON, Raw, Text和URL编码的数据。
cookie-parser
这就是一个解析Cookie的工具。通过req.cookies可以取到传过来的cookie,并把它们转成对象。
multer
node.js中间件,用于处理 enctype="multipart/form-data"(设置表单的MIME编码)的表单数据。
mysql
Node.js来连接MySQL专用库,并对数据库进行操作。
安装命令:
npm i express
npm i body-parser
npm i cookie-parser
npm i multer
npm i mysql
#功能实现-NodeJS-数据库&文件&执行
1、Express开发
2、实现用户登录
3、加入数据库操作
先安装mysql 库
npm install mysql
连接数据库和对数据库内容进行查询,这里是查询admin表中 id=1 的数据
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : '654321',
database : 'phpstudy'
});
connection.connect();
var sql = 'SELECT * FROM admin where id=1';
connection.query(sql, function (error, results, fields) {
if (error) throw error;
console.log(results[0]);
});
将id值改成2
将id接收值改成一个可控变量,尝试用sql注入语句测试执行
var express =require('express');
var app = express();
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : '654321',
database : 'phpstudy'
});
connection.connect();
app.get('/sql', function (req, res) {
var id = req.query.id;
var sql = 'SELECT * FROM admin where id='+id;
connection.query(sql, function (error, results, fields) {
if (error) {throw error;
console.log('[SELECT ERROR] -',error.message);
return;}
console.log('[SELECT OK] -',results);
res.send(results);
});
});
var server = app.listen(8032, function () {
var host = server.address().address
var port = server.address().port
console.log("应用实例,访问地址为 http://%s:%s", host, port)
});
正常输入参数值
输入sql 语句,获取参数显示位置
在显示位置中输入sql语句,获取数据库名和用户名
-文件操作
1、Express开发
2、实现目录读取
3、加入传参接受
先创建一个1.txt文件,往里面输入一些内容
再创建一个js文件,写入下面代码
右键运行该文件,可以看到,这里的console 显示的内容是在程序里面显示而不是在浏览器中的控制台中,这里也说明node.js 可以相当于浏览器
这里访问js文件时不会运行里面的代码,而是直接输出了
如何在node.js中启动网站?如何让node.js运行在网站中?
先安装一个express库
先引入express 库 ,写一个端口监听的,然后启动这个js
在网页中访问这个js文件,网页中可以看到 并未执行上面的读取文件的操作,因为这里要加一个路由关系,比如:访问某个路径时执行这个函数
将要在访问某个地址时执行的代码放在函数里面,这样一访问地址时就会触发,从而执行里面的代码
这里刷新一次就会打印一次数据,但是这里还是将数据显示在js运行框里
如何让代码执行结果输出到浏览器中呢?
只需要加一句代码就可以将数据内容显示到浏览器上面去 ,输入 :res.send(data); 这个res是和上面那个有关的,所以当上面的名字改变时,那么下面的也要跟着改动
接收输入参数 req.query.x 将接收过来的值赋值给name ,res.send(name); 将那么值在浏览器中输出出来
其实req就是接收输入的内容,res就是 返回值
那么将文件名改成一个可控变量不就可以实现任意文本读取,这里用get请求方式
app.get('/file', function (req, res) {
var name = req.query.file;
// res.send(name);
fs.readFile(name, 'utf-8', function (err, data) {
if(err) throw err;
console.log(data);
res.send(data);
});
})
这里在js文件上级创建了一个1.txt文件,将参数值file 加一个../ 跳到上一级,读取1.txt文件,执行成功
这里是读取不了php文件,但是可以读取js文件,应该是不支持读取其他类型文件
将文件读取改成目录读取,用post提交方式请求
app.post('/dir', function (req, res) {
var name = req.query.dir;
// res.send(name);
fs.readdir(name, 'utf-8', function (err, data) {
if(err) throw err;
console.log(data);
res.send(data);
});
})
打开postman 选择post 提交方式, 将变量名和值输入到网址去,下面就会根据输入内容,将内容以post方式发送
这里就遍历 到了当前目录下的所有文件
遍历上级目录下的所有文件
这里右键源代码是查看不到任何代码的,只能看到执行结果,和之前原生开发时可以看到前端的js代码完全相反
那么可以看到用了node.js 后 ,它既有前端的效果,又有后端的隐蔽性,同时 还是一个运行JavaScript的环境,不用搭建apache,也可以运行到web应用中
-命令执行(RCE)
1、eval
2、exec & spawnSync
先安装一个 child_process
执行命令测试: exec & spawnSync
const child_process = require('child_process');
child_process.exec('calc');
代码执行测试: eval
#NodeJS安全
1、SQL注入&文件操作
2、RCE执行&原型链污染
RCE执行
构建网站:
const child_process = require('child_process');
var express =require('express');
var app = express();
// 命令执行
// child_process.exec('calc');
// child_process.spawnSync('calc');
// 代码执行
// eval('child_process.exec(\'calc\');');
app.get('/rce', function (req, res) {
const cmd = req.query.cmd; //定义一个变量接收参数值
child_process.exec(cmd);
})
var server = app.listen(8032, function () {
var host = server.address().address
var port = server.address().port
console.log("应用实例,访问地址为 http://%s:%s", host, port)
});
将cmd值输入 calc ,成功弹出计算器
原型链污染
原理:在一个应用中,如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象。这种攻击方式就是原型链污染。
当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象(object)都有一个私有属性(称之为 proto )指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(proto),层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。
意思:原型链污染是只要污染了object对象中的proto 就相当于把所有的对象都污染了,因为object是所有对象的顶级父类;
举例:
在 JavaScript 中,当访问对象的某个属性(如 obj.prop)时,查找顺序是:
- 先在对象自身上查找该属性,如果存在则直接返回其值。
- 如果自身不存在,就沿着 __proto__ 指向的原型对象查找。
- 如果原型对象也没有,就继续沿着原型的 __proto__ 向上查找(即原型链),直到找到 Object.prototype。
- 若整个原型链都没有该属性,则返回 undefined。
这里foo 的第二次输出bar值时仍是1,因为查询规则 先查 foo 自身,发现有 bar: 1,直接返回;
foo.__proto__.bar = 2; //foo.__proto__ 指向 Object.prototype(因为 foo 是普通对象,原型是 Object.prototype)
这行代码实际是给 Object.prototype 添加了一个属性 bar,值为 2。
新建的zoo 是一个空对象,自身没有 bar 属性
zoo 的 __proto__ 同样指向 Object.prototype(所有普通对象的原型都是 Object.prototype) 所以会输出2
// 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,此时bar为2
console.log(zoo.bar)
原型链污染配合RCE
有原型链污染的前提之下,我们可以控制基类的成员,赋值为一串恶意代码,从而造成代码注入。
运行代码之后会弹出计算器
let foo = {bar: 1}
console.log(foo.bar)
foo.__proto__.bar = 'require(\'child_process\').execSync(\'calc\');'
console.log(foo.bar)
let zoo = {}
console.log(eval(zoo.bar))
效果
2、NodeJS黑盒无代码分析
实战测试NodeJS安全:
判断:参考前期的信息收集
黑盒:通过对各种功能和参数进行payload测试
白盒:通过对代码中写法安全进行审计分析
-原型链污染
如果攻击者控制并修改了一个对象的原型,(proto)
那么将可以影响所有和这个对象来自同一个类、父祖类的对象。