Ruby on Rails Security

Posted by Jonathan

Recently I've been made aware of people inside US Government organizations using my Ruby on Rails Security presentation as an excuse to limit Ruby on Rails adoption and projects inside those organizations.

They mandate that applications in Rails should be redone in Java because of the issues I covered.

It is not clear to me how anybody who saw/read this presentation would come to the conclusion that Rails is insecure. Every application is vulnerable. Some more and some less. Yes, this means that also Java applications are attackable.

It is my honest and strong belief that Ruby on Rails applications are not less secure than any other web application. In contrast, the Ruby on Rails framework provides several advanced security mechanisms that make it very easy to write secure applications. Further, Ruby on Rails enables very sane security options by default. Some of those defaults include auto-escaping, prevent Cross-Site Request Forgery, or protected from SQL-injection. I would even go as far as to state that the typical Rails application is more secure than the typical web application for those reasons.

The security of an application stands and falls with the knowledge and abilities of the people implementing and running it. Rails makes it very easy to write secure applications.

The conclusion I came to in my presentation still holds:

Ruby is by no means a "web app security silver bullet" but adding security is easy and not a pain like in many other frameworks

I hope this will change the opinion of some people and remove my presentation as their argument.

RailsConf Europe 2008

Posted by Jonathan

Day two of RailsConf Europe 2008 is over and so are my two sessions.

On tutorial day Mathias and I did a 4h workshop on deploying and monitoring Rails applications. The tutorial went really well, apart from the AirportExpress base station not coping with 100 laptops connecting to it. In the practical part we had a FreeBSD server with 40 virtual machines running and helped the audience deploy an example application with git or svn and Mongrel or mod_rails.

On day two I held my Security on Rails session where I go over the various attacks and countermeasures against Rails applications. This session was also well received and I hope I could educate people a bit about WebAppSecurity.

The slides are available as PDF here: Security on Rails (PDF) Deploying and Monitoring Rails (PDF)

Further, you can find both presentations at slideshare.

Security On Rails
View SlideShare presentation or Upload your own. (tags: ruby rubyonrails)


The slides are available as PDF here: Security on Rails (PDF) Deploying and Monitoring Rails (PDF)

If you attended one of my sessions, I encourage you to rate them at the conference site.

So far my expectations have been met and I've could catch up with a lot of people. I'm looking forward to day three of RailsConf Europe!

Why you should upgrade to Rails 2.1

Posted by Jonathan

If you are following my twitter stream you have probably read me rambling about a security hole in Rails < 2.1.

With Rails 2.1 out for a while I though I should describe the problem a bit as there are many Rails applications stuck in 1.2.3 or 2.0.

The story begins with me giving a talk about Ruby on Rails security at Dynamic Languages World Europe in Karlsruhe. While talking about SQL injection, Tobias Schlottke, a fellow German Rails developer, mentioned that he once saw ActiveRecord allowing random SQL in the :offset option. Everybody in the room agreed that shouldn't be possible or wanted.

Later that day I sat down with Steven Bristol and we tried to verify this. Playing with MySQL we only got ActiveRecord::StatementInvalid exceptions. So I installed PostgreSQL (damn you MacPorts!) and tried the same with PostgreSQL and SQLite. Both times I got straight SQL Injection. Looking through the code I found out that the :offset parameter was also prone to SQL injection:

# vulnerable controller code
User.find(:all, :limit => params[:limit])

User.find(:all, :limit => 10, :offset => params[:offset])

# with params[:offset] or params[:limit] set to '; DROP TABLE users;'
# you got a big problem ...

MySQL is also affected but thanks to its default setting of disallowing multiple SQL statements per API-call you cannot insert a new SQL clause. You can just manipulate the one executing by setting other parameters (like an offset).

I've seen a lot of code taking the :offset parameter straight from the HTTP params and all people I talked with thought of it being a big problem. Especially since the documentation speaks about :offset and :limit being two integers (so you, or at least I, would expect auto-quoting like done for User.find(params[:id])).

I wrote a patch that casts both :limit and :offset to integers and sent it to core@ hoping for it to be included before Rails 2.1. After a bit of discussion if this is a bug or a feature (as apparently :limit can also be '1, 5'), Steven got hold of DHH at RailsConf and of Aaron Bedra who apparently discovered this problem a couple of months ago. Steven wrote a new patch with Aaron that also checks for the '1, 5' syntax of :limit and David committed it for Rails 2.1.

Unfortunately the committed patch didn't include fixing the MySQL adapter as it overrides the method that adds the limit and offset options. This isn't as bad as it sounds as MySQL will not allow multiple SQL statements by default. David later committed a patch of mine fixing MySQL in edge (and the coming 2.1.1).

