$SAFE = 5 and Safe Ruby Misleading?

I’ve not really played around with $SAFE or security
in general a lot, so someone can overrule me if I’m
making mistakes.

However, where exactly are you getting this modified
String from? It seems like what you’re saying is, you
want to be able to run some arbitrary script, have it
give an object back to you, and then call the methods
of that object. Is that correct?

I’m not really sure of a good response to that
requirement. In one case, you’d know what’s in the
script, so you wouldn’t have to worry about it. If
you’re just running random code and accepting values
back, then that seems like an unsafe decision in
general, not necessarily a Ruby problem.

(Digression)
Take an example I learned in a computer security class
at college. It involves Java serialization. Basically,
if you have a client/server model that communicates
with serialized objects, you can build a malicious
object and send it to the server, as follows:

class MaliciousObject
{
static
{
doMaliciousStuff();
}
}

Since the class is sent and loaded along with the
serialized object, when the class gets loaded, the
server/client is compromized. There are lots of ways
you can try to work around this (none of which
really work 100%), however, it seemed to me at the
time that the real problem was that this server was
just accepting raw objects as input from an arbitrary
source. If you just communicate the data inside
the object, you’re fine, but as soon as you transfer
the objects themselves, it can lead to trouble.

I think the bottom line may be that you shouldn’t
be accepting and using objects from arbitrary sources.
You don’t even need Ruby’s capabilities to kill
someone in this manner, since (assuming you could)
if you subclassed String in Java, you could pass it
somewhere that accepts a String, only in your
class charAt(int) has a method body of
System.exec(“rm -rf /”);

$SAFE allows you to contain malicious code and
keep it from damaging the system, but if you allow
code to be passed out to a trusted environment,
then I think the burden would be on you to make
sure somehow that the object isn’t malicious (not
that that’s even possible, like you’ve said).

It seems to me that what you’re asking would take
a very complex security system (Java’s system is
quite complex and it doesn’t get it right), and
I don’t think $SAFE was designed to cover it.

But as I said, I’m no expert on $SAFE. Could it
be made so that objects could be marked as tainted
so that their methods couldn’t execute system(“rm -rf /”)?
Is that already the case? It’s an interesting topic.

Hope this helps.

  • Dan

djd15@cwru.edu wrote:

$SAFE allows you to contain malicious code and
keep it from damaging the system, but if you allow
code to be passed out to a trusted environment,
then I think the burden would be on you to make
sure somehow that the object isn’t malicious (not
that that’s even possible, like you’ve said).

It seems to me that what you’re asking would take
a very complex security system (Java’s system is
quite complex and it doesn’t get it right), and
I don’t think $SAFE was designed to cover it.

Yeah. There is no way to check. Further, there is no way to even be sure your
data from even a simple case is secure? Pass out a string? No, that won’t
work, they overrided =~. Badness. A hash is just as bad. It’s almost
impossiboe to detect a really devious person doing this, since they could
uncode and execute highly obfuscated strings.

Which is why I’m curious what $SAFE = 4 is meant to do? It’s easy to make it
so that code runs in a box, what’s difficult is to get data out of that box.

The only solution I can think of is for an object’s methods and class methods
to be frozen (no aliasing, no adding, no removing) but its variables still be
mutable. With that constraint, with some care you could write a Ruby program
that could execute totally untrusted code and return meaninful values from it.

···


Dave Fayram
kirindave@lensmen.net
Developer / Idealist

Scripsit ille »Dave Fayram« kirindave@lensmen.net:

$SAFE allows you to contain malicious code and
keep it from damaging the system, but if you allow
code to be passed out to a trusted environment,
then I think the burden would be on you to make
sure somehow that the object isn’t malicious (not
that that’s even possible, like you’ve said).

It seems to me that what you’re asking would take
a very complex security system (Java’s system is
quite complex and it doesn’t get it right), and
I don’t think $SAFE was designed to cover it.

Yeah. There is no way to check. Further, there is no way to even be sure your
data from even a simple case is secure? Pass out a string? No, that won’t
work, they overrided =~. Badness. A hash is just as bad. It’s almost
impossiboe to detect a really devious person doing this, since they could
uncode and execute highly obfuscated strings.

Hm… what about:

str = /.*/m.match(str)[0]

BTW, why this:

irb(main):001:0> a = “Hello”
=> “Hello”
irb(main):002:0> class << a
irb(main):003:1> def =~(x)
irb(main):004:2> p [:you, :lose]
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> a =~ /./
=> 0
irb(main):008:0> a.=~ /./
[:you, :lose]
=> nil

What did I do wrong?

···

djd15@cwru.edu wrote:


0 >Array { 0 >c 0 >n { >p { >a { >c } { *c } *a set === IF XCALL } { # ~
{ &Array >n } { } *n 0 === IF VCALL *p 1 - &n } *p 0 == IF XCALL } } # \

Array &Array >a " another " set 17 &a " hacker" set 23 &a Folth set # _/ _
42 &a get 23 &a get 42 &a get 17 &a Just PRINT PRINT PRINT PRINT ; ; PRINTLN

Which is why I'm curious what $SAFE = 4 is meant to do? It's easy to make it
so that code runs in a box, what's difficult is to get data out of that box.

Try something like this

   def a_verifier(str)
      Thread.new do
         $SAFE = 4
         res = begin
                  eval str
               rescue Exception
                  $!
               end
         begin
            res = ::String.new(res.to_s)
            # add some test if you want
         rescue Exception
            "unknown error"
         end
      end.value
      # here you have a String object which is tainted
      # be carefull with it
   end

the basic idea
   * protect it against any error to control the result
   * never trust the result : if you want a String, you explicitely create
     it with a method that you can trust (::String::new in my case)
   * never try to correct an error : in my case don't try to interpret the
     error in the second begin ... rescue ... end (it return "unknown
     error")

Guy Decoux

Rudolf Polzer wrote:

BTW, why this:

irb(main):001:0> a = “Hello”
=> “Hello”
irb(main):002:0> class << a
irb(main):003:1> def =~(x)
irb(main):004:2> p [:you, :lose]
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> a =~ /./
=> 0
irb(main):008:0> a.=~ /./
[:you, :lose]
=> nil

What did I do wrong?

This seem to work if you put the regexp with brackets:

a=~ (/./)

My guess it’s an assumption by the parser used to speed up execution,
where you have a variable in the left, and regexp syntax on right, with
no additional evaling requirements(such as brackets)

See this code:
a=“aaa”
1.upto(1000000) do
a=~/./
end

time:
real 0m2.992s
user 0m2.860s
sys 0m0.060s

Add brackets and you have:

real 0m3.362s
user 0m3.300s
sys 0m0.050s

Idan

This was the part I was missing, and thanks to everyone who helped me out by
pointing me to it.

I’m investigating using Ruby for a mobile agents platform, and for such a
project, that sort of assurance is important.

···

ts decoux@moulon.inra.fr wrote:

        res = ::String.new(res.to_s)


Dave Fayram
kirindave@lensmen.net
Developer / Idealist