From 9a15a2be04db115ebb6a302185a13bf369ca8a07 Mon Sep 17 00:00:00 2001 From: David Maloney Date: Fri, 18 Apr 2014 20:08:28 -0500 Subject: [PATCH] basic login attempt functionality groundowkr now layed for trying authentication attempts on the SSH LoginScanner, with test coverage --- lib/metasploit/framework/login_scanner/ssh.rb | 76 +++++++++++++++++++ .../framework/login_scanner/ssh_spec.rb | 30 ++++++++ 2 files changed, 106 insertions(+) diff --git a/lib/metasploit/framework/login_scanner/ssh.rb b/lib/metasploit/framework/login_scanner/ssh.rb index eac2d03281..8acbdc069d 100644 --- a/lib/metasploit/framework/login_scanner/ssh.rb +++ b/lib/metasploit/framework/login_scanner/ssh.rb @@ -8,6 +8,7 @@ module Metasploit class SSH include ActiveModel::Validations + # @!attribute connection_timeout # @return [Fixnum] The timeout in seconds for a single SSH connection attr_accessor :connection_timeout @@ -75,6 +76,7 @@ module Metasploit end def attempt_login(user, pass) + ssh_socket = nil opt_hash = { :auth_methods => ['password','keyboard-interactive'], :msframework => msframework, @@ -104,6 +106,13 @@ module Metasploit return [:fail,nil] # For whatever reason. Can't tell if passwords are on/off without timing responses. end + if ssh_socket + proof = gather_proof + create_session(proof,user,pass) + [:success, proof] + else + [:fail, nil] + end end @@ -121,6 +130,73 @@ module Metasploit private + def create_session(proof,user,pass) + conn = Net::SSH::CommandStream.new(ssh_socket, '/bin/sh', true) + + datastore_opts = { + 'USERPASS_FILE' => nil, + 'USER_FILE' => nil, + 'PASS_FILE' => nil, + 'USERNAME' => user, + 'PASSWORD' => pass + } + + session = Msf::Sessions::CommandShell.new(conn.lsock) + session.info = "SSH: #{user}:#{pass} (#{host}:#{port})" + + # Set module details on the session if we have them + if msfmodule + session.set_from_exploit(msfmodule) + session.exploit_datastore.merge!(datastore_opts) + end + + # Register the new session + if msframework + msframework.sessions.register(session) + end + + # Set the session platform + case proof + when /Linux/ + session.platform = "linux" + when /Darwin/ + session.platform = "osx" + when /SunOS/ + session.platform = "solaris" + when /BSD/ + session.platform = "bsd" + when /HP-UX/ + session.platform = "hpux" + when /AIX/ + session.platform = "aix" + when /Win32|Windows/ + session.platform = "windows" + when /Unknown command or computer name/ + session.platform = "cisco-ios" + end + end + + def gather_proof + proof = '' + begin + Timeout.timeout(5) do + proof = ssh_socket.exec!("id\n").to_s + if(proof =~ /id=/) + proof << ssh_socket.exec!("uname -a\n").to_s + else + # Cisco IOS + if proof =~ /Unknown command or computer name/ + proof = ssh_socket.exec!("ver\n").to_s + else + proof << ssh_socket.exec!("help\n?\n\n\n").to_s + end + end + end + rescue ::Exception + end + proof + end + def host_address_must_be_valid unless host.kind_of? String errors.add(:host, "must be a string") diff --git a/spec/lib/metasploit/framework/login_scanner/ssh_spec.rb b/spec/lib/metasploit/framework/login_scanner/ssh_spec.rb index fa86e8aadd..b41d147bf8 100644 --- a/spec/lib/metasploit/framework/login_scanner/ssh_spec.rb +++ b/spec/lib/metasploit/framework/login_scanner/ssh_spec.rb @@ -338,6 +338,36 @@ describe Metasploit::Framework::LoginScanner::SSH do Net::SSH.should_receive(:start) { raise Net::SSH::Exception } expect(ssh_scanner.attempt_login('root', 'toor')).to eq [:fail,nil] end + + it 'returns [:fail,nil] if no socket returned' do + Net::SSH.should_receive(:start).and_return nil + expect(ssh_scanner.attempt_login('root', 'toor')).to eq [:fail,nil] + end + end + + context 'when it succeeds' do + + it 'gathers proof of the connections' do + Net::SSH.should_receive(:start) {"fake_socket"} + my_scanner = ssh_scanner + my_scanner.should_receive(:gather_proof) + my_scanner.attempt_login('root', 'toor') + end + + it 'creates a session from the connection' do + Net::SSH.should_receive(:start) {"fake_socket"} + my_scanner = ssh_scanner + my_scanner.should_receive(:gather_proof).and_return('root') + my_scanner.should_receive(:create_session).with('root', 'root', 'toor') + my_scanner.attempt_login('root', 'toor') + end + + it 'returns a success code and proof' do + Net::SSH.should_receive(:start) {"fake_socket"} + my_scanner = ssh_scanner + my_scanner.should_receive(:gather_proof).and_return('root') + expect(my_scanner.attempt_login('root', 'toor')).to eq [:success, 'root'] + end end end