Ruby on Rails (RoR) is atm my favorite piece of software to hunt bugs at. After quite some time spending on looking at Rails apps I figured that I oversaw the most easy way to attack an (Open Source) Rails app for quite a while. Before I come to my main point we'll have to look at both RoR sessions and authentication systems:
RoR sessions are by default stored client-side in a cookie. In order to be tamper resistant, this cookie is signed with an SHA-1 HMAC. When the HMAC is missing or the cookie being tampered with RoR will refuse to use the session variables within the cookie.
So, let's look at such a cookie, as an example I'll use a Github session cookie:
_gh_sess=BAh7BzoQX2NzcmZfdG9rZW4iMStDQWNRZ1l4VlZPb3ZPM3FBYXZWZGtsYzF2NUVENkdaRnhEK1A0QmNqU1k9Og9zZXNzaW9uX2lkIiUwMTg4M2VjNzNkOTE3YTM5MzliN2Q2ZWUyNDc1ODJlMA%3D%3D--101900b7c006dee701987683bca2de06399d1305This cookie consists of a Base64 blog and the HMAC, both separated by "--". When decoding the Base64 you'll get a marshaled Ruby Object, namely the session hash which is accessible in the web application via session[:some_session_var]. The decoded and de-marshaled Github cookie looks like this:
{:_csrf_token=>"+CAcQgYxVVOovO3qAavVdklc1v5ED6GZFxD+P4BcjSY=", :session_id=>"01883ec73d917a3939b7d6ee247582e0"}So in this case for instance the _csrf_token would be accessible via session[:_csrf_token] from within Githubs' RoR code.
While reading some OSS RoR applications I mainly came across this three authentication mechanisms for RoR:
Where authenticated_system is the simplest mechanism, which will just put a field "user_id" inside the session, by this ID then authenticated_system will pull the user with that ID out of the database as the currently logged in user.When a RoR application is created the secret which goes into the HMAC will be created along with all the other files a minimal RoR application would need. This secret usually is a 64 byte long random string and lives in $railsapp/config/initializers/secret_token.rb. The simple problem is, that most developers are simply not aware of the confidentiality of this file, and in result they'll happly check it into Github or other online repositories (This guy already figured that a while ago ).
When using authenticated_system it's pretty obvious how to break into such an application:
Well that was easy, let's look at authlogic now.
An authlogic cookie usually uses a database stored token to
identify the user. The relevant parts of the session cookie
are:
> User.find_by_id({:select =>"* from users limit 1 --"}) User Load (0.5ms) SELECT * from users limit 1 -- FROM "users" WHERE "users"."id" IS NULL LIMIT 1 => #<User id: 1, [... all the fun stuff]By knowing this behaviour we can now easily circumvent the authlogic protection with the knowledge of the "secret_token".
{ "session_id" => "41414141", "user_credentials"=>"Phenoelit", "user_credentials_id"=>{ :select=> " *,\"Phenoelit\" as persistence_token from Users -- " } }
Last man standing would then be devise/warden, which works similar to authlogic but is not exploitable in that way described above.