Code execution from file?


#1

hello,

i have a text field in a database table (a text file, generally
speaking), with arbitrary Ruby code. i would like to execute this code
in my application’s current environment. but i need to hide all my
variables except for some specific ones from this code.

so i was thinking to create a class with a static method that accepts
parameters and put the content of the file in to this method. and pass
the variables i want to expose as parameters.

how is this done in ruby? i have never done it.

in addition, how to execute this code with maximal security level?

also, what would happen if the code reopened classes that i have
created? is this possible? how to make it not do that?

basically i want the user to type some code in to my application and
run it but i want to protect against malicious users who want to erase
my hard disk, etc. :wink:

thanks for any help!
konstantin


#2

please forgive the term “class with a static method”, i meant “class
with a class method”. my brain is still in this c++/java/c# rut.


#3

On Thu, 8 Dec 2005, ako… wrote:

how is this done in ruby? i have never done it.

in addition, how to execute this code with maximal security level?

also, what would happen if the code reopened classes that i have created? is
this possible? how to make it not do that?

basically i want the user to type some code in to my application and run it
but i want to protect against malicious users who want to erase my hard
disk, etc. :wink:

 harp:~ > cat a.rb
 def run file, vars = {}
   code = IO::read file
   dumped = vars.inject({}){|d,kv| k, v = kv; d.update k => 

Marshal::dump(v)}
Thread::new(vars, code) do |vars, code|
$SAFE = 4
program = “”
dumped.each{|k,v| program << “#{ k } = Marshal::load
<<-data\n#{ v }\n__data__\n\n”}
program << code
eval program
end.value
end

 value = run "b.rb", "a" => "forty-two"
 p value

 value = run "c.rb", "a" => "42"
 p value


 harp:~ > cat b.rb
 a.upcase


 harp:~ > cat c.rb
 a.center 42


 harp:~ > ruby a.rb
 "FORTY-TWO"
 "                    42                    "

hth.

-a


#4

There are a number of mechanisms to “lock down” the execution for a
chunk of Ruby code, the most common being the built-in $SAFE global
variable. Unfortunately, due to a number of recently-published
vulnerabilities in the implementation of the $SAFE checks, it’s
generally not considered a good way to insure that your application
will be protected from malignant users.

The best example I’ve seen to date of a truly safe Ruby interpreter has
to be why the lucky stiff’s “Try Ruby” website
(http://tryruby.hobix.com/), which lets anyone interact with a
restricted IRb prompt via their browser and some slick Javascript. You
might email why_ directly, and see if he’d be willing to share some
tips with you (and the rest of us!).

At a bare minimum, you need to make sure the following built-in classes
and modules are either entirely hidden, or redefined to “safe”
versions:

Dir
File
FileStat
FileUtils
IO (at least methods like ‘popen’, ‘fcntl’, etc.)
ObjectSpace
Pathname
Process
GzipFile
Kernel (‘fork’, ‘at_exit’, ‘caller’, ‘load’, ‘require’, many more)

…and probaby others, as well. Really, it’s a hard thing to make
arbitrary code execution safe, so unless you can re-use and share
effort with others, I’d caution you against doing this unless
absolutely necessary.

Good luck,

Lennon


#5

thank you very much for your help.

my application runs inside a web browser process, and creating a thread
does not seem scalable.

i tried to evaluate a file from inside a lambda and it seems to work. i
also was able to set safety level for lambda locally which effectively
restores $SAFE when lambda finishes. the only drawback of this was that
my environment is still visible from lambdas.

code = <<CODE
v = MyClass.new(‘from text’)
v.mymethod
CODE

class MyClass
def initialize(v) @v = v end
def mymethod() puts “SAFE=#{$SAFE}, value=#{@v}” end
end

p = eval <<LAMBDA
lambda {
module Mod
$SAFE = 3
#{code}
end
}
LAMBDA

v = MyClass.new(‘original’)
v.mymethod
p.call
v.mymethod


#6

putting aside security issues i think i was able to isolate my
environment and pass only desired variables in to the code. i create a
temporary module and evaluate my code in its context. then i remove
this module (that is why i needed the external module A, i could not
figure out how to remove a module otherwise). experts, does this look
feasible? i do not know though what to do to ensure security…

thanks
konstantin

module A
x = ‘x’
y = ‘y’

module T
class << self
def get_binding§ binding end
end
end

eval(‘puts x’, T.get_binding(x)) -> # fails

puts eval(’“variable #{p}”’, T.get_binding(x))

remove_const :T

#puts eval(’“variable #{p}”’, T.get_binding(x)) -> # fails
end


#7

is there a way in ruby to set up a hook that would intercept calls to
any methods of a class?


#8

On Thu, 8 Dec 2005, ako… wrote:

thank you very much for your help.

my application runs inside a web browser process, and creating a thread does
not seem scalable.

but what if the user code creates one? it’s the only way to isolate
$SAFE and
your vars, see below…

i tried to evaluate a file from inside a lambda and it seems to work. i also
was able to set safety level for lambda locally which effectively restores
$SAFE when lambda finishes. the only drawback of this was that my
environment is still visible from lambdas.

also note that lambda will prevent everything in it’s scope from
ever
being garbage collected. this is bad idea in a web app - assuming it’s
persistant like fastcgi.

in any case be careful:

 harp:~ > cat a.rb
 code = IO::read "b.rb"

 p = eval <<LAMBDA
    lambda {
    module Mod
    $SAFE = 3
    #{code}
    end
    }
 LAMBDA

 p.call
 p "we won't see this!"


 harp:~ > cat b.rb
 exit!


 harp:~ > ruby a.rb

a thread will prevent you main program from exiting.

regards.

-a


#9

The Facets library has a lot of cool methods,
one of which (‘wrap_method’) can intercept calls.

http://facets.rubyforge.org/rdoc/index.html