JavaScript completely "tamper safe" variables - javascript

So, here is the issue.
I have something like:
// Dangerous __hostObject that makes requests bypassing
// the same-origin policy exposed from other code.
(function(){
var danger = __hostObject;
})();
delete __hostOBject;
Am I perfectly safe knowing no script can tamper or access __hostObject?
( If they can, I have an CSRF vulnerability or worse. )
Note 1: This is for a browser extension. I have better hooks than other scripts running on the page. I execute before them and I'm done before they've even loaded.
Note 2: I know this has been asked multiple times for scripts in general. I'm wondering if it's possible if I know I load before any other scripts.

Provided that the __hostObject is deletable, the code in your question is safe.
However, I assume that your real code is slightly more complicated. In that case, very careful coding is required, because the page can change built-in methods (e.g. Function.prototype.call) to get into your closure and do whatever evil things they want. I had successfully abused functionality of extension frameworks such as Kango and Crossrider via this method when I performed such a test.

Won't simply adding a breakpoint and reloading the script expose your __hostObject

Related

Can a webpage detect a tampermonkey userscript?

My question is sort of two-fold. First, how the sandbox model works, how it impacts the userscript, what is accessible / seen from the webpage and userscript point of view, and if using a different sandbox model affects the page being able to notice your script being injected into the page (or not). Second, how scripts are injected into the page, and can the page detect it?
First
From what I can see, when you use #grant none, the sandbox is disabled and you will have access to the webpage and its javascript. IF you make any changes to the javascript and/or DOM, it is possibly detectable by the page.
My understanding is, if you use #grant unsafeWindow, your script will be isolated in its own js context, anything you do to window will NOT be seen by the webpage, BUT you can access the webpage and javascript through unsafeWindow. You will have regular access to the DOM, e.g. document returns the regular page document rather than you needing to say unsafeWindow.document. Obviously, any changes you make to the DOM or page js context (e.g. unsafeWindow.foo = 'bar';) will still be detectable. The reason it is unsafe is not because of being detected or not, but because you are able to potentially give the untrusted page access to privileged GM_* functions in this mode, (which are not granted in regular mode, which means that #grant GM_* for any function will isolate the js context, and you'll lose access to the page's js context unless you #grant unsafeWindow)
Second
How are scripts injected into the page? Is it possible that the webpage can notice the userscript injection (assuming the userscript modifies NOTHING on the page).
For example, if a script was injected using a script tag, I think the page could possibly notice the script injection, even get a look at its code?
Does the sandbox model have any role in the way this happens, and make it "safer" to not be seen? For example, if the js contexts are isolated if you use #grant unsafeWindow, then perhaps the js on the webpage can't even see any userscript load event, making #grant unsafeWindow fundamentally safer, UNLESS you go modifying the DOM or unsafeWindow of course.
I'm also assuming that there's no leak of special functions, objects, properties, etc (such as GM_info to the webpage which would betray the existence of tampermonkey?). Neither in #grant none mode or #grant unsafeWindow mode (provided you didn't leak anything to the page)
This lets me feel that unsafeWindow is actually safer in terms of not being detected (because the js contexts are isolated), as long as you don't go modifying anything (and especially DON'T expose privileged GM_* functions to unsafeWindow). For example, if you used an eventListener on #grant none mode, it may possibly be detected, but if you use it in #grant unsafeWindow mode, it may not be detected because of the isolation? Furthermore, IF it was possible for a page to detect the userscript loading (I don't know if this is actually possible or not), it wouldn't know if the js contexts are isolated
In a brief summary, can a page detect either your userscript's or tampermonkey's existence IF you don't betray it?
Are any of my above thoughts above incorrect in any area, and if so, how does it actually work?
Update
A little information for clarification:
A userscript only reads information passively from the page (perhaps using a MutationObserver). It doesn't alter anything in any way, does not use any js libraries (neither from the userscript nor from the webpage) no ajax calls, no script nodes, definitely no clicks, etc. The script MAY read some information from JS vars on the page (let's assume those vars and functions are not booby trapped), as well as using a WebSocket (internal service). Using an IIFE too. So the question mostly is, is tampermonkey in and of itself (and if it runs a page script) detectable?
In this answer: https://stackoverflow.com/a/8548311
I can rule out 1, 4, 5, 6, and 7; probably 2 and 3 as well, but I don't know if tampermonkey in and of itself would affect any of these
Browsers and Greasemonkey/Tampermonkey/Violentmonkey have (mostly) improved how they do injection, scoping, and sand-boxing. Userscripts are not injected using ordinary <script> tags (although your script may need to create such tags in some occasions).
In fact, there's almost no need to use an IIFE nowadays.
But, in addition to the detection methods in the previously linked question:
In #grant none mode, if you #require a library that copies itself to window scope, the page can see it. Most libraries do not do that, but one that does is jQuery.
Tampermonkey actually provides the installed script version to sites that are whitelisted in the advanced settings. This is mainly for script hosts like greasyfork.org.
I don't know if a page can detect WebSockets in use by a userscript. I doubt it.
Bottom line, is for a "read only" userscript, that does not require global libraries in #grant none mode, the page cannot detect it.
(Unless the page is greasyfork.org, etc., and you have the Allow communication with cooperate pages setting at the default value.)
If you discover some leak whereby a page can detect a "passive" script, let us know and chances are it can get plugged.
As mentioned by the answer https://stackoverflow.com/a/8548311 if you do something of the likes it is definitely detectable. But, depending on what you want to do with the tampermonkey script, it will be easier or more difficult to detect, and in some cases impossible.
From what you are asking, it seems like what you want to make is just invoke an IIFE from the page, and just stop there, "let's say it just reads information".
This is really tricky to capture, and usually for this, the page should have to compare profilers and execution times and such of other users against you, or some other funky things, and there is no real easy way to find out if the user executed extra JS in the page (as long as you use an IIFE) that has NO SIDE EFFECT. I am not saying that it is 100% undetectable, but let's say it's really really tricky.
If you are going to modify the DOM, make API calls to an external or internal service, fake movements of the user or other things of this kind, you are going to be detected. So, it depends on what you want to do with the page, but you can be detected "quite easily".
In a brief summary, can a page detect either your userscript's or tampermonkey's existence IF you don't betray it?
Yes a page can detect these in those cases in which you leave a trace in the page (as defined above). Keep in mind that this will happen only there is a reason for the page to want to know if that is happening. Also keep in mind that no page will implement something like this just for the sake of it, so don't expect normal pages to complain about this.
Amazon implemented something that waits 3 or 4 seconds and then retrieves all console information.
This is what is looks like
{
"logs": [{
"level": "error",
"message": "Cannot set property 'checked' of null",
"error": {
"errorMessage": "Cannot set property 'checked' of null",
"errorName": "TypeError",
"errorStackTrace": "TypeError: Cannot set property 'checked' of null\n at storageUpdate (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?name=myUserscript).user.js&id=ea4d27bb-1f9a-44e5-847c-2f61122b4d75:14467:86)\n at Window.configModal (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?name=myUserscript).user.js&id=ea4d27bb-1f9a-44e5-847c-2f61122b4d75:14488:29)\n at <anonymous>:3:100\n at E.z.<computed> (eval at exec_fn (:1:157), <anonymous>:43:442)"
},
"context": {
"logTime": 1610058101393
}
}]
}
It's new, and whether or not they are targeting me, they can clearly see that tampermonkey is working:
chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/

