CVE-2023-32314 (published 2023-05-15T20:15Z)

vm2 is a sandbox that can run untrusted code with Node's built-in modules. A sandbox escape vulnerability exists in vm2 for versions up to and including 3.9.17. It abuses an unexpected creation of a host object based on the specification of `Proxy`. As a result a threat actor can bypass the sandbox protections to gain remote code execution rights on the host running the sandbox. This vulnerability was patched in the release of version `3.9.18` of `vm2`. Users are advised to upgrade. There are no known workarounds for this vulnerability.

CVSS v3 base score: 10.0, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)

Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/patriksimek/vm2
commit_details: repo_url: https://github.com/patriksimek/vm2
author: XmiliaH
author_date: 2023-05-13 21:15:04+02:00
msg: Ensure host array does not leak through proxy
repository: repo_url: https://github.com/patriksimek/vm2
repo_name: patriksimek/vm2
description: Advanced vm/sandbox for Node.js
repo_language: JavaScript
owner: patriksimek
file_changes: filename: setup-sandbox.js
change_type: ModificationType.MODIFY
diff: @@ -21,6 +21,7 @@ const {
 const {
 	getPrototypeOf: localReflectGetPrototypeOf,
 	apply: localReflectApply,
+	construct: localReflectConstruct,
 	deleteProperty: localReflectDeleteProperty,
 	has: localReflectHas,
 	defineProperty: localReflectDefineProperty,
@@ -432,6 +433,63 @@ if (AsyncGeneratorFunction) {
 	overrideWithProxy(AsyncGeneratorFunction.prototype, 'constructor', AsyncGeneratorFunction, makeCheckFunction(true, true));
 }
 
+function makeSafeHandlerArgs(args) {
+	const sArgs = ensureThis(args);
+	if (sArgs === args) return args;
+	const a = [];
+	for (let i=0; i < sArgs.length; i++) {
+		localReflectDefineProperty(a, i, {
+			__proto__: null,
+			value: sArgs[i],
+			enumerable: true,
+			configurable: true,
+			writable: true
+		});
+	}
+	return a;
+}
+
+const makeSafeArgs = Object.freeze({
+	__proto__: null,
+	apply(target, thiz, args) {
+		return localReflectApply(target, thiz, makeSafeHandlerArgs(args));
+	},
+	construct(target, args, newTarget) {
+		return localReflectConstruct(target, makeSafeHandlerArgs(args), newTarget);
+	}
+});
+
+const proxyHandlerHandler = Object.freeze({
+	__proto__: null,
+	get(target, name, receiver) {
+		const value = target.handler[name];
+		if (typeof value !== 'function') return value;
+		return new LocalProxy(value, makeSafeArgs);
+	}
+});
+
+function wrapProxyHandler(args) {
+	if (args.length < 2) return args;
+	const handler = args[1];
+	args[1] = new LocalProxy({__proto__: null, handler}, proxyHandlerHandler);
+	return args;
+}
+
+const proxyHandler = Object.freeze({
+	__proto__: null,
+	apply(target, thiz, args) {
+		return localReflectApply(target, thiz, wrapProxyHandler(args));
+	},
+	construct(target, args, newTarget) {
+		return localReflectConstruct(target, wrapProxyHandler(args), newTarget);
+	}
+});
+
+const proxiedProxy = new LocalProxy(LocalProxy, proxyHandler);
+
+overrideWithProxy(LocalProxy, 'revocable', LocalProxy.revocable, proxyHandler);
+
+global.Proxy = proxiedProxy;
 global.Function = proxiedFunction;
 global.eval = new LocalProxy(localEval, EvalHandler);
 

programming_language: JavaScript