第三十八天(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)时,查找顺序是:

  1. 先在对象自身上查找该属性,如果存在则直接返回其值。
  2. 如果自身不存在,就沿着 proto 指向的原型对象查找。
  3. 如果原型对象也没有,就继续沿着原型的 proto 向上查找(即原型链),直到找到 Object.prototype。
  4. 若整个原型链都没有该属性,则返回 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)

那么将可以影响所有和这个对象来自同一个类、父祖类的对象。