Slide 1

Slide 1 text

A Sandboxing Story* starring Ruby * narrated by Eric Allam

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

railsforzombies.org

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

railsforzombies.org

Slide 6

Slide 6 text

• Metafilter • Hacker News • Reddit • etc... Release Day

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

http://www.metafilter.com/97726/brains-brain1-brain2-brain3 DAMN YOU ZED SHAW!

Slide 9

Slide 9 text

eval "rm -rf *" fork while fork want = gets.chomp f = File.open("/etc/passwd")

Slide 10

Slide 10 text

puts '' puts 'Hello there, and what\'s your first name?' name = gets.chomp puts 'Your name is ' + name + '? What a lovely name!' puts 'You must have a middle name though?' name1 = gets.chomp puts 'Your middle name is ' + name1 + '? What a handsome middle name!' puts 'Lastly, your last name must be as dashing as the first two?, What is it if I may ask?' name2 = gets.chomp puts 'Please to meet you, ' + name + ' ' + name1 + ' ' + name2 +'. You sound like a fine specimen! ' insert credit

Slide 11

Slide 11 text

Thank you! and we are very sorry :(

Slide 12

Slide 12 text

duh, #gsub! black_list = %w(chmod chown File flock IO.ioctl Kernel syscall trap fork setpgid Process setsid setpriority egid ` Object HTTP IO Fiber GC IRB Module Thread Net:: ObjectSpace OpenSSL OpenURI require load __callee__ __method__ abort autoload binding eval exit fail fork open raise readline set_trace_func sleep spawn system throw trap taint untaint STDOUT STDERR %x exec connection popen send module_eval Base64 logger alias try to_sym begin rescue Exception Error ensure instance_variable logger).join('|') code.gsub!(Regexp.new(black_list), '')

Slide 13

Slide 13 text

duh, #gsub! black_list = %w(chmod chown File flock IO.ioctl Kernel syscall trap fork setpgid Process setsid setpriority egid ` Object HTTP IO Fiber GC IRB Module Thread Net:: ObjectSpace OpenSSL OpenURI require load __callee__ __method__ abort autoload binding eval exit fail fork open raise readline set_trace_func sleep spawn system throw trap taint untaint STDOUT STDERR %x exec connection popen send module_eval Base64 logger alias try to_sym begin rescue Exception Error ensure instance_variable logger).join('|') code.gsub!(Regexp.new(black_list), '') while $:.shift; end

Slide 14

Slide 14 text

Doing it better • better sandboxing techniques • live sandbox breaking competition • conclusion

Slide 15

Slide 15 text

What is “sandboxing”?

Slide 16

Slide 16 text

run untrusted code

Slide 17

Slide 17 text

run untrusted code • Protect against namespace pollution

Slide 18

Slide 18 text

run untrusted code • Protect against namespace pollution • Block dangerous operations

Slide 19

Slide 19 text

run untrusted code • Protect against namespace pollution • Block dangerous operations • limit resource utilization

Slide 20

Slide 20 text

run untrusted code • Protect against namespace pollution • Block dangerous operations • limit resource utilization • Protect secrets (/etc/passwd)

Slide 21

Slide 21 text

run untrusted code • Protect against namespace pollution • Block dangerous operations • limit resource utilization • Protect secrets (/etc/passwd)

Slide 22

Slide 22 text

run untrusted code • Protect against namespace pollution • Block dangerous operations • limit resource utilization • Protect secrets (/etc/passwd) Do something fun!

Slide 23

Slide 23 text

keep_methods = [:eval, : +, :to_s, :methods, :new, :__id__, :object_id, :equal?, :==, :undef_method, :puts, :__send__, :const_get] (Kernel.methods - keep_methods).each {|m| Kernel.__send__(:undef_method, m) rescue nil } Object.constants.each {|con| (Object.const_get(con).methods - keep_methods).each {|m| Object.const_get(con).__send__(:undef_method, m) rescue nil } } puts eval(%{#{ARGV[0]}}) sandbox.rb $ ruby sandbox.rb "1+1" 2 $ ruby sandbox.rb "system('echo HELLO')" sandbox.rb:7:in `eval': undefined method `system'

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

more fun more safe The sandbox tradeoff

Slide 26

Slide 26 text

we need to give our ruby users more drugs

Slide 27

Slide 27 text

we need to give our ruby users more drugs ruby!

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

From the beginning

Slide 32

Slide 32 text

is it $SAFE yet? code = params[:code] #=> "`rm -rf /`" code.tainted? # => true eval(code) # => world 'splodes $SAFE = 1 code = params[:code] #=> "`rm -rf /`" code.tainted? # => true eval(code) # => SecurityError: Insecure operation - eval

Slide 33

Slide 33 text

code = params[:cmd] #=> "`rm -rf /`" code = <<-RUBY $SAFE=3 #{code} RUBY Thread.new { eval(code) }.value # => SecurityError http://www.ruby-doc.org/docs/ProgrammingRuby/html/taint.html

Slide 34

Slide 34 text

“Considering that $SAFE has fallen out of use and there is a renewed interest in managing many namespaces/environments on a single VM, I figured hey.” - _why (http://www.ruby-forum.com/topic/150864)

Slide 35

Slide 35 text

Semantic Analysis ripper (ruby parser) + access control lists code = 'eval("evil")' pp Ripper.sexp code [:program, [[:method_add_arg, [:fcall, [:@ident, "eval", [1, 0]]], [:arg_paren, [:args_add_block, [[:string_literal, [:string_content, [:@tstring_content, "evil", [1, 6]]]]], false]]]]]

Slide 36

Slide 36 text

rubycop (0.5.0) code = "class Foo; end" policy = Rubycop::Analyzer::Policy.new Rubycop::Analyzer::NodeBuilder.build(code).accept(policy) # => false policy.whitelist_const('Foo') Rubycop::Analyzer::NodeBuilder.build(code).accept(policy) # => true https://github.com/rubymaverick/RubyCop

Slide 37

Slide 37 text

CALL_BLACKLIST = %w[ abort alias_method at_exit autoload ... syscall system trap undef __callee__ __method__ ].to_set.freeze

Slide 38

Slide 38 text

CONST_BLACKLIST = %w[ ARGF ARGV .... TOPLEVEL_BINDING Thread VERSION ].freeze

Slide 39

Slide 39 text

Only “sandboxes” the untrusted code

Slide 40

Slide 40 text

Cons

Slide 41

Slide 41 text

Cons • Doesn’t create an new isolated “environment”

Slide 42

Slide 42 text

Cons • Doesn’t create an new isolated “environment” • If code gets through it can do anything

Slide 43

Slide 43 text

Cons • Doesn’t create an new isolated “environment” • If code gets through it can do anything • Too restricted, not enough ruby

Slide 44

Slide 44 text

Cons • Doesn’t create an new isolated “environment” • If code gets through it can do anything • Too restricted, not enough ruby • scary ruby edge cases

Slide 45

Slide 45 text

Many many edge cases in ruby begin x rescue (`ls`; RuntimeError) => err end "abc#{`ls`}" :"abc#{`ls`}" `ls` %x[ls] system("ls")

Slide 46

Slide 46 text

1.3 million submissions

Slide 47

Slide 47 text

freaky freaky sandboxes (a.k.a. “jails”) http://www.ruby-forum.com/topic/150864 jruby_sandbox Isolates Namespace: s = Sandbox.new s.eval <<-RUBY class Foo def self.bar; 'bar'; end end RUBY s.eval 'Foo.bar' # => 'bar' Foo # => NameError: uninitialized constant Foo

Slide 48

Slide 48 text

RubyInstanceConfig cfg = new RubyInstanceConfig(); ... SandboxProfile profile = new SandboxProfile(this); cfg.setProfile(profile); wrapped = Ruby.newInstance(cfg)

Slide 49

Slide 49 text

sand = Sandbox.safe sand.eval %{`echo HELLO`} # => "HELLO\n" sand.activate! sand.eval %{`echo HELLO`} #=> Sandbox::SandboxException: NoMethodError: undefined method ``' for main:Object Blocks dangerous operations:

Slide 50

Slide 50 text

Protects secrets: File.read('/etc/passwd') => "##\n# User Database etc.." s = Sandbox.safe s.activate! s.eval 'File.read("/etc/passwd")' #=> Sandbox::SandboxException: Errno::ENOENT: No such file or directory - /etc/passwd

Slide 51

Slide 51 text

Limits resource utilization sandbox.eval %{while; true; end}, timeout: 5 # => Sandbox::SandboxException: Timeout::Error: execution expired

Slide 52

Slide 52 text

Can give sandbox “capabilities” class Foo def bar; 'bar'; end end sandbox.ref(Foo) foo = sandbox.eval('Foo.new') # => # foo.bar # => 'bar'

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

Sandbox Breaker 3000 http://sandboxbreaker3000.herokuapp.com

Slide 55

Slide 55 text

The Freaky Theory of Sandboxes Allen Ginsberg

Slide 56

Slide 56 text

The Freaky Theory of Sandboxes Ginsberg’s Theorem: conservation of mass/energy entropy increases impossibility of reaching absolute zero

Slide 57

Slide 57 text

The Freaky Theory of Sandboxes Ginsberg’s Theorem: 1. You can’t win: conservation of mass/energy entropy increases impossibility of reaching absolute zero

Slide 58

Slide 58 text

The Freaky Theory of Sandboxes Ginsberg’s Theorem: 1. You can’t win: 2. You can’t break even: conservation of mass/energy entropy increases impossibility of reaching absolute zero

Slide 59

Slide 59 text

The Freaky Theory of Sandboxes Ginsberg’s Theorem: 1. You can’t win: 2. You can’t break even: 3. You can’t quite the game conservation of mass/energy entropy increases impossibility of reaching absolute zero

Slide 60

Slide 60 text

The Freaky Theory of Sandboxes There will always be another exploit One exploit is enough to take everything down No amount of exploits will stop us from trying

Slide 61

Slide 61 text

1. You can’t win: The Freaky Theory of Sandboxes There will always be another exploit One exploit is enough to take everything down No amount of exploits will stop us from trying

Slide 62

Slide 62 text

1. You can’t win: 2. You can’t break even: The Freaky Theory of Sandboxes There will always be another exploit One exploit is enough to take everything down No amount of exploits will stop us from trying

Slide 63

Slide 63 text

1. You can’t win: 2. You can’t break even: 3. You can’t quite the game The Freaky Theory of Sandboxes There will always be another exploit One exploit is enough to take everything down No amount of exploits will stop us from trying

Slide 64

Slide 64 text

If you’d like to contribute • https://github.com/rubymaverick/RubyCop • https://github.com/omghax/jruby-sandbox • http://sandboxbreaker3000.herokuapp.com

Slide 65

Slide 65 text

Dray Lacy (omghax)

Slide 66

Slide 66 text

Thank you! • @eallam