Vm2 (virtual machine 2) is a library that provides a secure and sandboxed environment for executing JavaScript code, primarily used in server-side environments such as Node.js. It lets you create and run JavaScript code within a controlled environment, providing an extra layer of security by isolating code execution from your main application. Thus, potentially unsafe or untrusted code can be executed without impacting the stability or security of the hosting application.
Eval (a JS function) is used instead of the vm2 position when the latter isn’t present, which causes some serious security issues. eval and the vm2 library are both related to dynamically executing JavaScript code. But if user input is directly passed to eval, it can allow arbitrary code execution, while vm2 provides a sandbox environment and restricts access to specific global objects or functions.
Vm2 can also be used to do malware analysis, testing, and code debugging using an online code editor (if a user writes some malicious code), et al. Npm is a JS package manager; some npm packages use vm2 as dependencies to name popular packages, e.g., Cloudinary and Puppeteer.
Into the Depths – Exploring Vulnerabilities Impacting Vm2's Sandbox Integrity
A vulnerability has recently been discovered in the widely used vm2 library; it raises concerns about the integrity of its sandboxing capabilities. The vulnerability allows attackers to bypass the built-in sandbox and gain unauthorized access, thus enabling them to execute arbitrary code within the environment. Such an exploit undermines the core purpose of vm2 as a secure JS execution environment.
With the ability to execute arbitrary code, adversaries can potentially perform malicious activities, compromise sensitive data, and/or exploit system vulnerabilities. This revelation calls for immediate attention by developers and users relying on vm2; you should reassess your security measures and implement appropriate safeguards to mitigate the risk of exploitation.
Let’s explore the CVEs that unravel vm2's sandbox security capabilities and their impact on JavaScript applications.
CVE-2023-32314
This vulnerability is related to using err.name.toString in the ErrorPrototypeToString function, which is called from the host context. The issue arises because the error argument of the prepareStackTrace function is not handled properly by handlers defined in vm2/lib/bridge.js.
This is due to prepareStackTrace being called directly by the V8 engine without going through proxy handlers. The behavior of a proxy object's [[Call]] internal method is relevant. It points to the creation of argArray in the host context and the host object being passed to apply(target, thiz, args). This suggests that accessing the Function constructor of the host context is possible, which could be a security concern in the vulnerable code.
This vm2 vulnerability is related to the mishandling of the error argument in the prepareStackTrace function, potentially allowing unauthorized access to the Function constructor in the host context.
The sandbox escape vulnerability affects vm2 versions up to 3.9.17. Exploiting it could enable a malicious actor to bypass the protective sandbox measures. Once they’re circumvented, an attacker can execute arbitrary code on the respective host system remotely.
Figure 1 - CVE-2023-32314 proof of concept
Figure 2 - Output of malicious code (cal/etc/passwd)
CVE-2023-30547
To prevent host exceptions from leaking into the vm2 sandbox, code is preprocessed using a transformer() function that adds handleException() sanitizer function calls. However, this vulnerability is related to CatchClause with ObjectPattern.
When this scenario occurs, the code calls handleException() and then rethrows the sanitized exception within a nested try-catch block. Used for sanitization, handleException() calls thisReflectGetPrototypeOf(), a function that accesses the prototype of an object using Reflect.getPrototypeOf().
This step is important for proper sandbox functionality. The issue arises when the prototype object is proxied, and a getPrototypeOf() proxy handler is present. This can throw an unsanitized host exception, which is then caught by the outer catch statement.
An attacker could take advantage of this by raising a non-proxied host exception inside a getPrototypeOf() proxy handler. They can register it to an object and throw it, thereby leaking the host exception into the sandbox. By accessing the leaked host exception, the attacker can potentially escape the sandbox and gain access to host functions, compromising environment security.
In simpler terms, this vulnerability arises when exceptions from the host environment can leak into the sandbox. This can happen due to the handling of CatchClause with ObjectPattern. To exploit this vulnerability, an attacker can use a specific technique to raise a host exception, allowing them to escape the sandbox and access host functions.
This affects vm2 versions up to 3.9.16. Successful exploitation lets adversaries bypass the sandbox and execute arbitrary code.
Figure 3 - CVE-2023-30547 proof of concept
CVE-2023-29199
Another vm2 library vulnerability relates to how host exceptions can potentially leak into the sandbox. The code goes through a preprocessing step using a transformer function to prevent this. During this phase, the code is instrumented with function calls to sanitize caught exceptions, including potential host exceptions. The catch clause identifier name is interpolated into the code to sanitize the caught exception.
However, a post-processing step vulnerability can replace a specific identifier, $tmpname, with the string value stored in the respective variable. This replacement occurs in code responsible for sanitizing exceptions when using ObjectPattern. Here an attacker can exploit this vulnerability by using an identifier containing the $tmpname string. They bypass the handleException() sanitizer and potentially access the host function constructor, compromising environment security.
In simpler terms, this vm2 vulnerability relates to how host exceptions can enter the sandboxed environment. The code tries to sanitize caught exceptions, but a vulnerability in the post-processing step lets a bad actor bypass the sanitizer function by using a specific identifier. This can lead to their gaining access to the host function constructor and executing their own code outside of the sandbox.
This affects vm2 versions before 3.9.16. Its successful exploitation allows attackers to bypass the sandbox and execute arbitrary code.
Figure 4 - CVE-2023-29199 proof of concept
CVE-2023-29017
The root cause of this vulnerability lies in the manipulation of the Error.prepareStackTrace property and the constructor functions. This property formats and manipulates stack traces in error objects. By overriding it, an attacker gains control over the stack trace handling. Two main manipulations occur within this custom function:
- The 'fames.constructor.constructor' part retrieves the constructor function of the frames object, then retrieves its constructor function again. This allows access to the global constructor function.
- This latter function executes code ('return process') in the context of the global environment. This allows access to the process object, a core Node.js module providing access to various system functionalities.
This vulnerability arises from manipulating the Error.prepareStackTrace property and uses constructor functions to 1) gain access to the global environment and 2) execute arbitrary code. It affects vm2 versions before 3.9.15. Successful exploitation allows attackers to bypass the sandbox and execute arbitrary code.
Figure 5 - CVE-2023-29017 proof of concept
CVE-2023-32313
This vulnerability involves leveraging util.inspect function behavior in Node.js by using a proxy object. The object permits the interception of property access and modification of its underlying behavior.
By intercepting its property access and returning a custom function or modifying its behavior, an adversary can disrupt the normal execution of code that relies on util.inspect. Such manipulation can lead to unexpected behavior, errors, or unintended consequences depending on context. Attackers could use it to bypass security measures, gain unauthorized access to sensitive information, or execute arbitrary code within the affected system. Vm2 versions prior to 3.9.18 are affected.
Figure 6 - CVE-2023-32313 proof of concept
Previous vulnerabilities discovered in vm2 are similar. They have also exposed the potential for compromising the sandbox environment and executing arbitrary code, highlighting the critical nature of these security issues. It’s crucial to address these vulnerabilities promptly and take immediate action to safeguard your systems.
How to Identify Vulnerable Systems
That said, if a vulnerable vm2 version is installed in your system, Uptycs XDR has you covered. Its scanning detects such vulnerabilities, so you don’t have to worry much. Its vulnerabilities table stores scan results that can be queried as follows:
Figure 7 - Vulnerability detection using Upytcs XDR
How to Fix This Issue
Detection of vulnerabilities starts with keeping your systems secure. But you also need to apply fixes to completely remove them, leaving behind no trace of potential threats. You can upgrade vm2 to the latest version using the npm update vm2 command.