So if the new features alone will not bring you to Rails 2.1, here you have another reason to upgrade.

For you guys stuck in 1.2 or 2.0 land, either always cast your :limit and :offset options, or use those patches:

Rails 1.2.3 patch

Rails 2.0.2 patch

P.S: You guys using will_paginate are save as it casts the parameters by default.

24C3 - Ruby on Rails Security

Posted by Jonathan

The slides and a video of my Ruby on Rails Security session are now online. The 24C3 was a lot of fun, unfortunately I couldn't spend all 4 days there.

My talk covered most of the common web application vulnerabilities like Cross Site Scripting and Cross Site Request Forgery, SQL and Code injection, and deployment security and how they apply to Rails. Further Ruby on Rails specific issues like Rails plugin security, JavaScript/Ajax security, and Rails configuration were be examined and best practice solutions were introduced.


The is also a Google video version: Ruby on Rails Security.



Get the slides (PDF - 1.6 MB) or the video (mkv - 95 MB). Other formats are available from the official mirrors or the torrent site.

Apache 2.2 authentication with mod_authnz_external

Posted by Jonathan

The last couple of hours I’ve been fighting with Apache 2.2 authentication. I must say, it is really a mess, hard to debug, and very frustrating. Many times I wished for Lighttpd to have SVN/DAV and authentication modules.

The scenario is simple, you have a server that hosts your subversion repository and you with to serve it through HTTPS for security and access control. Subversion repositories can be served through the svn+ssh access method but this is inflexible (you can not control access to certain branches of the repository at least with subversion <= 1.2) and not properly encrypted (AFAIK svn+ssh only encrypts the login/authentification but not the actual data stream) as you need SSH/shell access for every user. So there is only the HTTPS access method left. And only Apache 2 as a server.

Because you need authentication for the subversion access the simplest thing to do is to create UNIX accounts on the server and let Apache authenticate with HTTPAuth over SSL against this database. You would think that the most common UNIX web server would be able to do this out of the box or at least with just one or two configuration knobs. But this is not the case, configuring Apache 2.2 to authenticate against UNIX shadow accounts is a major pain. Quite surprising if you look how easily dovecot (a POP/IMAP mail server, very young in comparison to Apache) does this out of the box.

There are are two recommended ways to do this, PAM (mod_auth_pam2) and using an external provider (mod_authnz_external). Both modules are not core modules and have their problems.

First there is PAM, the Pluggable Authentification Modules. The nice thing about PAM is that (apart from not being under active development any longer) it is itself a pain to configure and debug. Further in order to make mod_auth_pam(2) access to the shadow files you need to either run Apache as root (BIG MISTAKE!) or give the Apache user read access to the shadow files (also not a very good idea!). So after fighting with PAM for some time, I choose to try the second auth module, mod_authnz_external.

Mod_authnz_external hooks into the Apache auth systems and allows you to define external programs that get the username/password combination and return a status code. This way Apache can outsource the authentication process to an external program that has extra privileges and Apache can keep its low security profile. The author of mod_authnz_external ships such an external program, pwauth. Pwauth authenticates against UNIX shadow files or PAM (yea!). Pwauth runs as setuid root in order to do this but this is much better than running Apache as root or give Apache access to the shadow files.

At compile time you tell pwauth which users are allowed to use it (in my case the user www) and which auth backend to use (shadow or PAM). This can be tested like this (in (t)csh):

# sudo -u www pwauth ; echo $status 
username
correct_pw
0
# sudo -u www pwauth ; echo $status 
username
incorrect_pw
1
#

On FreeBSD the PAM config for pwauth looks like this:

# cat /etc/pam.d/pwauth
auth       required     pam_unix.so debug
account    required     pam_unix.so debug

(I thought that the same configuration would work for Apache, but it didn’t.)

When this is working for you, the fun part starts. Telling Apache how to use mod_authnz_external. In theory you just need to load it and explicitly use it in a part. But in reality it depends on other authz/authn modules and conflicts with others. Again not fun to debug. After a long period of trail and error I got it working. Comment out ALL authn/authz modules and load only these:

LoadModule auth_sys_group_module libexec/apache22/mod_auth_sys_group.so
LoadModule authnz_external_module libexec/apache22/mod_authnz_external.so
LoadModule auth_basic_module libexec/apache22/mod_auth_basic.so
LoadModule authz_host_module libexec/apache22/mod_authz_host.so

Now you can use it in your virtual host definitions. An important fact is that you need to redefine the external provider in every virtual host entry as it will not be inherited from the root node.

