Save the world from evil code crusade

R

Robert Klemme

David A. Black said:
Hi --



It's OK -- I just think you may find that whatever you end up doing is
going to be a clever hack anyway, so don't necessarily discard all of
them :)

I wonder actually whether you will have to write a sort of pre-parser,
and check the code itself, and then eval it. And then of course
create some configurable way to do this.

One major problem when checking code is that checking names is not
sufficient. IMHO you cannot solve this by changing the Ruby interpreter
because you must trap into method calls for certain classes (File and Socket
come to mind) and check which binary method actually gets called. And that
doesn't even deal with user defined C extensions - I gues you would have to
prohibit them altogether.
If you do, I guess it would be "do not allow" by default -- so that if
someone tried to sneak in something bad (by doing some kind of
scrambling or method renaming or something), it would just be ignored.

Although I agree from a security perspective that could mean that
extensibility and maybe user defined classes suffer...

Regards

robert
 
L

Logan Capaldo

Has anyone in this thread seen E? I get the feeling that a capability
based security model could be retrofitted onto ruby with some (a lot
of) work. Here's one possible way I can see of doing it

All methods that allocate / initialize new objects would not return
the object. Instead they would return a "name" some kind of unique
symbol (generated ala gen_sym in lisp). There would then be some kind
of object that kept track of the name -> object mappings. This leaves
you with 1 object to audit and secure instead of hundreds. You then
provide some mechanism for objects to have capabilities granted to
them. You would grant capabilites to objects, NOT "name"s. The name
would have a reference this master mapping object. the master mapping
object would nto have any way to change what object was pointed to by
what name. Any mehtod called on a "name" would turn into master_object
["name"].check_capabilites_and_send(meth_sym, *args)
The result objects of this call would be another "name" again. You'd
never get back the actual object you were working with so even if you
hacked "name" to add evil methods it wouldn't matter since you can't
send any message directly to the referenced object. You can't even
get a reference to it. I leave implementing this as an exercise to
the reader ;)
 
A

aurelianito

It's the first item of a lot of personal projects (dreams) I have.
I'll name a few:

