Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

[fix] leaky functions and globals #10

Merged
merged 1 commit into from

1 participant

@bmeck
Collaborator

This should fix the following:

Function constructor attack (use a comm channel instead of naive function).
Leaking global attack (easy Function wrapper).
Type coercion attack (serialize inside the sandbox, though you may want to add a circular dependency serializer inside the sandbox).
Function.caller attack (fixed by strict mode).
Function.arguments attack (fixed by strict mode).
Native prototype attack (fixed by lack of leaks from others, hooking Function.prototype.call from leaked function for example).

I added some basic examples.

@bmeck bmeck referenced this pull request
Closed

Leak + "use strict" #9

@bmeck bmeck merged commit 5dcb4b4 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 2, 2012
  1. @bmeck
This page is out of date. Refresh to see the latest.
Showing with 79 additions and 29 deletions.
  1. +20 −0 example/example.js
  2. +59 −29 lib/shovel.js
View
20 example/example.js
@@ -26,3 +26,23 @@ s.run( "while (true) {}", function( output ) {
console.log( "Example 5: " + output.result + "\n" )
})
+// Example 6 - Caller Attack Failure
+s.run( "(function foo() {return foo.caller.caller;})()", function( output ) {
+ console.log( "Example 6: " + output.result + "\n" )
+})
+
+// Example 7 - Argument Attack Failure
+s.run( "(function foo() {return [].slice.call(foo.caller.arguments);})()", function( output ) {
+ console.log( "Example 7: " + output.result + "\n" )
+})
+
+// Example 8 - Type Coersion Attack Failure
+s.run( "(function foo() {return {toJSON:function x(){return x.caller.caller.name}}})()", function( output ) {
+ console.log( "Example 8: " + output.result + "\n" )
+})
+
+// Example 9 - Global Attack Failure
+s.run( "x=1;(function() {return this})().console.log.constructor('return this')()", function( output ) {
+ console.log( "Example 9: " + output.result + "\n" )
+})
+
View
88 lib/shovel.js
@@ -4,51 +4,81 @@
/* ------------------------------ INIT ------------------------------ */
var util = require( 'util' )
, code
- , console
, result
+ , console
, sandbox
, Script
- , stdin
+ , stdin;
if ( ! ( Script = process.binding( 'evals').NodeScript ) )
- if ( ! Script = process.binding('evals').Script )
- Script = require( 'vm' )
+ if ( ! ( Script = process.binding('evals').Script ) )
+ Script = require( 'vm' );
/* ------------------------------ Sandbox ------------------------------ */
-// Sandbox methods
-console = []
-sandbox =
- { console:
- { log: function() { var i, l
- for ( i = 0, l = arguments.length; i < l; i++ )
- console.push( util.inspect( arguments[i] ) )
- }
- }
- }
-sandbox.print = sandbox.console.log
+var console = [];
// Get code
-code = ''
-stdin = process.openStdin()
+code = '';
+stdin = process.openStdin();
stdin.on( 'data', function( data ) {
- code += data
+ code += data;
})
-stdin.on( 'end', run )
+stdin.on( 'end', run );
+
+function getSafeRunner() {
+ var global = this;
+ // Keep it outside of strict mode
+ function UserScript(str) {
+ // We want a global scoped function that has implicit returns.
+ return Function('return eval('+JSON.stringify(str+'')+')');
+ }
+ // place with a closure that is not exposed thanks to strict mode
+ return function run(comm, src) {
+ // stop argument / caller attacks
+ "use strict";
+ var send = function send(event) {
+ "use strict";
+ //
+ // All comm must be serialized properly to avoid attacks, JSON or XJSON
+ //
+ comm.send(event, JSON.stringify([].slice.call(arguments,1)));
+ }
+ global.print = send.bind(global, 'stdout');
+ global.console = {};
+ global.console.log = send.bind(global, 'stdout');
+ var result = UserScript(src)();
+ send('end', result);
+ }
+}
// Run code
function run() {
- result = (function() {
- try {
- return Script.runInNewContext( this.toString(), sandbox )
- }
- catch (e) {
- return e.name + ': ' + e.message
- }
- }).call( code )
+ var context = Script.createContext();
+ var safeRunner = Script.runInContext('('+getSafeRunner.toString()+')()', context);
+ var result;
+ try {
+ safeRunner({
+ send: function (event, value) {
+ "use strict";
+ switch (event) {
+ case 'stdout':
+ console.push.apply(console, JSON.parse(value).slice(1));
+ break;
+ case 'end':
+ result = JSON.parse(value)[0];
+ break;
+ }
+ }
+ }, code);
+ }
+ catch (e) {
+ result = e.name + ': ' + e.message;
+ }
process.stdout.on( 'drain', function() {
process.exit(0)
- })
- process.stdout.write( JSON.stringify( { result: util.inspect( result ), console: console } ) )
+ });
+
+ process.stdout.write( JSON.stringify( { result: util.inspect( result ), console: console } ) );
}
Something went wrong with that request. Please try again.