Giter VIP home page Giter VIP logo

evel's People

Contributors

kumavis avatar natevw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

evel's Issues

Dynamic import() is not blockable as a global

Evel'ing this successfully returns a Promise:

import('test')

Testing in Chrome 80, the promise gets rejected with an Error: Cannot import module from an inactive browsing context. but it's possible that may vary depending on JS engine and/or iframe sandbox attribute support.

Dynamic import() works via a "function-like" keyword, not an actual call to a built-in function (e.g. it's like if or catch, rather than like eval or isNaN). We are unable to block keywords through shadowing (i.e. Function('import', "") throws just like var yield = return; would).

Even the Agoric/realms-shim ends up having to munge incoming source on account of this very issue: https://github.com/Agoric/realms-shim/blob/025d975da12c8033022c271e5d99a3810066dfa4/src/sourceParser.js#L31

infinite loops

while(true); or regexps that abuse lookahead to get exponential execution times?

Update globals list

ES6 adds many new globals, and we have a hardcoded list :-(

Note that some, like Reflect may expose new functionality that needs to be reviewed, see #25.

Work in workers

Web workers have a different name for their global context. We should handle this in our strict-mode global finding case.

Bypass 002: `Function("return this")()` d'oh!

Welp, this one is too easy:
http://natevw.github.io/evel/challenge.html#Function(%22return%20this%22)()

Credit: https://twitter.com/wisecwisec/status/357861675782770689

Need to dive into the spec and figure out why Function breaks out of strict mode while eval doesn't — this could be intentional and if so we're ± hosed? We could pass in evel.Function as Function, but I suspect it's trivial to get the original back via primitives and any other exposed methods.

Recap of recent progress

Okay, so here's how I understand #15 works mostly for @kumavis but also adding some followup items I want to check.

According to ES5, even a literal Function Definition follows the algorithm for Creating Function Objects. This algorithm sets the prototype of the new object to "the standard built-in Function prototype object".

This is why I gave up before, but your trick is that as section 15.3 of the spec describes, this "standard built-in" object can still be configured like any other.

The heart of the matter is this line of code:

_gObj.Function.prototype.constructor = evel.Function;

[Note that I’ve simplified it! You were assigning _gObj.Function.constructor.prototype.constructor which is equivalent to _gObj.Function.prototype.constructor.prototype.constructor (since Function.hasOwnProperty('constructor') === false). But Function.prototype.constructor === Function already, which is where the simplified version above comes from.]

So anyway, the way the environment starts out is with (to use V8’s naming) Function.prototype = function Empty() {}. ES5 simply specifies that Function.prototype is a function that accepts any arguments and returns undefined. To quote 15.3.4.1 verbatim: "The initial value of Function.prototype.constructor is the built-in Function constructor." This is what gave us the #12 exploit.

So, the only magic we really have to do is change the constructor value from the initial Function to evel.Function. Assigning _gObj.Function is just taking care of the more obvious access.

We need to assign evel.Function.constructor = evel.Function while setting up the iframe because we have fixed up the iframe context’s Function.prototype.constructor whereas evel.Function still has its original "standard built-in Function" proto from when generated on the main window. (This confused me for a bit: the constructor assignment to self in step 17 of 13.2 happens on the Function.prototype object not the Function.__proto__/Object.getPrototypeOf(Function) one.)

So I think this just might work (great job @kumavis) but I also think there may be some loose ends lying around:

Allow caller to optimize re-runs

Right now the function wrapper has to crawl through the window prototype chain every time it is called. And once we integrate the iframe trick in #1 that setup would add additional overhead.

In some cases with repeated calls to wrapped functions where decent performance is desirable (e.g. calling a PouchDB map function over a whole bunch of documents) the caller may know that globals haven't been added. We could add a .evel_reuseSandbox property on the wrapped function to speed up repeated calls.

Getter on `Object.prototype` gets global as `this` when accessed via variable lookup

This code (borrowed from @mathiasbynens's clever globalThis polyfill) is able to return the window object (of the iframe):

(function() {
	//if (typeof globalThis === 'object') return;
	Object.defineProperty(Object.prototype, '__magic__', {
		get: function() {
			return this;
		},
		configurable: true // This makes it possible to `delete` the getter later.
	});
	__magic__.globalThis = __magic__; // lolwat
var tmp = __magic__;
	delete Object.prototype.__magic__;
return tmp;
}())

Most of the properties e.g. XMLHttpRequest are still somehow shadowed on the returned window object but I did notice at least evel(codeAbove).webkitRequestFileSystem() available which is suspicious.

Oops?

Function('(0,eval)("this")')

Use other depencencies in code

Consider this a question...
How can I implement dependencies and how can I make sure this doesn't open new vulnerabilities?

For example I have some custom Javascript classes and I want to use them during the evaluation of my code. Just to give an example of what it could look like:

Person = function( FirstName, LastName, Age, Sex ){
    this.FirstName = FirstName;
    this.LastName = LastName;
    this.Age = Age;
    this.Sex = Sex;
};

Parent = function( FirstName, LastName, Age, Sex ){
    Person.call( this, FirstName, LastName, Age, Sex );
    this.Children = [];
};
Parent.prototype = Object.create( Person.prototype );

Father = function( FirstName, LastName, Age ){
    Parent.call( this, FirstName, LastName, Age, "male" );
};
Father.prototype = Object.create( Parent.prototype );

Mother = function( FirstName, LastName, Age, Sex ){
    Parent.call( this, FirstName, LastName, Age, "female" );
};
Mother.prototype = Object.create( Parent.prototype );

Child = function( Father, Mother, FirstName, Age, Sex ){
    Person.call( this, FirstName, Father.LastName, Age, Sex );
    this.Mother = Father;
    this.Father = Father;
    this.Age = Age;

    this.addChildToFather();
    this.addChildToMother();
};
Child.prototype = Object.create( Person.prototype );
Child.prototype.setParents = function(){
    if( this.ObjectPlacement !== null ){
        this.ObjectPlacement.PlacesObject.push( this );
    }
};
Child.prototype.addChildToFather = function(){
    if( this.Father !== null ){
        this.Father.Children.push( this );
    }
};
Child.prototype.addChildToMother = function(){
    if( this.Mother !== null ){
        this.Mother.Children.push( this );
    }
};

So prototype etc. should be available...

DOM warning (documentation)

Might be worth making clear that passing any sort of DOM object to the untrusted script is dangerous e.g. script injection via .innerHTML and probably many more avenues…. (Although, how much will our iframe mitigate of that?)

Clean up whitelist

There are a few non-standard extensions in the JS whitelist that probably don't need to be there (uneval for one!)

I don't want to take too many "extras" away (e.g. TypedArrays) on the other hand it's safer to be conservative.

Prevent untrusted code from modifying prototypes

Right now even if we took Object off our global whitelist, an untrusted script could exploit its environment in most browsers via {}.__proto__.toString = function mayhem() {} or whatnot.

Assuming the tricks I'm using do in fact otherwise block globals usage, there's an iframe trick available that would give each run of the script its own JS execution context.

c.f. browserify/vm-browserify#2

Sandbox-available evel.Function not correctly sanitized

The following code still breaks out of the sandbox:

Function.__proto__.constructor("return this")()

As surmised in #17, the fix in #15 merely shadows (rather than fully replacing) the original constructor property. Don't think this will be too hard to fix properly.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.