jQuery on MTurk, why does Chrome report "Unsafe JavaScript attempt to access frame with URL"?

I'm doing a couple of things with jQuery in an MTurk HIT, and I'm guessing one of these is the culprit. I have no need to access the surrounding document from the iframe, so if I am, I'd like to know where that's happening and how to stop it!
Otherwise, MTurk may be doing something incorrect (they use the 5-character token & to separate URL arguments in the iframe URL, for example, so they DEFINITELY do incorrect things).
Here are the snippets that might be causing the problem. All of this is from within an iframe that's embedded in the MTurk HIT** (and related) page(s):
I'm embedding my JS in a $(window).load(). As I understand it, I need to use this instead of $(document).ready() because the latter won't wait for my iframe to load. Please correct me if I'm wrong.
I'm also running a RegExp.exec on window.location.href to extract the workerId.
I apologize in advance if this is a duplicate. Indeed - after writing this, SO seems to have a made a good guess at this: Debugging "unsafe javascript attempt to access frame with URL ... ". I'll answer this question if I figure it out before you do.
It'd be great to get a good high-level reference on where to learn about this kind of thing. It doesn't fit naturally into any topic that I know - maybe learn about cross-site scripting so I can avoid it?
** If you don't know, an MTurk HIT is the unit of work for folks doing tasks on MTurk. You can see what they look like pretty quick if you navigate to http://mturk.com and view a HIT.
I've traced the code to the following chunk run within jquery from the inject.js file:
try {
isHiddenIFrame = !isTopWindow && window.frameElement && window.frameElement.style.display === "none";
} catch(e) {}
I had a similar issue running jQuery in MechanicalTurk through Chrome.
The solution for me was to download the jQuery JS files I wanted, then upload them to the secure amazon S3 service.
Then, in my HIT, I called the .js files at their new home at https://s3.amazonaws.com.
Tips on how to make code 'secure' by chrome's standards are here:
http://developer.chrome.com/extensions/contentSecurityPolicy.html
This isn't a direct answer to your question, but our lab has been successful at circumventing (read hack) this problem by asking workers click on a button inside the iframe that opens a separate pop-up window. Within the pop-up window, you're free to use jQuery and any other standard JS resources you want without triggering any of AMT's security alarms. This method has the added benefit of allowing workers to view your task in a full-sized browser window instead of AMT's tiny embedded iframes.