<VirtualHost  *:80>
  DocumentRoot "/usr/local/www/example.com/htdocs" 
  ServerName example.com

  <Directory "/usr/local/www/example.com/htdocs">
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
  </Directory>

  <Location />
    SSLRequireSSL
    AuthType Basic
    AuthName "Restricted" 
    AuthBasicProvider external
    AuthExternal pwauth
    require valid-user
    require group my_group
  </Location>

  AddExternalAuth pwauth /usr/local/bin/pwauth
  SetExternalAuthMethod pwauth pipe
</VirtualHost>

The important entries are the external provides definition with name of “pwauth” just before the closing VirtualHost and the “AuthBasicProvider external && AuthExternal pwauth” inside the Location.

This finally works as wanted. I still can not believe that UNIX auth is so hard with Apache 2.2.

With SSL and mod_dav_svn the virtual host looks like this:

<VirtualHost _default_:443>
  DocumentRoot "/usr/local/www/example.com/htdocs" 
  ServerName example.com:443

  <Directory "/usr/local/www/example.com/htdocs">
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
  </Directory>

  SSLEngine on

  SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL

  SSLCertificateFile /etc/ssl/certs/cert.pem
  SSLCertificateKeyFile /etc/ssl/private/cert.key

  SSLOptions +StdEnvVars +OptRenegotiate

  BrowserMatch ".*MSIE.*" \
      nokeepalive ssl-unclean-shutdown \
      downgrade-1.0 force-response-1.0

  <Location />
    SSLRequireSSL
    DAV svn
    SVNPath /usr/local/svn/my_repo
    AuthType Basic
    AuthName "Restricted" 
    AuthBasicProvider external
    AuthExternal pwauth
    require valid-user
    require group my_group
  </Location>

  AddExternalAuth pwauth /usr/local/bin/pwauth
  SetExternalAuthMethod pwauth pipe
</VirtualHost>

 

OpenBSD 4.1 is there!

Posted by Jonathan

At least if you ordered the pre-release…

My OpenBSD 4.1 CD set and T-shirts arrived today.

The lyrics and the 4.1 song (MP3 4.1MB / OGG 8.3MB) are already up.

The offical release will be at the beginning of May but you can order OpenBSD 4.1 today.

A list of changes between OpenBSD 4.0 and 4.1 can be found here. One very nice addition in my opinion is hoststated(8), a host status daemon that can be used with PF to manage load-balanced servers and remove servers from the pool if they do not serve a HTTP page or your specific monitor script fails.

This is a very interesting for people like me who use OpenBSD firewalls in front of Rails applications. With OpenBSD/PF/pfsync/hoststated you can very easily have redundant firewalls load-balance your Apaches and automatically exclude a host that is down for whatever reason.

OpenBSD - Only *two* remote holes in the default install, in more than 10 years!

Posted by Jonathan

Yes, there is now a second remote hole in OpenBSD. In the last 10 years there was only one, yesterday Theo de Raadt posted the second one (discovered by Core Security Technologies) to misc@openbsd.org. The vulnerability involves a mbuf remote kernel buffer overflow in the IPv6 code.

So you should patch immediately if you use IPv6 or disable it through PF (as it is enabled by default):

block in inet6 

Apart from that the upcoming 4.1 release (“Puffy Baba & the 40 Vendors”) release is available for pre-order.

Ruby CGI.rb DoS vulnerability

Posted by Jonathan

There seems to be a Denial of Service vulnerability in Ruby’s CGI.rb that affects all Ruby applications that use CGI for Mime parsing. That will include all Rails applications that are running on Mongrel or CGI. The only not affected constellations are WEBrick and FastCGI. A malicious URL will cause CGI.rb to use max. CPU in an infinitive loop.

So if your are using Mongrel, hotfix your installation b using the latest pre-release that depends on the monkey-patch to CGI.rb:

sudo gem install mongrel --source=http://mongrel.rubyforge.org/releases

If you are using CGI.rb by other means, install the hotfix-gem and require it:

gem install cgi_multipart_eof_fix --source=http://mongrel.rubyforge.org/releases

More details by Zed Shaw here.

Stop the BLOB!

Posted by Jonathan

Finally here:

OpenBSD 3.9


UPDATE:
OpenBSD 3.9 is now officially released and avaliable through FTP.


Windows WMF Vulnerability and OpenSource

Posted by Jonathan

Richard Bejtlich wrote a good comment/wrap-up on the recent Windows WMF vulnerability and the unofficial patch from Ilfak Guilfanov hostet by SANS.

Richard summarizes:

