Playing With Fire: Running Uploaded Ruby Code in a Sandbox – David Stevenson
Intro
It is still new, but we will get a chance to interact with it live. There will be a competition to see who can compromise the sandbox first.
The prize is a Cupcake, but he has not bought it yet, because he doesn’t think anyone will break out.
Rules are you must break out of the sandbox itself, not compromise his box or the OS.
Why allow user code?
Say you want to make a decision about which folder to use for a user’s mail? You can write a bunch of complex rules, or you could allow your users to upload code to do it.
He makes a reference to the Neal Stephenson book about the Metaverse, where everyone uploads code.
Second Live also has a C metalanguage which allows players to create their own code and three-dimensional objects. In this type of game, the sky is the limit.
Google’s AppEngine is another example. Users can write their own code and run it in a sandbox, but Google handles all the scalability and hidden bits.
Why not allow user code?
Dangerous operations: Code could have errors, or not finish. Someone will upload an infinite loop almost immediately, you need to deal with it.
Knowledge: Are users programmers? Maybe they don’t want to learn a language, even one as easy and nice as Ruby.
API Manipulation: Maybe there are ways that users could manipulate your API in ways you have not even thought of yet…
What is a sandbox
- Limited functionality
- Can’t break out
- Separate code space
- There need to be separate code spaces – the user’s space is the “Jungle”
- Bounded execution time
Implementations
Freaky-freaky sandbox gem (MRI ruby): By why the lucky stiff with some contributions from David, written in C. It is a big hack, a bit of a disaster, but it works. We’ll get to play with it.
JavaSand gem (JRuby): Same API as Freaky-freaky, but not as much of a hack. JRuby provides more hooks into the internals, so you can do some of the same things that Freaky-freaky does, but without as much hackery and violation of internals.
Rubinius in the future? – Sub-virtual-machines could be used to create a sandbox, maybe even 20 lines of Rubinius. The C implementation is about 2000 lines.
Lets try it out
Expression Evaluator: 2+2 -> 4, etc.
He is creating the rails application from scratch, hopefully the bandwidth holds up. He’s not using Sinatra, because he doesn’t know how to get something scaffolded fast enough in the time constraints of a presentation.
Some dangerous things are NOT accessible in the sandbox, such as File and Kernel.
require 'test_helper' require 'redgreen' class ExprTest < ActiveSupport::TestCase test "two plus to equals four" do assert_equal 4, Expr.new(:expr => "2 + 2").value end end
class Expr < ActiveRecord::Base def value Sandbox.safe.eval(expr) end end
He then goes on to implement exception handling (test driven, of course), and also implements code to prevent infinite loops with a timeout.
He then wraps up the coding of the initial app, and he is exposing it to the audience. He has to do the standard rails stuff to make a new app work, delete index.html, set up routes, etc.
Now, the fun begins. Here’s some examples that are showing up within a minute:
Listing exprs Expr open testfile return `ls` context.freeze Dir.entries('.') while true; end `ls` self.instance_eval{while true; end} 1/0 `rm -rf ./' arr = ['a'] * 0xFFFFFFFFFFFFFFFFFFFFF ObjectSpace.count_objects p=lambda { 'yo' }; p.call `sudo reboot` a = 2; a+3 4*4 %x[tail log/production.log] File.new $* "HELLO GOGARUCO. YOUR ZIPPER IS DOWN. YES YOU. YEAH, ON THE RIGHT" while true; puts 'are we there yet'; end New expr
David is now discussing the restricted set of objects in the sandbox. The problem is that you need to reference things like Net::HTTP, but that is not in the set of restricted objects.
The solution is to reference some classes into the sandbox, and copy others. It runs the unsafe things “outside” of the sandbox, but users still cannot access these restricted classes.
Gem Support
- acts_as_wrapped_class
- acts_as_runnable_code
- Set up the sandbox easily with referenced classes
- Pass in top level binding and execute code
A Game! Hangman
http://hangman.sandbox.flouri.sh/
# API methods def word def guesses def guess!(char) def all_words
There is a cron job. Every minute, all the algorithms run, and everyone can make a guess.
Reviewing the attempts to break out of the sandbox
Sorry, no backtick:
Expr: `rm -rf ./` Value: "#Sandbox::Exception: SyntaxError: (eval):3:in `_eval': compile errorn(eval):3: unterminated string meets end of file"
He then ask if anyone has broken out of the sandbox. NO hands go up. Win! Everyone claps. Great preso!
About the Author