Log javascript errors from another subdomain

window.onerror in Firefox and Chrome seems to discard the real error message/location and always pass "Script error.", "", 0 when the offending script is on a different domain than the page itself. I have a site with separate www and static subdomains for pages and css/js, which renders error logging rather useless. Is there any way to enable the proper logging of such errors?
It doesn't sound like there's a workaround for this at the moment (I asked in the Mozilla IRC channel).
I've filed these bug reports to track this issue. Please chime in if you have opinions on what the best solution to this would be:
https://bugzilla.mozilla.org/show_bug.cgi?id=696301
https://bugs.webkit.org/show_bug.cgi?id=70574
The real solution is to use proper try { ... } catch(e) { ... } blocks in all code. However, I understand that may not always be an option.
If you don't have control over these other scripts, your next best option would be to load them as strings via JSONP, then use eval() (yes, I know eval is evil) to "inject" them into the current page. This way you'll still get the benefits of using a static domain (no cookies, CDN option, additional concurrent requests, etc), but the JS will end up being on the page's request domain.
Within your js file, on the top try doing
document.domain = "yourdomain.com"

IE equivalent for GM_setValue and GM_getValue (Greasemonkey storage)

I have made a script that runs with no glitch on Firefox. I'm retrieving some data from external domain in an iframe to insert them in the page by using setInterval()
I have tried to use Trixie, so that it runs in IE, but it seems that functions GM_getValue and GM_setValue were not defined.
I've added these replacement functions, based on cookies, but I can't get it to work in a cross-domain way : http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js
The cookie is created, and the data stored, but it's only accessible from the iframe, not from the top document.
Here is the basic structure I used : http://www.pastie.org/1889407
In test() I have access to the value stored with GM_getValue("destination",""), but it doesn't work inside function check().
1) Is there a way to make the cookie cross-domain?
2) Are there other ways to store data in IE in a cross-domain way? (I have briefly heard of Flash objects, but it doesn't seem quite a light solution...) Other implementations of these functions (getValue and setValue) are quite hard to find.
3) I'm using Trixie, maybe it's not the best solution, any advice on what plugin I should better use, to have those functions?
Well, after trying many solutions, I finally found an answer.
I used IE7PRO, that includes functions PRO_setValue and PRO_getValue, that ended to work just fine.
My problem was that I tried to access the external domain on an iframe, which is not supported by IE7PRO apparently.
I just splitted my script into 2 user scripts : one for my domain, that check if new PRO_getValue are available, and the other one for the external domain, that saves the data. One tab for my page, one for the other and surprisingly, it worked like a charm.
It doesn't seem possible with Trixie or GM4IE, the only inconvenient is that IE7PRO is heavy, since the GM script support is just a small part of this extension.