* Hosting services can set permissions for each ruby on rails (or
another ruby framework) application. All the applications run in the
same Apache instance.
* Ruby Web Start (combine the securedruby extension with rubygems and
voila!, ruby applications run from any web-server and they don't break
the user's computer).
* Implement a pure ruby version of the Seti program. Users would
download several projects simultaneously and they don't have to bother
to secure their machines from the code that is downloaded to them.

All these projects need fine grained security constraints like the Java
Virtual Machine has. But Ruby is a better language than Java and I
prefer to be able to run the code in a free platform (free as defined
by the Free Software Foundation). And I prefer to use an existing
language than start from scratch.

And, by the way, this is a personal project. I wanted to have the
feeling to program in a highly dynamic language. Most of my
professional experience is in Java and I feel I'm missing something.
That's why I'm tackling a problem that goes to the root of the
language.

I'm glad you asked.
Aureliano.
 
J

James Edward Gray II

And, by the way, this is a personal project. I wanted to have the
feeling to program in a highly dynamic language. Most of my
professional experience is in Java and I feel I'm missing something.
That's why I'm tackling a problem that goes to the root of the
language.

I really don't mean any offense by this, but you picked a pretty un-
Ruby-ish problem to start with, I think. That might make it hard to
learn Ruby and its merits.

I really don't mean to discourage you though. Best of luck with your
projects.

James Edward Gray II
 
K

Kero

One major problem when checking code is that checking names is not
sufficient. IMHO you cannot solve this by changing the Ruby interpreter
because you must trap into method calls for certain classes (File and Socket
come to mind) and check which binary method actually gets called. And that
doesn't even deal with user defined C extensions - I gues you would have to
prohibit them altogether.


Although I agree from a security perspective that could mean that
extensibility and maybe user defined classes suffer...

Could a SeLinux sandbox (or binding) work?

I've never used SE Linux, so my suggestion may be dumb.

+--- Kero ------------------------- kero@chello@nl ---+
| all the meaningless and empty words I spoke |
| Promises -- The Cranberries |
+--- M38c --- http://members.chello.nl/k.vangelder ---+
 
A

aurelianito

1. What exactly will your security model look like? I'm not even sure that
you have a clear vision yet so maybe before dealing with implementations it
might be more helpful to first make clear what will be allowed, what not and
how you want to separate areas of different capabilities.

A permission is an object with the method implies?( permission )

implies?( permission ) returns true iff self implies permission. For
instance:

TRUE == write_in_any_file.implies?( write_in_foo_txt ).
FALSE == write_in_any_file.implies?( format_hard_disk ).

Of course, TRUE == a.implies( a ).

When a protected operation is tried, the method am_i_allowed?(
permission ) is executed. This method returns FALSE if the "current
permissions" do not imply permission.

Each source code file will be asociated with a permission. The current
permission is the AND of the implies of the permissions of all the
files in the calling stack.

For instance.
Lets imagine:
a.rb: has format_hard_disk && write_in_any_file permissions (*).
b.rb: has write_in_any_file permission (*).

(*) assume that the ruby process is running as root for a moment.

And this is the (pseudo)code:
a.rb:
def foo()
format_hard_disk()
end

b.rb:
require "a"
require "securedruby"
configure_permissions()
foo()

When "ruby b.rb" is run. The following process happens:
1 - foo is defined.
2 - Secured ruby is loaded.
3 - permissions for the files are set.
4 - foo is started. (STACK: b.rb, a.rb)
4.1 - format_hard_disk is started (STACK: b.rb - a.rb - ???).
4.2 - check if b.rb permissions imply format_hard_disk. (pass)
4.3 - check if a.rb permissions imply format_hard_disk. (NO pass)
4.4 - throw security exception (formatting is NOT EXECUTED).
2. When 1 is answered this one might become more clear: what security you
need is not possible using threads with different $SAFE levels?

That one is easy. Run untrusted libs/servlets/scripts without
modification and be able to box them without requiring the client code
to be any different as it would be if it's trusted.

Regards and thank's for your time and attention,
Aureliano.

PS: just having this conversation with all of you is making clearer for
me the picture of what I want to do. Thank you very much.
 
J

James Britt

James said:
I really don't mean any offense by this, but you picked a pretty un-
Ruby-ish problem to start with, I think. That might make it hard to
learn Ruby and its merits.

Or, viewed another way, trying to get something to do the sorts of
things it isn't typically inclined to do can be enlightening.

For example, if you wanted to turn Java into a fluid, dynamic language,
you'd certainly have to learn about all the restrictive, cramped parts.

Modifying or wrapping Ruby to make is less fluid and dynamic seems quite
an educational task; for starters, you'll probably end up knowing the
internals far better than most users.

James

--

http://www.ruby-doc.org - The Ruby Documentation Site
http://www.rubyxml.com - News, Articles, and Listings for Ruby & XML
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys
 
J

Joel VanderWerf

aurelianito wrote:
...
a.rb:
def foo()
format_hard_disk()
end

b.rb:
require "a"
require "securedruby"

These two lines in b.rb should be in the reverse order, shouldn't they?
How else would you protect against an a.rb lke this:
 
A

aurelianito

a.rb:
These two lines in b.rb should be in the reverse order, shouldn't they?
How else would you protect against an a.rb lke this:

You are completly right!

I was trying to show my secured ruby idea and ended up with a security
hole! (ups!).
 
J

Joel VanderWerf

aurelianito wrote:
...
Each source code file will be asociated with a permission. The current
permission is the AND of the implies of the permissions of all the
files in the calling stack.

The call stack would not be enough. Another .rb file might have been
required or loaded, and it (or a method defined in it) may have done
something that allows a method called later to violate its permissions.
For example, the required file might open a file at an insecure
location. The method that did this may no longer be on the stack when
the file is written to (assuming permissions allow writing to an already
open file handle).
 
A

aurelianito

..
The call stack would not be enough. Another .rb file might have been
required or loaded, and it (or a method defined in it) may have done
something that allows a method called later to violate its permissions.
For example, the required file might open a file at an insecure
location. The method that did this may no longer be on the stack when
the file is written to (assuming permissions allow writing to an already
open file handle).

I don't see why the stack isn't enough if method redefinition is not
allowed when there are not enough rights. If the file is open there,
it's a choice of the programmer. Notice that any direct action to open
the file would include the caller in the stack. And for indirect
actions, method redefinitions are disabled, so I can't think of any.
Can you show me another example?
 
S

Sean O'Halpin

I think saving the world from evil code is a laudable aim, but if
you're ever going to save us from things like this:

ruby -e'$><<([*$<]|[])' infile > outfile

then you'll have to whack Mark Hubbart (see ruby-talk/159649) which I
doubt is possible even in E.

Sean

P.S. ;)
 
