Skip to content
This repository

[fix] leaky functions and globals #10

Merged
merged 1 commit into from over 2 years ago

1 participant

Bradley Meck
Bradley Meck
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.

Bradley Meck bmeck referenced this pull request
Closed

Leak + "use strict" #9

Bradley Meck bmeck merged commit 5dcb4b4 into from
Bradley Meck bmeck closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Jan 01, 2012
Bradley Meck [fix] leaky functions and globals d30285f
This page is out of date. Refresh to see the latest.
20  example/example.js
@@ -26,3 +26,23 @@ s.run( "while (true) {}", function( output ) {
26 26
   console.log( "Example 5: " + output.result + "\n" )
27 27
 })
28 28
 
  29
+// Example 6 - Caller Attack Failure
  30
+s.run( "(function foo() {return foo.caller.caller;})()", function( output ) {
  31
+  console.log( "Example 6: " + output.result + "\n" )
  32
+})
  33
+
  34
+// Example 7 - Argument Attack Failure
  35
+s.run( "(function foo() {return [].slice.call(foo.caller.arguments);})()", function( output ) {
  36
+  console.log( "Example 7: " + output.result + "\n" )
  37
+})
  38
+
  39
+// Example 8 - Type Coersion Attack Failure
  40
+s.run( "(function foo() {return {toJSON:function x(){return x.caller.caller.name}}})()", function( output ) {
  41
+  console.log( "Example 8: " + output.result + "\n" )
  42
+})
  43
+
  44
+// Example 9 - Global Attack Failure
  45
+s.run( "x=1;(function() {return this})().console.log.constructor('return this')()", function( output ) {
  46
+  console.log( "Example 9: " + output.result + "\n" )
  47
+})
  48
+
88  lib/shovel.js
@@ -4,51 +4,81 @@
4 4
 /* ------------------------------ INIT ------------------------------ */
5 5
 var util = require( 'util' )
6 6
   , code
7  
-  , console
8 7
   , result
  8
+  , console
9 9
   , sandbox
10 10
   , Script
11  
-  , stdin
  11
+  , stdin;
12 12
 
13 13
 if ( ! ( Script = process.binding( 'evals').NodeScript ) )
14  
-  if ( ! Script = process.binding('evals').Script )
15  
-    Script = require( 'vm' )
  14
+  if ( ! ( Script = process.binding('evals').Script ) )
  15
+    Script = require( 'vm' );
16 16
 
17 17
 /* ------------------------------ Sandbox ------------------------------ */
18  
-// Sandbox methods
19  
-console = []
20  
-sandbox =
21  
-  { console:
22  
-    { log: function() { var i, l
23  
-        for ( i = 0, l = arguments.length; i < l; i++ )
24  
-          console.push( util.inspect( arguments[i] ) )
25  
-      }
26  
-    }
27  
-  }
28  
-sandbox.print = sandbox.console.log
  18
+var console = [];
29 19
 
30 20
 // Get code
31  
-code = ''
32  
-stdin = process.openStdin()
  21
+code = '';
  22
+stdin = process.openStdin();
33 23
 stdin.on( 'data', function( data ) {
34  
-  code += data
  24
+  code += data;
35 25
 })
36  
-stdin.on( 'end', run )
  26
+stdin.on( 'end', run );
  27
+
  28
+function getSafeRunner() {
  29
+  var global = this;
  30
+  // Keep it outside of strict mode
  31
+  function UserScript(str) {
  32
+    // We want a global scoped function that has implicit returns.
  33
+    return Function('return eval('+JSON.stringify(str+'')+')');
  34
+  }
  35
+  // place with a closure that is not exposed thanks to strict mode
  36
+  return function run(comm, src) {
  37
+    // stop argument / caller attacks
  38
+    "use strict";
  39
+    var send = function send(event) {
  40
+      "use strict";
  41
+      //
  42
+      // All comm must be serialized properly to avoid attacks, JSON or XJSON
  43
+      //
  44
+      comm.send(event, JSON.stringify([].slice.call(arguments,1)));
  45
+    }
  46
+    global.print = send.bind(global, 'stdout');
  47
+    global.console = {};
  48
+    global.console.log = send.bind(global, 'stdout');
  49
+    var result = UserScript(src)();
  50
+    send('end', result);
  51
+  }
  52
+}
37 53
 
38 54
 // Run code
39 55
 function run() {
40  
-  result = (function() {
41  
-    try {
42  
-      return Script.runInNewContext( this.toString(), sandbox )
43  
-    }
44  
-    catch (e) {
45  
-      return e.name + ': ' + e.message
46  
-    }
47  
-  }).call( code )
  56
+  var context = Script.createContext();
  57
+  var safeRunner = Script.runInContext('('+getSafeRunner.toString()+')()', context);
  58
+  var result;
  59
+  try {
  60
+    safeRunner({
  61
+      send: function (event, value) {
  62
+        "use strict";
  63
+        switch (event) {
  64
+          case 'stdout':
  65
+            console.push.apply(console, JSON.parse(value).slice(1));
  66
+            break;
  67
+          case 'end':
  68
+            result = JSON.parse(value)[0];
  69
+            break;
  70
+        }
  71
+      }
  72
+    }, code);
  73
+  }
  74
+  catch (e) {
  75
+    result = e.name + ': ' + e.message;
  76
+  }
48 77
   
49 78
   process.stdout.on( 'drain', function() {
50 79
     process.exit(0)
51  
-  })
52  
-  process.stdout.write( JSON.stringify( { result: util.inspect( result ), console: console } ) )
  80
+  });
  81
+  
  82
+  process.stdout.write( JSON.stringify( { result: util.inspect( result ), console: console } ) );
53 83
 }
54 84
 
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.