During a technical screening for a freelance job, I was sent a “simple Node API” coding test hosted on GitLab. At first glance, the repository looked legitimate. It referenced a real GitLab profile with a LinkedIn account, and the application itself was a normal Express based REST API.
While reviewing the code, I noticed a suspicious dependency called seeds-random. Static analysis of that package showed that it is not a random number utility, but a loader that silently fetches and executes remote JavaScript from jsonkeeper.com inside the Node process.
Public threat intelligence now lists seeds-random version 2.3.7 as one of many malicious npm packages linked to the DPRK aligned “Contagious Interview” operation, where attackers pose as recruiters, send code tests, and use backdoored npm packages to compromise developers. dprk-research.kmsec.uk
This report documents how I encountered the package, how it behaves, and why running this “job test” would expose any developer to remote code execution and credential theft.
1. Background and initial contact
- I was contacted through a well known freelance platform by someone claiming to represent a company that needed Node backend work.
- After a short chat, they sent me a GitLab repository link as a “technical assignment” to evaluate my skills:https://gitlab.com/testing5140742
- The GitLab account appeared to belong to a real person. The profile reused code and identity details from a developer who has a LinkedIn profile, which helped the whole setup look legitimate.
- The instructions were simple: clone the repo, run the API locally, and add a small feature.
I cloned the repository and opened it in my editor before running anything. The main server file looked like a standard Express application:

The business logic and routing code for /api/employees looked clean and familiar. The only thing that felt out of place was the seeds-random middleware. I had never seen or used this package in the Node ecosystem. This was the starting point for the deeper investigation.
2. How I found the malicious dependency
2.1 Package review
My usual process when working with someone else’s Node project is:
- Inspect package.json and package-lock.json
- Flag any libraries that I do not recognise
- Check their npm listing and source code before installing
In this project the dependency list was short. Alongside common packages like express, cors and body-parser, I saw:

A search for seeds-random showed that:
- The package description is vague and generic
- The keywords are unrelated to randomness and mention logging and JSON streaming
- The package was very recently updated
- There was almost no documentation or community usage
External metadata for seeds-random confirms version 2.3.7 with generic keywords like “fast, logger, stream, json”, and no clear explanation for why it exists at all. Libraries.io
This made me suspicious enough to inspect the code.
3. Static analysis of seeds-random
3.1 Entry point behavior
The main export in seeds-random looks like a logging middleware, but it behaves in a very unusual way. It imports a function called postCallers and immediately runs it, then returns a middleware that does nothing:

Key observations:
- The returned Express compatible middleware is a no op. It simply calls next().
- All meaningful work happens in postCallers at the time the module is loaded, not per request.
- This is not normal for an Express logger, and is a classic pattern for a loader that wants to execute something once at process start.
The function name postCallers and the defaultOptions structure try to mimic a real logging library, but there is no actual log logic. That pushed me into the next file.
The line that really stands out is:

If the functionality lives in ./lib/caller, that is the obvious place to look for malicious behavior.
4. Malicious loader in lib/caller
The ./lib/caller file is where the behavior changes from suspicious to clearly malicious.


There are multiple red flags here.
4.1 Shadowing the global process
const process = require(“./const”);
In normal Node applications, process is the built-in global object. Here, the code shadows it with a local module called ./const. This hides what is really going on and forces analysts to inspect another file to Understand where process.env values are coming from.
The code then pretends to read “environment variables” from that fake process:

The base64 strings decode to:
- DEV_API_KEY → https://jsonkeeper.com/b/CNMYL
- DEV_SECRET_KEY → dev-secret-key
- DEV_SECRET_VALUE → _
So the package has a hard coded command and control URL on jsonkeeper.com and a header based secret.
4.2 Remote code execution
Inside the retry loop, the code fetches remote JavaScript and executes it with full access to the Node runtime:

Important points:
- The code performs an HTTP GET request to https://jsonkeeper.com/b/CNMYL with header dev-secret-key: _.
- It expects a JSON document with a cookie field. That field holds arbitrary JavaScript code supplied by the attacker.
- It constructs a new function using new Function(“require”, s) and immediately calls it, passing Node’s require as argument.
- This grants the remote payload full access to require, which in turn provides file system access, network access, and the ability to inspect process.env and any credentials in the environment.
This pattern is a textbook remote code execution loader. The NPM package itself contains only the loader. The real malware is hosted remotely and can be changed at any time without updating the package on NPM.
4.3 Error handling and stealth
The loader is careful to avoid detection:
- It wraps the fetch and execution in a loop with retrycnt set to 5, silently swallowing any errors and never logging them.
- It saves console.log, executes the payload, then restores console.log. This is an explicit attempt to hide whatever the second stage code might print.
There is no legitimate logging library reason to hide errors and restore console.log like this.
5. Command and control infrastructure
The decoded configuration shows the following indicators:
- Command and control URL: https://jsonkeeper.com/b/CNMYL
- Header key: dev-secret-key
- Header value: _
jsonkeeper.com is a legitimate JSON storage service, which makes it attractive for malware authors as an innocuous looking hosting platform. JSON Keeper+1
The exact payload hosted at that path is not embedded in the repository and can be updated at any time. This confirms that seeds-random acts as a generic loader, not a one time static payload.
Because the loader runs when the Express app starts, any developer who installs dependencies and runs the sample project executes whatever code the attacker currently serves at that URL, with the same permissions as the local Node process.
6. Risk and impact
If I had simply run npm install and started the server without inspecting dependencies, the following would have happened:
- Node would load seeds-random when the application starts.
- seeds-random would call postCallers from ./lib/caller with the default options.
- postCallers would perform up to five attempts to fetch a remote payload from jsonkeeper.com with a secret header.
- If successful, it would execute the remote JavaScript with full access to require, the file system, network, and process environment.
Possible impacts include:
- Theft of SSH keys, NPM tokens, or cloud provider credentials from my workstation.
- Exfiltration of code from local repositories.
- Lateral movement into any other system reachable from the developer machine.
- Use of my machine as a staging point for further attacks.
Because the malicious activity runs inside a developer tool chain, it would likely have access to sensitive repositories and configuration.
7. Indicators of compromise
The following indicators can be used to check for exposure in other environments:
Packages
- npm package name: seeds-random
- Known malicious version: 2.3.7 Libraries.io
Network
- C2 URL: https://jsonkeeper.com/b/CNMYL
- HTTP header key: dev-secret-key
- HTTP header value: _
Code patterns
- const process = require(“./const”); shadowing the global process object inside a npm dependency.
- Use of atob to decode base64 strings into URLs and header values.
- Use of new Function.constructor(“require”, s) to execute remote JavaScript.
8. Recommendations
- Always review package.json and package lock for unfamiliar libraries before installing dependencies from an untrusted project.
- Search threat intelligence sources for unusual npm packages. In this case, external feeds already listed seeds-random 2.3.7 as malicious. dprk-research.kmsec.uk+1
- Run suspicious code only inside isolated containers or disposable virtual machines when working on untrusted tests or assignments.
- Educate developers about the Contagious Interview pattern so they recognize the combination of fake recruiter, quick test, and suspicious npm dependency.
Conclusion
This incident highlights how even small, harmless looking test projects can hide serious supply chain threats, and why dependency review must be part of every developer’s basic security hygiene, especially when working with code received from unknown individuals over freelance platforms or social networks.

