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”?
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!