Home JavaScript Malware Code review of a Fake Job Email
Post
Cancel

JavaScript Malware Code review of a Fake Job Email

I recently found this hilarious email in my Junk email for a job offer for a Web3 Platform with expected salary being

1
2
3
4
5
> Our expected salary for each role are:
> - Frontend (React): $150K/year
> - Backend (Node.js, Web3 integration): $200K/year
> - Blockchain (Web3 or Smart Contracts): $250K/year
> - CTO: $400k/year

I did however found them sharing a repo interesting - https://bitbucket.org/cryptooasis-work/socifi-mvp-v1/src/main/

The code itself looks AI generated, with 315 files, with only 1 commit.

package.json itself doesn’t seem to be using the usual malware tricks such as postinstall scripts. However, execp and fs from the dependencies list looks suspicious

Reviewing the files itself, the malware can be spotted in auth.js. Looking through bitbucket, nothing malicious can be spotted.

If you scroll to the right, you will spot heavily obfuscated javascript code.

The full code be seen if you click on raw format. Pastebin link: https://pastebin.com/pYMFTXRW

The code itself is heavily obfuscated using array-based string obfuscation. Some breakdown of these techniques can be seen below:

  • Encoded Module Imports - Encoded Module Imports (require(X(…))) - Makes it more difficult to understand what modules are being imported
  • Encoding & Decoding - Buffer is used in several places which is used for encoding and decoding
  • Dynamically Constructed token - long strings such as X(…) + X(…) are used to concatenate code during runtime which makes static analysis and code comprehension harder.
  • IIFE (Immediately Invoked Function Expression) ((function() { … })()) - Functions executed immediately when the script is run, also making it hard to understand code flow

This can be deobfuscated using the following tools:

  • https://github.com/ben-sb/javascript-deobfuscator
  • https://willnode.github.io/deobfuscator/

This will help understand the code further. (https://pastebin.com/PJm8Zr6d)

Interesting part of the code

  • Obfuscation with Lookup Function
1
2
3
4
5
6
7
8
function H(a, b) {
  const am = F();
  H = function (A, B) {
    A = A - 192;
    return am[A];
  };
  return H(a, b);
}

The H function maps numerical indices to obfuscated strings from an array am returned by F().

Calls like a9(205) are resolved into actual strings, e.g., module names or command strings.

This is classic control-flow obfuscation and string hiding — it serves no runtime logic purpose beyond hiding intent from static analysis.

Infinite Loop with Anti-Debugging & Execution Delay

1
2
3
4
5
6
7
8
9
10
11
12
(function(I, K) {
  const L = I();
  while (true) {
    try {
      const O = ...;
      if (O === K) break;
      else L.push(L.shift());
    } catch (_) {
      L.push(L.shift());
    }
  }
})(F, 350520);

This is a deliberate infinite loop with calculated arithmetic operations involving obfuscated constants.

The loop continually rotates the L array until a checksum (O) matches the hardcoded value K.

Common in JavaScript malware to:

  • Confuse static analysis tools.

  • Introduce delay in sandbox execution (anti-emulation).

  • Avoid debugger breakpoint tracing.

Self-Defending Function

1
2
3
4
5
6
7
const D = function() {
  const B = function() {
    // Inspect B.toString()
    // Detect tampering
  };
  B();
}();

This pattern defines a closure that runs immediately.

The inner function B() may call B.toString() to check its own source code — this is self-defending code.

Used to detect:

  • If the script has been modified.
  • If it’s running in a virtualized or instrumented environment.
  • If a debugger is present.

Imports and Host Environment Fingerprinting

1
2
3
4
5
6
7
const c = require("os"),
      e = require("fs");

const hd = c.homedir(), 
      hs = c.hostname(), 
      pl = c.platform(), 
      us = c.userInfo();

Native Node.js modules are loaded (os, fs) to gather:

Home directory Hostname Platform (e.g., “win32”, “linux”) Username

This is host fingerprinting — gathering enough information to:

Identify the environment Choose suitable payloads Avoid duplicate infections

Dynamic URL Construction and HTTP Fetch

1
2
3
4
5
rq = require("request");
pt = require("path");
zv = require("child_process");

rq.get("...");

Strings are built dynamically using the H function and base64 decoding.

Modules like request and child_process are used to:

Make network requests to malicious C2 domains.

Retrieve secondary-stage payloads.

Possibly download scripts or binaries and execute them directly.

File Handling and Execution Flow

Functions like g(), w(), Y(), J(), M() do things like:

  • fs.writeFileSync(…): Saves downloaded data to disk.
  • fs.existsSync(…): Checks if payload was successfully saved.
  • child_process.exec(…): Executes downloaded payloads.

This behavior indicates:

  • Second-stage loader behavior
  • Persistence mechanisms: e.g., cron jobs or autorun scripts
  • On-the-fly code injection or process hollowing

Conclusion

This malware was more sophicated than I thought, and this initial javascript malware seems to be downloaded a second dropper and executes it. If you look at https://github.com/DataDog/malicious-software-packages-dataset, or writeups from https://socket.dev/blog, you rarely see anti-debugging techniques in JS malware in the wild so this was a fun example.

Some useful tools for JS malware analysis in general

1
2
3
4
5
6
7
8
9
Tools used:
Grep
VSCode and sublime in restricted mode
Isolated Ubuntu host system
kalilinux-docker with VNC enabled
https://github.com/ben-sb/javascript-deobfuscator
https://willnode.github.io/deobfuscator/
ChatGPT
npm
This post is licensed under CC BY 4.0 by the author.