R

Robert Klemme

aurelianito said:
A permission is an object with the method implies?( permission )

implies?( permission ) returns true iff self implies permission. For
instance:

TRUE == write_in_any_file.implies?( write_in_foo_txt ).
FALSE == write_in_any_file.implies?( format_hard_disk ).

Of course, TRUE == a.implies( a ).

When a protected operation is tried, the method am_i_allowed?(
permission ) is executed. This method returns FALSE if the "current
permissions" do not imply permission.

So basically you control method execution.
Each source code file will be asociated with a permission. The current
permission is the AND of the implies of the permissions of all the
files in the calling stack.

You mean, permissions are inherited down the call stack? What do you think
how efficient that will be? The set of permissions potentially changes with
every method invocation...
For instance.
Lets imagine:
a.rb: has format_hard_disk && write_in_any_file permissions (*).
b.rb: has write_in_any_file permission (*).

(*) assume that the ruby process is running as root for a moment.

And this is the (pseudo)code:
a.rb:
def foo()
format_hard_disk()
end

b.rb:
require "a"
require "securedruby"
configure_permissions()
foo()

When "ruby b.rb" is run. The following process happens:
1 - foo is defined.
2 - Secured ruby is loaded.
3 - permissions for the files are set.
4 - foo is started. (STACK: b.rb, a.rb)
4.1 - format_hard_disk is started (STACK: b.rb - a.rb - ???).
4.2 - check if b.rb permissions imply format_hard_disk. (pass)
4.3 - check if a.rb permissions imply format_hard_disk. (NO pass)
4.4 - throw security exception (formatting is NOT EXECUTED).

IMHO you will never get this to work reliably without modifying Ruby
interpreter code. Because of Ruby's dynamic nature you won't be able to
catch all method renamings. Also, there is no way to safely intercept every
method call AFAIK. Maybe you can try to do something with set_trace_func
though...
That one is easy. Run untrusted libs/servlets/scripts without
modification and be able to box them without requiring the client code
to be any different as it would be if it's trusted.

??? With $SAFE you don't need to change libs either.
Regards and thank's for your time and attention,
Aureliano.

PS: just having this conversation with all of you is making clearer
for me the picture of what I want to do. Thank you very much.

Cheers

robert
 
A

aurelianito

Yes! I want to implement in ruby something like this. The description
there is a copy of the Java security manager.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: Save the world from evil code crusade"

|> Sandboxed and all.
|
|Guaranteed?

No. But we are trying to make it as safe and useful as possible, as
other parts of Ruby.

matz.
 
E

Eivind Eklund

Has anyone in this thread seen E? I get the feeling that a capability
based security model could be retrofitted onto ruby with some (a lot
of) work.

Excepting ObjectSpace, Ruby/DL, and C extensions, Ruby already has a
capability based security model. You can only send a message to (call
a method on) an object you have a reference to, so an object reference
is a capability.

Of course, some other kind of capability model might also be
interesting - just thought I'd mention that we already have one.

Eivind.
 
E

Eivind Eklund

That's why I've started the securedruby project
(http://securedruby.rubyforge.org). My problem is that I'm unable to
stop "evil code" to modify already defined classes. The idea of the
library (already only vaporware) is to redefine all the potentially
dangerous operations in the core library to check for permissions.

Setting up a list of "unsafe" methods (blacklisting) is likely to
break when more methods are added. Instead, I'd set up a list of
*safe* methods (whitelisting).

For the unsafe code part, I'd use safemode and proxy over the classes
you want to allow by overriding the MyClass constant with a pure
method_missing based proxy. The proxyied calls can be filtered for
security on the "outside", where only your code runs.

Eivind.
 
J

James Britt

Yukihiro said:
Hi,

In message "Re: Save the world from evil code crusade"

|> Sandboxed and all.
|
|Guaranteed?

No. But we are trying to make it as safe and useful as possible, as
other parts of Ruby.

Can't ask for more than that!


Thanks,


James

--

http://www.ruby-doc.org - The Ruby Documentation Site
http://www.rubyxml.com - News, Articles, and Listings for Ruby & XML
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys
 

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
473,773
Messages
2,569,594
Members
45,119
Latest member
IrmaNorcro
Top