The unofficial patch Tom references was written by Ilfak Guilfanov and described here. What is this? It’s a patch created by a non-Microsoft developer, acting more rapidly than Microsoft itself. Sure, you can argue that Microsoft is working now to develop a patch that will hopefully address deeper problems, perhaps serious problems. Nevertheless, SANS has reverse engineered the unoffical patch to ensure its validity, wrote a FAQ about the vulnerability, and is now hosting a .msi to ease patch installation. This is unprecedented.

Where is Microsoft on this issue? They published their initial advisory on 28 Dec and updated it 30 Dec. Nothing they’ve done has helped resolve the issue. Meanwhile, the
Metasploit project has released a module to generate malicious WMF files. This puts exploit creation in the hands of the lowest common denomintaor.

Make sure to read his whole post.

Metasploit Framework on Ruby

Posted by Jonathan

Metasploit, the nice exploit framework, released an alpha for the new 3.0 version. Apart from new features and exploits, the language was switched from Perl to Ruby:

Unlike the 2.0 series, the 3.0 branch is written in Ruby, an object-oriented, interpreted scripting language, that has drastically simplified the implementation of the framework.

...

To demonstrate how the 3.0 branch has simplified exploit development, check out the following code sample, which provides the exploit body for the 3Com 3CDaemon 2.0 FTP Username Overflow (3cdaemon_ftp_user.rb):

-
connect
print_status(“Trying target #{target.name}...”)
buf = Rex::Text.rand_text_english(2048, payload_badchars)
seh = generate_seh_payload(target.ret)
buf[229, seh.length] = seh
send_cmd( [‘USER’, buf] , false )
disconnect
handler
-

Nice and clean, especially if compared to the older Perl exploit wrappers.

Another nice feature is the integration of IRB as known in Rails from script/console that allows direct interactation with the framework instance.

A hint for OS X users: Apple’s included version of pack/unpack is of course broken so you need to use another version. I run the framework with fink’s version of Ruby:

# /sw/bin/ruby msfconsole

See the release notes for more information.

22C3 - Private Investigations

Posted by Jonathan

The 22nd Chaos Communication Congress (22C3) is a four-day conference on technology, society and utopia. The Congress offers lectures and workshops on a multitude of topics including (but not limited to) information technology, IT-security, internet, cryptography and generally a critical-creative attitude towards technology and the discussion about the effects of technological advances on society.

So the 22nd CCC is not far away and I wanted to help to spread the word. I’ll certainly be there as the congress will be held in my hometown Berlin, Germany. I was thinking about presenting something about Rails and in particular about security issues and default settings in Rails but when I finally decided to do so it was to late.

There is now an official 22C3 blog, a public wiki and the artwork is also finished.

See you in december!

UPDATE:
The schedule was released.

FreeRADIUS SSL CA Annoyances

Posted by Jonathan

The last couple of days I’ve struggling with FreeRADIUS. Apart from not having a good documentation, a configuration mess, and not compiling out-of-the-box on OpenBSD, its OpenSSL CA scripts are broken.

FreeRADIUS ships a script that should help you set up your own CA for a self-signed certificate for EAP-TTLS. This script (CA.all in the scripts directory of the distribution) uses OpenSSL’s CA.pl script to do the SSL magic for you. The only problem is that the path to CA.pl is hardcoded:

SSL=/usr/local/ssl
export PATH=${SSL}/bin/:${SSL}/ssl/misc:${PATH}

...

echo “newreq.pem” | /usr/local/ssl/misc/CA.pl -newca

The hardcoded path will of course not work on OS X, FreeBSD or OpenBSD. As the good guys from FreeRADIUS do not use any type of error handling, this problem will manifest later with this error message:

Error opening CA private key ./demoCA/private/cakey.pem
1254:error:02001002:system library:fopen:No such file or directory:bss_file.c:278:fopen(’./demoCA/private/cakey.pem’,’r’)
1254:error:20074002:BIO routines:FILE_CTRL:system lib:bss_file.c:280:
unable to load CA private key

Took me a while to understand why the demoCA stuff was not created. The funny thing is that the path stuff would not work anyway as CA.pl is located in /usr/src/crypto/openssl/apps/ in FreeBSD and in /System/Library/OpenSSL/misc/ under OS X.

I hate developer that assume that UNIX = Linux.

Puffiana Jones is here!

Posted by Jonathan

I just got my OpenBSD 3.8 pre-order.

The official release date is November 1, 2005. Order a CD and support the project!

UPDATE:
OpenBSD 3.8 is now officially released. Further this article got a Chinese Slashdotting.

1und1/www.degut.de

Posted by Jonathan

You could mean that 1und1, one of the big players in the german hosting market, is able to configure its webservers…

PHP warnings on www.degut.de