Ask a Question related to Ruby, Design and Development.
-
kirindave@lensmen.net #1
$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]kirindave@lensmen.net[/email]
[email]dfayra00@umail.ucsb.edu[/email]
Coder / Idealist
--
kirindave@lensmen.net Guest
-
how safe is code in .dcr/.dxr
Would i be right in thinking that it's much more difficult to decompile director movies distributed as .exe or .dxr or streamed as .dcr than it is... -
Implementing the tape safe enum pattern in Ruby.
I rather like the type safe enum pattern in Java, and I was wonding how I might implement it in Ruby. Here is an example (based on the earlier OO... -
TCPSocket & $SAFE=1
In a mod_ruby script I am trying to lookup an ip address with TCPSocket.gethostbyname But it fails - even though the same script in batch mode... -
What exactly is safe mode?
Hello, Can someone point me to somewhere that explains what safe mode is and why it's good to have it on or off? Tried to get an explanation on... -
Web-Safe Colo(u)rs?
IMHO, web safe palette is now very much outdated concept. Mac -
Brian Candler #2
Re: $SAFE = 5 and Safe Ruby Misleading?
On Tue, Aug 12, 2003 at 08:14:53AM +0900, [email]kirindave@lensmen.net[/email] wrote:
You're right in one sense - if someone has rights to modify core objects and> 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?
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
-
Bill Kelly #3
Re: $SAFE = 5 and Safe Ruby Misleading?
Hi,
From: "Dave Fayram" <kirindave@lensmen.net>Not sure if this is sophisticated enough for your purpose - I just> 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 analyzing it in a $SAFE = 3 thread, where an object can't untaint
> itself, but doing so for an object would be pretty much impossible.
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
-
Yukihiro Matsumoto #4
Re: $SAFE = 5 and Safe Ruby Misleading?
Hi,
In message "Re: $SAFE = 5 and Safe Ruby Misleading?"
on 03/08/13, Dave Fayram <kirindave@lensmen.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
-
djd15@cwru.edu #5
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
-
Dave Fayram #6
Re: $SAFE = 5 and Safe Ruby Misleading?
[email]djd15@cwru.edu[/email] wrote:
Yeah. There is no way to check. Further, there is no way to even be sure your> $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.
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]kirindave@lensmen.net[/email]
Developer / Idealist
--
Dave Fayram Guest
-
djd15@cwru.edu #7
Re: $SAFE = 5 and Safe Ruby Misleading?
----- Original Message -----
From: Dave Fayram <kirindave@lensmen.net>
Date: Tuesday, August 12, 2003 2:01 pm
Subject: Re: $SAFE = 5 and Safe Ruby Misleading?
Well, I'm not sure I totally agree with that. It is> [email]djd15@cwru.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.
>
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
-
Idan Sofer #8
Re: $SAFE = 5 and Safe Ruby Misleading?
Rudolf Polzer wrote:
This seem to work if you put the regexp with brackets:>
>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?
>
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
Idan Sofer Guest
-
ts #9
Re: $SAFE = 5 and Safe Ruby Misleading?
>>>>> "D" == Dave Fayram <kirindave@lensmen.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
-
Dave Fayram #10
Re: $SAFE = 5 and Safe Ruby Misleading?
ts <decoux@moulon.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]kirindave@lensmen.net[/email]
Developer / Idealist
--
Dave Fayram Guest



Reply With Quote

