Professional Web Applications Themes

$SAFE = 5 and Safe Ruby Misleading? - Ruby

Hey folks. With all this talk of duck typing and such, I got to thinking about some of my code that I *thought* executed untrusted code relatively safely. I'd run code in a safe 5 thread, let it return a string, which I checked to make sure it was a "string." However, I now realize that concept is totally meaningless. Unless I'm misunderstanding, doesn't this mean that there is no such thing as an object you can "trust" in an absolute sense in Ruby. So, with that in mind, someone could return an object that, to me as the receiver, ...

  1. #1

    Default $SAFE = 5 and Safe Ruby Misleading?

    Hey folks.

    With all this talk of duck typing and such, I got to thinking about some of my
    code that I *thought* executed untrusted code relatively safely. I'd run code
    in a safe 5 thread, let it return a string, which I checked to make sure it
    was a "string." However, I now realize that concept is totally meaningless.

    Unless I'm misunderstanding, doesn't this mean that there is no such thing as
    an object you can "trust" in an absolute sense in Ruby. So, with that in mind,
    someone could return an object that, to me as the receiver, looks like a
    string, but its actual purpose is to iterate over every object in the runtime,
    looking for database like objects, with the purpose of destroying them. Or it
    could try and erase as much as it could.

    I know these objects come out tainted, but in order to use them, we need to
    untaint them. How do I know that that .upper! isn't actually going to destroy
    me?

    The only way I can see it is if an object could be "partially frozen", its
    methods and class methods locked, but its member variables not locked. I don't
    think there's any way to achieve this.

    Am I totally wrong about this?

    --
    Dave Fayram
    [email]kirindavelensmen.net[/email]
    [email]dfayra00umail.ucsb.edu[/email]
    Coder / Idealist
    --




    kirindave@lensmen.net Guest

  2. #2

    Default Re: $SAFE = 5 and Safe Ruby Misleading?

    On Tue, Aug 12, 2003 at 08:14:53AM +0900, [email]kirindavelensmen.net[/email] wrote:
    > With all this talk of duck typing and such, I got to thinking about some of my
    > code that I *thought* executed untrusted code relatively safely. I'd run code
    > in a safe 5 thread, let it return a string, which I checked to make sure it
    > was a "string." However, I now realize that concept is totally meaningless.
    >
    > Unless I'm misunderstanding, doesn't this mean that there is no such thing as
    > an object you can "trust" in an absolute sense in Ruby. So, with that in mind,
    > someone could return an object that, to me as the receiver, looks like a
    > string, but its actual purpose is to iterate over every object in the runtime,
    > looking for database like objects, with the purpose of destroying them. Or it
    > could try and erase as much as it could.
    >
    > I know these objects come out tainted, but in order to use them, we need to
    > untaint them. How do I know that that .upper! isn't actually going to destroy
    > me?
    You're right in one sense - if someone has rights to modify core objects and
    classes in your Ruby environment, then you're toast whatever way you look at
    it.

    However the assumption is that these tainted objects come from an external,
    non-Ruby environment, and typically this means they come in as objects of a
    fixed type, such as String. (Examples: data taken from ENV, data taken from
    stdin, data taken from CGI GET or POST parameters)

    So someone cannot send you a general Ruby object, unless you were to
    explicitly unmarshal it, and they cannot directly touch your Ruby run-time
    environment. If they could, they could redefine ENV to return whatever they
    like, or they could simply override methods in your classes to do whatever
    they liked.

    But given that you have (say) a string from an untrusted source, you can
    untaint it like:

    foo = ENV['HTTP_HOST']
    if /\A[a-zA-Z0-9.-]+\z/ =~ foo
    foo.untaint
    else
    raise "Oi! No!"
    end

    The idea being that maybe you want to do something like
    system("nslookup -q=a #{foo}")
    but clearly certain values of foo are dangerous, like "; rm -rf /*"
    The tainting mechanism just forces you to think about what values of a
    variable may or may not be safe before you use them in system calls,
    filenames, and such like.

    BEWARE: Don't use /^...$/ in this case, use /\A...\z/

    irb(main):001:0> evil = "foo\nrm -rf /*"
    => "foo\nrm -rf /*"
    irb(main):002:0> evil =~ /^foo$/
    => 0
    irb(main):003:0> evil =~ /\Afoo\z/
    => nil

    Regards,

    Brian.

    Brian Candler Guest

  3. #3

    Default Re: $SAFE = 5 and Safe Ruby Misleading?

    Hi,

    From: "Dave Fayram" <kirindavelensmen.net>
    > Basically, can I evaluate something in a $SAFE = 4 thread, then get it
    > out and work with it meaningfully? The only way I can think of is to
    > try yzing it in a $SAFE = 3 thread, where an object can't untaint
    > itself, but doing so for an object would be pretty much impossible.
    Not sure if this is sophisticated enough for your purpose - I just
    needed arbitrary code to be evaluated "safely" at $SAFE = 4, and have
    it be able to modify an object passed to it and return [some] result.

    def safe_eval(_eval_str, *_args)
    th = Thread.new(_eval_str, *_args) do |eval_str, *args|
    res, warning = nil, nil
    begin
    $SAFE = 4
    res = eval(eval_str)
    rescue Exception => ex
    warning = ex.to_s
    end
    [res, warning]
    end
    th.value
    end

    The calling code in my case is $SAFE = 1... not that the safe-level
    of the calling code should matter, I believe.

    For some reason, looking at my calling code, I wanted to pass in
    a tainted object so it could be modified directly by the $SAFE = 4
    thread via mutator methods (i.e. gsub!)... Though if one didn't
    want to allow any object passed to it to be modified, the evaluated
    code could presumably dup the object, modify the copy, and return
    the modified object as a result... (Unless that's asking too much
    of $SAFE = 4... I don't remember <grin>)

    Anyway what I'm doing is essentially:

    some_object_to_be_modified.taint # allow mutator methods in high SAFE levels
    res, warning = safe_eval(untrusted_code, some_object_to_be_modified)
    if warning
    log warning
    log untrusted_code
    end


    Hope this helps,

    Bill



    Bill Kelly Guest

  4. #4

    Default Re: $SAFE = 5 and Safe Ruby Misleading?

    Hi,

    In message "Re: $SAFE = 5 and Safe Ruby Misleading?"
    on 03/08/13, Dave Fayram <kirindavelensmen.net> writes:

    |Basically, can I evaluate something in a $SAFE = 4 thread, then get it
    |out and work with it meaningfully?

    The untrusted thread can return value from it. For example,

    def safe_eval(str)
    Thread.start {
    $SAFE=4
    eval(str) # the last evaluated value
    }.value # can be retrieved using "value" method
    end

    Besides, the thread can push data to the queue, then data is read by
    the trusted code, validated, then output.

    matz.

    Yukihiro Matsumoto Guest

  5. #5

    Default Re: $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 Guest

  6. #6

    Default Re: $SAFE = 5 and Safe Ruby Misleading?

    [email]djd15cwru.edu[/email] 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
    [email]kirindavelensmen.net[/email]
    Developer / Idealist
    --





    Dave Fayram Guest

  7. #7

    Default Re: $SAFE = 5 and Safe Ruby Misleading?



    ----- Original Message -----
    From: Dave Fayram <kirindavelensmen.net>
    Date: Tuesday, August 12, 2003 2:01 pm
    Subject: Re: $SAFE = 5 and Safe Ruby Misleading?
    > [email]djd15cwru.edu[/email] 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.
    >
    Well, I'm not sure I totally agree with that. It is
    possible to get data out of a $SAFE=4 process, as
    people have shown. However, you can only get data
    out, not functionality.

    For example, you have your $SAFE=4 thread do some
    calculations, and need to get that to the trusted
    thread. Depending on the data, you could dump it
    to a file, or set up a socket to send the raw data
    in either text or some sort of binary format. You
    can dump hashes to YAML, for example.

    What you can't do is send objects out of the $SAFE=4
    thread directly. Data is not the same as objects
    (which I think is really the key to solving your
    problem). Objects contain data and functionality,
    and the functionality is the real problem.

    Another way to do it (borrowing an idea that someone
    posted here already), is to pass the unknown object
    to a $SAFE=4 thread that you've programmed, and copy
    the relevant data into a known class. Like:

    safe = Thread.start {
    $SAFE=4
    str = untrusted.call
    String.new str
    }.value

    As Matz did. Now you know that the result is of
    class String, with no methods redefined (unless
    the untrusted code can redefine methods of core
    classes, in which case you can't execute any
    untrusted code anyway).

    This is a simplistic example, but you can always
    copy the relevant information if it's accessible.
    The only thing this prevents is the untrusted code
    providing new functionality by writing new classes
    or redefining methods in a non-malicious way, but
    I can't think of any real way to distinguish
    between the two (other than, as I said before,
    tagging each object as allowed or not allowed to
    perform certain operations in their methods, which
    would be very complex).

    So you can get data out of $SAFE threads safely.

    The only remaining case is if the data itself is
    malicious, but Ruby can't really handle that for
    you (i.e. make sure you're not calling "eval safe"
    above without first checking what string safe is).

    I don't think the situation is as hopeless as you
    surmise.

    Cheers.

    - Dan


    djd15@cwru.edu Guest

  8. #8

    Default Re: $SAFE = 5 and Safe Ruby Misleading?

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





    Idan Sofer Guest

  9. #9

    Default Re: $SAFE = 5 and Safe Ruby Misleading?

    >>>>> "D" == Dave Fayram <kirindavelensmen.net> writes:

    D> Which is why I'm curious what $SAFE = 4 is meant to do? It's easy to make it
    D> 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

    ts Guest

  10. #10

    Default Re: $SAFE = 5 and Safe Ruby Misleading?

    ts <decouxmoulon.inra.fr> wrote:
    > res = ::String.new(res.to_s)

    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.

    --
    Dave Fayram
    [email]kirindavelensmen.net[/email]
    Developer / Idealist
    --




    Dave Fayram Guest

Similar Threads

  1. how safe is code in .dcr/.dxr
    By Peter Blumenthal in forum Macromedia Director Lingo
    Replies: 16
    Last Post: November 26th, 06:09 AM
  2. Time: safe way to go to next day?
    By Emmanuel Touzery in forum Ruby
    Replies: 7
    Last Post: October 21st, 07:22 PM
  3. Replies: 2
    Last Post: September 16th, 09:32 AM
  4. Web-Safe Colo(u)rs?
    By Mac McDougald in forum Adobe Photoshop Elements
    Replies: 1
    Last Post: July 19th, 03:08 AM

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139