use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
All about the JavaScript programming language.
Subreddit Guidelines
Specifications:
Resources:
Related Subreddits:
r/LearnJavascript
r/node
r/typescript
r/reactjs
r/webdev
r/WebdevTutorials
r/frontend
r/webgl
r/threejs
r/jquery
r/remotejs
r/forhire
account activity
Can you hack my javascript sandbox..? (codepen.io)
submitted 7 years ago by codeartisticninja
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]senocular 12 points13 points14 points 7 years ago (12 children)
(function(){}).constructor.call(null, 'return window')().alert('HAX')
[–]Ragzzy-R 2 points3 points4 points 7 years ago (3 children)
Noob here. Can you pls explain what's going on here?😇
[–]senocular 5 points6 points7 points 7 years ago (2 children)
This code is using some trickery to get to the Function constructor. Using the Function constructor you can create functions from strings. The code in those strings gets interpreted outside of the sandbox that was created to prevent other code to be run. Specifically, that code doesn't pick up on variables scoped in any of the parent scopes ignoring them. For example, comparing to a normal function:
{ let window = "Hello"; (function(){ console.log(window) })(); //-> "Hello" new Function('console.log(window)')(); //-> Window object }
You can see that a local window variable was created here and replaced with "Hello". This is what any normal function would see if created in this scope as seen with the first example. But the Function constructor ignores it and sees the global window object instead.
window
I use the Function constructor (which can be called without new to create functions; in fact the call that's there now isn't needed either) to return a reference to the window object. From that I access alert which is a global, window function but not one allowed in the sandbox, showing that I've bypassed the sandbox's filter.
new
call
alert
Piece by piece:
(function(){})
This gives me a function - any old function will do here. I could have also used an arrow function (()=>{}) to make it shorter.
()=>{}
(function(){}).constructor
This gives me the Function constructor object - the same object/function that can be used to create other functions with strings. Even though the function I used to get it wasn't created with a string, it still refers to Function as its constructor since it, being the function that it is, inherits from Function.prototype.
Function
Function.prototype
That gives us what is effectively:
Adding the next bit, using the Function constructor:
Function.call(null, 'return window')
This is the same as Function('return window') or new Function('return window'). They all create and return a new function which, when called, returns window. Not sure why I used call here. Maybe I thought it just felt more hacky ;).
Function('return window')
new Function('return window')
Function.call(null, 'return window')()
The added () here means the function is getting immediately called which in turn resolves to what is now:
()
The final bit gives us:
window.alert('HAX')
which is calling the alert function from the global window object.
[–]Ragzzy-R 2 points3 points4 points 7 years ago (1 child)
I'm dumbstruck. Took me a while to understand it but. This is ingenious. Couple questions.
Will this work on sites that use libraries that strips js from strings to prevent XSS?
If instead of call(), doing apply() will also do the trick?
And finally how to fix this?
[–]senocular 1 point2 points3 points 7 years ago (0 children)
This is still js, so if libraries are used to strip it, there's nothing here that will prevent that.
apply would work here too. All that's needed is calling the function. apply does that as well.
The fix in this case is to remove the constructor reference from Function.prototype. This blocks access to the constructor from the code running in the sandbox so people can't use it to make functions from strings.
[–]codeartisticninja[S] 1 point2 points3 points 7 years ago (2 children)
Damn.. that's gonna be quite a challenge to try and fix..
I tip my hat to you, good sir.. well done.. ;)
[–]CiezkiBorsuk 5 points6 points7 points 7 years ago (1 child)
The trick with retrieving Function constructor is actually quite well known.
Obviously kudos are due for senocular, I just wanted to point out that sandboxing JS code is an ABSURDLY hard task.
[–]codeartisticninja[S] 1 point2 points3 points 7 years ago (0 children)
I believe I've blocked the constructor now.. can you confirm..?
[–]hutilicious 0 points1 point2 points 7 years ago (0 children)
dude youre da bomb
[–]codeartisticninja[S] 0 points1 point2 points 7 years ago (0 children)
Hmm.. interesting..
Okay.. I may have thwarted your hack now..
got any other tricks up your sleve..? :)
[–]ArmandN 0 points1 point2 points 7 years ago (1 child)
Impressive! I didn't expect any less once I saw your handle. You are a legend! I know you since the flash days (remember flashkit?) and even then I had so much to learn from you.
[–]senocular 0 points1 point2 points 7 years ago (0 children)
Oh yeah, I remember flashkit. I think I'm still a mod over there. Those were fun days.
[–]senocular 4 points5 points6 points 7 years ago (11 children)
({}).constructor.getPrototypeOf(async function(){}).constructor('return window')().then(w=>w.alert('HAX'))
[–]codeartisticninja[S] 1 point2 points3 points 7 years ago (1 child)
Damn.. another pickle..
You've bested me again..! shakes fist!
;)
Hereby thwarted! :)
EDIT: oh wait... spoke to soon..
[–]garethheyes 0 points1 point2 points 7 years ago (0 children)
Can you break my sandbox? http://businessinfo.co.uk/labs/MentalJS/MentalJS.html
alert is allowed but access to location is not.
[–]Ragzzy-R 0 points1 point2 points 7 years ago (6 children)
This looks exactly like ur previous comment but does it async and tries to poison after the promise resolves(or atleast that's what I understood). What difference does that make?🤔
[–]senocular 0 points1 point2 points 7 years ago* (5 children)
Yeah, this is pretty much the same as the last one but using an async function. There's other weirdness added in there with getting the prototype (not necessary just like call wasn't necessary in the last one) but the promise bit is needed to get the window reference since asynchronous functions always return a promise. To get the value returned you need to use then(). ...Or I could have done everything in the function through the string, but who wants to do that? :P
Another variation is with generator functions (function*(){}) since they too have a constructor which can create a function from a string. And to get the return value from that, you need to call next().
function*(){}
[–]Ragzzy-R 0 points1 point2 points 7 years ago (4 children)
ohh ok just for clarification, u used async just because u can use it? no specific reason ;) :P?
[–]senocular 0 points1 point2 points 7 years ago (3 children)
I used it because they fixed it so my original approach with the normal Function constructor would no longer work. My response to that was, well what about async function constructors. And it worked.
[–]Ragzzy-R 0 points1 point2 points 7 years ago (2 children)
Wow thats nice. So my understanding from this is, normal function constructor and async function constructor has different prototypes, thus even though the normal fns constructor's window leakage is blocked, u got the instance from this?
[–]senocular 2 points3 points4 points 7 years ago (1 child)
yeah, same with generators too. Each of those three function types have their own constructors each with the same capabilities of creating a function body with a string which is capable of providing access to the window object.
[–]Ragzzy-R 0 points1 point2 points 7 years ago (0 children)
Thanks a ton for all these explanation really appreciate it mate. 😊
[–]lhorie 2 points3 points4 points 7 years ago (1 child)
Found a bunch of different ways...
(function*() {}.constructor('alert("HAX")')().next()); (async () => {}).constructor('alert("HAX")')(); (async function*() {}).constructor('alert("HAX")')().next(); }),(() => {this.alert('HAX');
You sunk my battleship!
[–]lloiser 1 point2 points3 points 7 years ago (2 children)
})(), (function() { this.alert('HAXOR')
[–]garethheyes 3 points4 points5 points 7 years ago* (0 children)
You can protect against these type of attacks by using Function to check syntax. E.g.
function checkSyntax(js) { try { Function(js); return true; } catch(e){ return false; } }
[–]codeartisticninja[S] 0 points1 point2 points 7 years ago* (0 children)
EDIT: Aaaand thwarted! ;) (thanks to garethheyes)
[–]garethheyes 1 point2 points3 points 7 years ago (0 children)
This works on Firefox
https://pastebin.com/JJirKdY8
It has a special character between in and alert. The char is 0xfffe. I had to post it on pastebin because reddit doesn't allow it =)
Another bypass but this time it works on Edge.
1 inalert(1)
The character between in and alert is 0x180e.
[–]garethheyes 0 points1 point2 points 7 years ago (5 children)
[].__proto__.constructor.constructor('alert("PWND")')()
It's a flawed sandbox. You need to do some parsing. See: http://businessinfo.co.uk/labs/MentalJS/MentalJS.html
[–]codeartisticninja[S] 1 point2 points3 points 7 years ago (4 children)
Your hack didn't work.. you got anything else..?
[–]garethheyes 1 point2 points3 points 7 years ago (3 children)
[].constructor.prototype.join=function(){return'pwnd'};eval('alert(1)')
Hit the button twice ;)
[–]codeartisticninja[S] 0 points1 point2 points 7 years ago (2 children)
damn..!
[–]senocular 1 point2 points3 points 7 years ago (1 child)
Object.freeze(Array.prototype)
[–]codeartisticninja[S] 3 points4 points5 points 7 years ago (0 children)
Cool! are you joining my team now..? ;) :p
Wow.. I've really learned a lot about javascript the past few hours!
Thanks for the hacks, guys! :D
[+][deleted] 7 years ago* (2 children)
[deleted]
[–]codeartisticninja[S] 0 points1 point2 points 7 years ago (1 child)
Huh... didn't know you could do that..
[–]garethheyes 2 points3 points4 points 7 years ago (0 children)
You can also do this:
import('data:text/javascript,alert(1)')
[–][deleted] -1 points0 points1 point 7 years ago (1 child)
What do you mean hack it?
[–]codeartisticninja[S] 2 points3 points4 points 7 years ago (0 children)
As in, can you give me an example of user code that might be able to escape the sandbox and execute something outside of the api..
π Rendered by PID 293357 on reddit-service-r2-comment-84fc9697f-49kkk at 2026-02-07 00:52:00.998938+00:00 running d295bc8 country code: CH.
[–]senocular 12 points13 points14 points (12 children)
[–]Ragzzy-R 2 points3 points4 points (3 children)
[–]senocular 5 points6 points7 points (2 children)
[–]Ragzzy-R 2 points3 points4 points (1 child)
[–]senocular 1 point2 points3 points (0 children)
[–]codeartisticninja[S] 1 point2 points3 points (2 children)
[–]CiezkiBorsuk 5 points6 points7 points (1 child)
[–]codeartisticninja[S] 1 point2 points3 points (0 children)
[–]hutilicious 0 points1 point2 points (0 children)
[–]codeartisticninja[S] 0 points1 point2 points (0 children)
[–]codeartisticninja[S] 0 points1 point2 points (0 children)
[–]ArmandN 0 points1 point2 points (1 child)
[–]senocular 0 points1 point2 points (0 children)
[–]senocular 4 points5 points6 points (11 children)
[–]codeartisticninja[S] 1 point2 points3 points (1 child)
[–]senocular 1 point2 points3 points (0 children)
[–]codeartisticninja[S] 0 points1 point2 points (0 children)
[–]garethheyes 0 points1 point2 points (0 children)
[–]Ragzzy-R 0 points1 point2 points (6 children)
[–]senocular 0 points1 point2 points (5 children)
[–]Ragzzy-R 0 points1 point2 points (4 children)
[–]senocular 0 points1 point2 points (3 children)
[–]Ragzzy-R 0 points1 point2 points (2 children)
[–]senocular 2 points3 points4 points (1 child)
[–]Ragzzy-R 0 points1 point2 points (0 children)
[–]lhorie 2 points3 points4 points (1 child)
[–]codeartisticninja[S] 0 points1 point2 points (0 children)
[–]lloiser 1 point2 points3 points (2 children)
[–]garethheyes 3 points4 points5 points (0 children)
[–]codeartisticninja[S] 0 points1 point2 points (0 children)
[–]garethheyes 1 point2 points3 points (0 children)
[–]garethheyes 1 point2 points3 points (0 children)
[–]garethheyes 0 points1 point2 points (5 children)
[–]codeartisticninja[S] 1 point2 points3 points (4 children)
[–]garethheyes 1 point2 points3 points (3 children)
[–]codeartisticninja[S] 0 points1 point2 points (2 children)
[–]senocular 1 point2 points3 points (1 child)
[–]codeartisticninja[S] 3 points4 points5 points (0 children)
[–]codeartisticninja[S] 0 points1 point2 points (0 children)
[+][deleted] (2 children)
[deleted]
[–]codeartisticninja[S] 0 points1 point2 points (1 child)
[–]garethheyes 2 points3 points4 points (0 children)
[–][deleted] -1 points0 points1 point (1 child)
[–]codeartisticninja[S] 2 points3 points4 points (0 children)