code execution from file?

A

ako...

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. ;-)

thanks for any help!
konstantin
 
A

ako...

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.
 
A

ara.t.howard

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. ;-)

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
--
===============================================================================
| ara [dot] t [dot] howard [at] noaa [dot] gov
| all happiness comes from the desire for others to be happy. all misery
| comes from the desire for oneself to be happy.
| -- bodhicaryavatara
===============================================================================
 
A

ako...

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
 
R

rcoder

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
 
A

ara.t.howard

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
--
===============================================================================
| ara [dot] t [dot] howard [at] noaa [dot] gov
| all happiness comes from the desire for others to be happy. all misery
| comes from the desire for oneself to be happy.
| -- bodhicaryavatara
===============================================================================
 
A

ako...

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(p) 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
 
A

ara.t.howard

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

this still leaves things like Object in scope. to truely isolate yourself
you'll need to fork a process that does all the work.


-a
--
===============================================================================
| ara [dot] t [dot] howard [at] noaa [dot] gov
| all happiness comes from the desire for others to be happy. all misery
| comes from the desire for oneself to be happy.
| -- bodhicaryavatara
===============================================================================
 
A

ako...

i understand abouth threads. i think i am just researching the problem
so far. would you explain about Object?

i tried this:

module A
class Object
def mymethod(x) puts "#{x} in A" end
end

code = <<CODE
class Object; end
o = Object.new
#o.mymethod(p) # -> fails
class Object
def mymethod(x) puts "\#{x} in code" end
end
o = Object.new
o.mymethod(p)
CODE

x = 'x'
y = 'y'

module T
class << self
def get_binding(p) binding end
end
end

eval("#{code}", T.get_binding(y))

o = Object.new
o.mymethod(x)

remove_const :T
end
 
A

ako...

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,200
Messages
2,571,046
Members
47,646
Latest member
xayaci5906

Latest Threads

Top