Where I want my code to run

Why Building a Sandbox in Pure Javascript is a Fool’s Errand

d0nut
4 min readMay 31, 2018

Hey all!

I was terribly excited to see all of my new followers on Medium and wanted to ensure I started giving a (near) constant supply of new reading material for everyone. As such, I decided to write this quick article based on a topic of discussion that came up during my trip to Amsterdam (which I’ll write about soon enough :P ).

Why Sandbox Javascript (with Javascript)?

Honestly there probably isn’t a good reason. Unless you’re actively exploiting either a CTF challenge or an electron app that implemented the security guidelines to a ‘T’, you probably won’t find yourself in a case where you have XSS/Code injection and you don’t have access to eval , alert/confirm , or the DOM. Let’s focus on the former case by coming up with a “CTF-like” example.

Note: I’m leaving console alone so we can output values. If there’s an escape with the console object, that’s unintentional and would likely also be blacklisted in a real jailing attempt.

Let’s assume that during the course of this challenge/exploit you were able to change the code value that is passed into jail and there’s a global flag value that you’re trying to get access to. How do you do it?

Well, you might observe that we’ve explicitly blocked eval (not that eval would do you any good here anyway) and we’ve also blocked Function which, as it would have it, Function does help us. If we were able to get a new Function with arbitrary code in it, we would be able to execute code in the global context (outside of the wrapping Function object). That’s the key difference between eval and Function ; eval is executed in the current context but Function is executed in the global context.

There is a method you can use to get access to a Function object, actually! If you declare a new function with function that actually is treated as a Function object to first-class functions in javascript. Let’s see what that payload would look like.

flag = "I'm the flag!";function jail(code){
...
}
jail("(function(){}).constructor('alert(flag)')()")

This will allow you to open an alert dialog with the flag value!

Is there a way we could restrict function from accessing its constructor? We want to make our jail as tight as possible, after all. I figured out that if you override the Function prototype constructor, you’re no longer able to access it in this way any longer.

Now, if we try the trick with the constructor function, it won’t work!

Is there a way that we can overcome any javascript “jailing”?

Yes we can (overcome javascript jailing techniques)

Using a little-known feature called GeneratorFunction or function* . If you’re not familiar with the concept of generators, they allow you to “generate” new values every time you call them. For example, what if you wanted to make an iterator that increments by square numbers?

function* square_increment(){
var value = 1;

while(true){
yield value*value;
value ++;
}
}

Now, every time you call this function (using .next() ) it will return the next number in the series.

square_generator = square_increment();
console.log(square_generator.next().value); // 1
console.log(square_generator.next().value); // 4
console.log(square_generator.next().value); // 9
console.log(square_generator.next().value); // 16
...

What makes GeneratorFunction unique is that, unlike Function and function , the GeneratorFunction class is not globally accessible. While you can technically access the prototype with the following:

Object.getPrototypeOf(function*(){}).constructor

Setting anything on the prototype here does nothing when you create a generator with function* syntax!

Object.getPrototypeOf(function*(){}).constructor = null;// This still works!
((function*(){}).constructor("console.log(1)"))().next();

Let’s reevaluate our previous escape but with generator syntax…

flag = "I'm the flag!";function jail(code){
...
}
jail("((function*(){}).constructor('alert(flag)'))().next()")

This will work! You should see an alert dialog with the flag on your screen now!

As far as I can tell, there is no way to prevent this in javascript and you have to use more sane sandboxing methods like iframe or web workers. If you want to achieve nearly the same effect as this jailing example, you should use web workers. There’s no DOM access and everything is isolated from the global scope.

Closing Remarks

Let me know if anyone has an idea on how to prevent function generators from accessing their constructor as I would be interested in figuring out a way around it. Also, if anyone has feedback on my writing, I would also appreciate that as well! I’m trying to get better at explaining things in an entertaining, easy-to-read fashion so I can be more helpful to anyone who happens across my articles!

See ya soon!

--

--

d0nut
d0nut

Written by d0nut

Security Engineer, developer, and part-time bug hunter

Responses (4)