Javascript Sandbox?

Would it be possible to sandbox user-submitted Javascript by overriding various functions such as alert, window.location, and eval?
I'm not looking for a perfect solution. I'm sure some people would still find a way to rearrange divs to spell out swear words or something malicious, but if I could disable page redirects 100% reliably I would be mostly happy.
I tried in Chrome, and doing something like
context={}; //use this to prevent `this` from being `window`
context.f=function(){
var window=null,location=null,eval=function(){};
console.log(window); //also the other two
};
context.f();
seems promising. If I replace the console line with user-submitted code (checking for paren balancing), would that be an absurdly bad idea or a mildly bad idea? On Chrome I can still break things by going through this to Function and redefining things, but that would be acceptable to me.
You can use Microsoft Web Sandbox or Google Caja.
Here are two more possible solutions (disclaimer: I just started looking for this myself, so I am not an expert).
This is very interesting, uses web workers to sandbox untrusted code:
https://github.com/eligrey/jsandbox
even though, I wonder if that is maintaned anymore, or if the following html5 "sandbox" iframe attribute supersedes it:
http://www.w3schools.com/html5/att_iframe_sandbox.asp
vm.js is a javascript virtual machine implemented in pure coffeescript(should run in relatively old browsers) and can be used as a lightweight in-process sandbox. It can break infinite loops and shields global objects from modifications.
Depending on what this needs to do, you could always run the javascript in a document-context-free environment, like through Rhino, and then grab the results server-side and clean/insert those.
You could also try Douglas Crockford's AdSafe, though it does limit the possibilities of JavaScript.
Masking the globals with local variables is not secure actually. Preprocessing the untrusted code with tools like Google Caja may help, but it's not necessary:
For a web-browser simply running a code in a Worker is enough - it seems to be pretty restricted nowadays. See update below
For Node.js you may fork() in a sandboxed process and execute the code there (using the child_process module).
There are also some libraries for simplifying the sandboxing, one of those created by myself is Jailed (there's also a demo with JS-Console which executes user-submitted code in a sandbox).
Update: obviously I was wrong, the worker is not secure by itself, as it can access some of same-origin stuff, like IndexedDB for instance. I have submitted a related question. The solution is to additionally put the worker into a sasndboxed iframe, which is also implemented in my Jailed library.
Use HTML5 "sandbox" iframe attribute.
I made a javascript function for this.
function evalUnsafe(userCode) {
var vars = [];
var legal = {console: 'console', alert: 'alert'};
for(var b in this) {
if (!(b in legal)) {
vars.push(b);
}
}
var funcs = vars.join(",");
var code = "(function sandbox(" + funcs + ") {function runthis() {eval(" + JSON.stringify(userCode) + ");};var a = new runthis();})();";
eval(code);
}
And then you can do this
Example 1:
evalUnsafe("alert(window);");
Example 2 (from a php file):
evalUnsafe(<?php echo json_encode(file_get_contents("example.js"));?>);
You can download it from here:
https://github.com/oyvindrestad/javascriptsandbox

Categories