Skip to main content

Node.js Malware created by pkg project

1. Overview

JavaScript is one of the most used programming languages, and Node.js is also used widely as execution environment. However, Node.js applications cannot be run without Node.js runime, so using them as malware requires a little effort. For this reason, pkg project that convert NodeJS applications to EXE file or other executable formats can be the attractive tool for attackers. This article describes tips for analyzing Node.js malware created by pkg project.

2. Sample

Node Stealer
SHA256: d6aee63ffe429ddb9340090bff2127efad340240954364f1c996a8da6b711374

3. pkg project

pkg project packs js files and runtime into a single file. This executable file created is structured as follows. The custom runtime has the capability to execute js files stored in the virtual file system and downloaded from following repositories.

Repositories URL : https://github.com/vercel/pkg-fetch

The pkg project adds virtual files and index to the back of the runtime. Furthermore, the runtime has the placeholder that stores the offset of the virtual file as follows. Patching this placeholder(PAYLOAD_POSITION) makes it possible for the custom runtime to find out the location of the virtual file system.

The virtual file system index is json format as follows. A virtual file and folder has multiple objects, with offsets and data lengths stored for each object. For example, the data of object type 0 in app.js starts from PAYLOAD_POSITION+0 and its data length is 120648.

{
  "C:\\snapshot\\t2\\app.js": {
    "0": [       //Object type
      0,          // Offset
      120648  //File size
    ],
    "1": [
      120648,
      59506
    ],
    "3": [
      180154,
      121
    ]
  },
...
  "C:\\snapshot\\t2\\node_modules\\minipass-fetch\\lib": {
    "2": [
      29042524,
      106
    ],
    "3": [
      29042630,
      117
    ]
  },
...
Details of each type are shown in the table. Type 0 is only js file's object. This is data compiled from js file into format that can be executed by v8 JavaScript engine. Depending on the options, js file may only have type 0 objects and not type 1 objects.
Type 1 object is raw file data and type 2 object is list of stored files for directories. Type 3 is metadata for pkg, with no interesting data.

Object TypeDescription
0V8 code cache
1File data
2Directory
3Meta data

4. Malware analysis

The executable file created by pkg project has the following surface information. This is information that the custom runtime has, and it is rare for attacker to modify or delete it.

Now that I know this sample was created by pkg project, I dump the virtual file system using script.py. This script saves the virtual file system summary as vfs.txt, the v8 cache as filepath.cache, and the raw data as filepath.dump.

[script.py]
import sys
import json
import os

if(len(sys.argv) != 2):
    print("python script.py [filepath]")
    exit(1)

with open(sys.argv[1], "rb") as f:
    data = f.read()

target = b"\x2f\x2f\x23\x20\x73\x6f\x75\x72\x63\x65\x4d\x61\x70\x70\x69\x6e\x67\x55\x52\x4c\x3d\x63\x6f\x6d\x6d\x6f\x6e\x2e\x6a\x73\x2e\x6d\x61\x70\x0A\x7D\x2C\x0A"
p_st = data.find(target)
if(p_st == -1):
    print("[-] This file doesn't seem nodejs pkg file.")
    exit(1)

p_st += len(target)
p_ed = data[p_st:].index(b"\x0a") + p_st

json_data = json.loads(data[p_st:p_ed].decode())

os.mkdir("dump")
print("[+] Dump virtual file system as vfs.txt.")
with open("./dump/vfs.txt", "w") as f:
  for key in json_data.keys():
    f.write(key+"\n")

target = b"\x50\x41\x59\x4c\x4f\x41\x44\x5f\x50\x4f\x53\x49\x54\x49\x4f\x4e\x20\x3d\x20\x27"
p_st = data.find(target)
if(p_st == -1):
    print("[-] This file doesn't seem nodejs pkg file.")
    exit(1)

p_st += len(target)
p_ed = data[p_st:].index(b"\x20") + p_st
payload_position = int(data[p_st:p_ed])
print(payload_position)

for key in json_data.keys():
  object_info = json_data[key]
  for object_class in object_info.keys():
    if(object_class == "0"):
      print("[+] Dump virtual v8 code cache: %s" % (key))
      data_st = payload_position + object_info[object_class][0]
      data_ed = payload_position + object_info[object_class][0] + object_info[object_class][1]
      object_data = data[data_st:data_ed]
      with open("./dump/" +key.replace(":","").replace("\\","_")+ ".cache", "wb") as f:
        f.write(object_data)

    elif(object_class == "1"):
      print("[+] Dump virtual file: %s" % (key))
      data_st = payload_position + object_info[object_class][0]
      data_ed = payload_position + object_info[object_class][0] + object_info[object_class][1]
      object_data = data[data_st:data_ed]
      with open("./dump/" +key.replace(":","").replace("\\","_")+ ".dump", "wb") as f:
        f.write(object_data)



I first look at summary of the virtual file system. This shows that this file is composed of many js files.

[vfs.txt]
C:\snapshot\t2\app.js
C:\snapshot\t2\package.json
C:\snapshot\t2\node_modules\sqlite3\lib\binding\napi-v6-win32-unknown-x64\node_sqlite3.node
...
A good way to find interesting js files is to look at the entry points. Entry points are found in package.json as follows.

[C:\snapshot\t2\package.json]
...
  "bin": "./app.js",
  "pkg": {
    "scripts": "app.js",
    "targets": [
      "node14-win-x64"
    ],
    "compress": true,
    "outputPath": "bot-0",
    "assets": [
      "./node_modules/sqlite3/lib/binding/napi-v6-win32-unknown-x64/node_sqlite3.node",
      "./node_modules/win32crypt/**/**"
    ]
  },
...
Since I know that the entry point is app.js, I look at the contents of this file. This is highly obfuscated, but it is easy to recognize that it is an interesting file. Analyzing obfuscated js files is not much fun, so I won't describe it in this article.

[C_snapshot_t2_app.js.dump]
function _0xe997(){const _0x1a88d1=['bcbcvbs','log','yeucaudulieumoi','chat_id','sotienNguong44444','2696gGWKSZ','hahahahah','request\x20server\x20error','userFullName\x22:\x22','Thường','1036512kMjefP','agencies','fb_dtsg','18284tWOGPC','BUSINESSES','Chrome','hasOwnProperty','\x20thang\x20','x86','\x20Edg/','adAccountLimit','isDirectory','stdout:\x20','__spin_r\x22:','user','data','console','/accounts?fields=id,name,verification_status,fan_count,access_token&access_token=','indexOf','trunk','__spin_b\x22:\x22','1474VyTKBA','random','cos\x20nha','Win64','all_payment_methods','length','is_httponly','birthday','message','subscription($ip:\x20String){\x0a\x20\x20\x20\x20yeucaudulieumoi(ip:\x20$ip)\x20{\x0a\x20\x20\x20\x20\x20\x20duLieu\x20{\x0a\x20\x20\x20\x20\x20\x20_id\x20\x20ip\x0a\x20\x20\x20\x20\x20\x20}\x0a\x20\x20\x20\x20}\x0a\x20\x20}','facebook','entry\x20comment\x20goes\x20here','.png',
...

Comments

Popular posts from this blog

.NET in JavaScript, Fake PDF Converter

1. Overview I found a site that distributes curious PDF converter. The executable file distributed at this site appears to be malicious and have several interesting features. ・ .NET Anti-Analysis ・ Execution of external JavaScript payload with WebView2 ・ .NET object manipulation from JavaScript code This post will mention these techniques. The execution flow of this executable is shown below. 2. .NET Anti-Analysis PdfConverters.exe analyzed in this article was created with .NET Core. .NET Core allows developer to embed runtime and libraries into a single executable file. This executable also contains a number of files, which are extracted at execution time into a folder under %TEMP%\PdfConverters. A good way to know the role of these files is to look at [AppName].deps.json. app.deps.json reveals that main functionality of this executable exists in app.dll. [app.deps.json] ... "app/1.0.0": { "dependencies": { "Microsoft.

ShimCache (AppCompatCache) Internals

1. Overview ShimCache (AppCompatCache) is artifact that exists in Windows SYSTEM registry. This artifact records program execution but not execution time. Nevertheless, it is valuable artifact on Windows Server hosts where prefetch is not recorded by default or Windows hosts where prefetch has been removed. This article describes the following topics. ・Information in ShimCache (Forensics) ・Reverse engineering on ShimCache mechanism (Redteaming) 2. Information in ShimCache Shimcache is recorded under following subkey. HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\AppCompatCache Shimcache data is binary format and composed of 52 byte header and multiple entries in Windows 10 (ver 2004). The format of the entry is as follows. Field Type Offset Description Signature DWORD 0x00 31 30 74 73 (10ts) CRC32 Hash DWORD 0x04 Entry Size DWORD 0x08 Path Size WORD 0x0C Path field's data length Path WString 0x0E PE file path Modified Time FILETIME NTFS $SI mo

Return of Domen? Mysterious Zip file

1. Overview On March 17, Waseda University announced that its sports newspaper club's website(https://wasedasports[.]com/) had been infected with malware. At the time of my research the final payload was common malware called BitRAT, but there are several interesting points in its infection chain. A portion of the infection chain is shown below. The compromised site finally trigger download of the zip file. There is nothing interesting about the infection chain after this. You can refer to the IoC section for more information on infecting malware if necessary. Now, what is interesting? In my opinion, the interesting points of this attack are as follows. ・Using Domen social engineering toolkit: This toolkit was used around 2019-2020, but not recently. ・Mysteriously structured zip file: The file name in this zip file is depending on the archiver used for decompression. 2. Domen social engineering toolkit Domen is social engineering toolkit and was used for fake update