Arbitrary system files readable in 1.0.4 - 1.1.2

I just found a vulnerability in one of my web apps that was running
Mongrel 1.1.2 where I could go to URIs like
/.%252e/.%252e/.%252e/.%252e/.%252e/.%252e/.%252e/etc/passwd and it
would serve the actual /etc/passwd file.

The issue seems to be in lib/mongrel/handlers.rb in the change from
1.0.3 to 1.0.4

   req_path = HttpRequest.unescape(path_info)
  •  if @path
    
  •    req_path = File.expand_path(File.join(@path, path_info), @path)
    
  •  else
    
  •    req_path = File.expand_path(req_path)
    
  •  end
    
  •  if req_path.index(@path) == 0 and File.exist? req_path
    
  •    # it exists and it's in the right location
    
  •  # Add the drive letter or root path
    
  •  req_path = File.join(@path, req_path) if @path
    
  •  req_path = File.expand_path req_path
    
  •  if File.exist? req_path
    
  •    # It exists and it's in the right location
       if File.directory? req_path
    

The main difference is that “req_path.index(@path) == 0” is removed,
which seems to be the cause of the vulnerability.

Adding that check back in fixes it in 1.1.2, but may cause issues on
Windows (I haven’t checked)

Also, downgrading to 1.0.3 fixes it.

I guess expand_path doesn’t interact well with HTTP escaping.

This is pretty critical, can you file a ticket against it?

Evan

Also, attaching a diff with a failing test would totally rock.

Evan

On Fri, 28 Dec 2007 19:28:25 -0500
“Evan W.” [email protected] wrote:

I guess expand_path doesn’t interact well with HTTP escaping.

This is pretty critical, can you file a ticket against it?

No, you’re miss-reading the change set. The 1.0.4 change removed the
expand path on one of the conditions, so now it’s using a relative
path on one of the if branches, AND removed the check that ensured the
expanded_path began with the expanded web root. Notice mine has
expand_path on the if and the else so it’s always done, and then makes
sure that the expanded path begins with the web root.

No matter what you do, you must expand path all paths before you do
any comparisons or reads and never use an indirect path. It might be
better to setup any paths you’re doing, and then the very last thing is
always expand path.

Here’s the change again:

  •  req_path = HttpRequest.unescape(path_info)
    
  •  if @path
    
  •    req_path = File.expand_path(File.join(@path, path_info), @path)
    
  •  else
    
  •    req_path = File.expand_path(req_path)
    
  •  end
    
  •  if req_path.index(@path) == 0 and File.exist? req_path
    
  •    # it exists and it's in the right location
    
  •  # Add the drive letter or root path
    
  •  req_path = File.join(@path, req_path) if @path
    
  •  req_path = File.expand_path req_path
    
  •  if File.exist? req_path
    
  •    # It exists and it's in the right location
       if File.directory? req_path
    

Notice the - lines have “if req_path.index(@path) == 0…” that’s the
part that ensures that the given path (after expansion) begins with the
path being used by the web server as the root. If you don’t have the
required expand path before this, AND make sure that the beginning of
the expanded path is always the root path, then you have this bug.

I haven’t looked at the real code yet, but don’t wait for a patch, fix
this and I’d say remove the gem so it doesn’t go out more since it’s a
huge vulnerability.

In fact, pushing out a 1.0.5 that reverts this change to fix it and
doing it now is probably the best.


Zed A. Shaw

New gems are out. You can downgrade to 1.0.3 or you can upgrade to
1.0.5 or 1.1.3. Versions prior to 1.0.4 are not affected.

Thanks,

Evan

On Dec 28, 2007 7:01 PM, Eric M. [email protected] wrote:

I just found a vulnerability in one of my web apps that was running
Mongrel 1.1.2 where I could go to URIs like
/.%252e/.%252e/.%252e/.%252e/.%252e/.%252e/.%252e/etc/passwd and it
would serve the actual /etc/passwd file.

The issue seems to be in lib/mongrel/handlers.rb in the change from
1.0.3 to 1.0.4

can you download and install the 1.1.3 gem I put online from here:

http://mmediasys.com/mongrel/mongrel-1.1.3.gem

and let me know if it worked before we put it on rubyforge.

also, knowing the Dir.pwd of your public doc root will be good, or a
test case showing the problem, since I couldn’t reproduce the behavior
you described under Windows.

(I know there isn’t /etc/passwd on windows, tried other file) :smiley:

Please let me know ASAP.


Luis L.
Multimedia systems

A common mistake that people make when trying to design
something completely foolproof is to underestimate
the ingenuity of complete fools.
Douglas Adams

Also, I want to reiterate that people should subscribe to the RSS
feed, where handy release announcements come directly to you.

http://mongrel.rubyforge.org/rss.xml

Evan