rapid7 / metasploit-framework

Metasploit Framework
https://www.metasploit.com/
Other
33.73k stars 13.89k forks source link

Non-ASCII characters in auxiliary/scanner/smb/smb_login #8423

Open m-oleary opened 7 years ago

m-oleary commented 7 years ago

Attempting to run auxiliary/scanner/smb/smb_login against a target fails with the error Auxiliary failed: NoMethodError undefined method `disconnect!' for nil:NilClass if the the tested password is something other than ASCII.

Steps to reproduce

Create a file with a password with non-ASCII characters; for example create the file abbe.lst with the single password

abbé

(This word appears in the file /usr/share/wordlists/metasploit/password.lst on the current version of Kali, which is how I found the issue)

msf auxiliary(smb_login) > show options

Module options (auxiliary/scanner/smb/smb_login):

   Name              Current Setting                           Required  Description
   ----              ---------------                           --------  -----------
   ABORT_ON_LOCKOUT  false                                     yes       Abort the run when an account lockout is detected
   BLANK_PASSWORDS   false                                     no        Try blank passwords for all users
   BRUTEFORCE_SPEED  5                                         yes       How fast to bruteforce, from 0 to 5
   DB_ALL_CREDS      false                                     no        Try each user/password couple stored in the current database
   DB_ALL_PASS       false                                     no        Add all passwords in the current database to the list
   DB_ALL_USERS      false                                     no        Add all users in the current database to the list
   DETECT_ANY_AUTH   true                                      no        Enable detection of systems accepting any authentication
   PASS_FILE         /usr/share/wordlists/metasploit/abbe.lst  no        File containing passwords, one per line
   PRESERVE_DOMAINS  true                                      no        Respect a username that contains a domain name.
   Proxies                                                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RECORD_GUEST      false                                     no        Record guest-privileged random logins to the database
   RHOSTS            10.0.15.200                               yes       The target address range or CIDR identifier
   RPORT             445                                       yes       The SMB service port (TCP)
   SMBDomain         pluto                                     no        The Windows domain to use for authentication
   SMBPass                                                     no        The password for the specified username
   SMBUser           jbach                                     no        The username to authenticate as
   STOP_ON_SUCCESS   false                                     yes       Stop guessing when a credential works for a host
   THREADS           1                                         yes       The number of concurrent threads
   USERPASS_FILE                                               no        File containing users and passwords separated by space, one pair per line
   USER_AS_PASS      false                                     no        Try the username as the password for all users
   USER_FILE                                                   no        File containing usernames, one per line
   VERBOSE           true                                      yes       Whether to print output for all attempts

msf auxiliary(smb_login) > run

[*] 10.0.15.200:445       - SMB - Starting SMB login bruteforce
[*] 10.0.15.200:445       - This system does not accept authentication with any credentials, proceeding with brute force
[-] Auxiliary failed: NoMethodError undefined method `disconnect!' for nil:NilClass
[-] Call stack:
[-]   /usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/smb.rb:136:in `ensure in attempt_login'
[-]   /usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/smb.rb:136:in `attempt_login'
[-]   /usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/base.rb:231:in `block in scan!'
[-]   /usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/base.rb:154:in `block in each_credential'
[-]   /usr/share/metasploit-framework/lib/metasploit/framework/credential_collection.rb:132:in `block in each'
[-]   /usr/share/metasploit-framework/lib/metasploit/framework/credential_collection.rb:130:in `each_line'
[-]   /usr/share/metasploit-framework/lib/metasploit/framework/credential_collection.rb:130:in `each'
[-]   /usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/base.rb:141:in `each_credential'
[-]   /usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/base.rb:205:in `scan!'
[-]   /usr/share/metasploit-framework/modules/auxiliary/scanner/smb/smb_login.rb:113:in `run_host'
[-]   /usr/share/metasploit-framework/lib/msf/core/auxiliary/scanner.rb:135:in `block (2 levels) in run'
[-]   /usr/share/metasploit-framework/lib/msf/core/thread_manager.rb:100:in `block in spawn'
[*] Auxiliary module execution completed

Running the command with LogLevel 5 yields the following data from /root/.msf4/logs/framework.log

[05/20/2017 16:05:19] [e(0)] core: thread exception: ScannerHost(scanner/smb/smb_login)-10.0.15.200  critical=false  error: NoMethodError undefined method `disconnect!' for nil:NilClass
  source:
    /usr/share/metasploit-framework/lib/msf/core/auxiliary/scanner.rb:129:in `block in run'
    /usr/share/metasploit-framework/lib/msf/core/auxiliary/scanner.rb:116:in `loop'
    /usr/share/metasploit-framework/lib/msf/core/auxiliary/scanner.rb:116:in `run'
    /usr/share/metasploit-framework/lib/msf/base/simple/auxiliary.rb:140:in `job_run_proc'
    /usr/share/metasploit-framework/lib/msf/base/simple/auxiliary.rb:82:in `run_simple'
    /usr/share/metasploit-framework/lib/msf/base/simple/auxiliary.rb:92:in `run_simple'
    /usr/share/metasploit-framework/lib/msf/ui/console/command_dispatcher/auxiliary.rb:104:in `cmd_run'
    /usr/share/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:430:in `run_command'
    /usr/share/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:392:in `block in run_single'
    /usr/share/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:386:in `each'
    /usr/share/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:386:in `run_single'
    /usr/share/metasploit-framework/lib/rex/ui/text/shell.rb:205:in `run'
    /usr/share/metasploit-framework/lib/metasploit/framework/command/console.rb:48:in `start'
    /usr/share/metasploit-framework/lib/metasploit/framework/command/base.rb:82:in `start'
    /usr/bin/msfconsole:48:in `<main>'
[05/20/2017 16:05:19] [e(0)] core: Call Stack
/usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/smb.rb:136:in `ensure in attempt_login'
/usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/smb.rb:136:in `attempt_login'
/usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/base.rb:231:in `block in scan!'
/usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/base.rb:154:in `block in each_credential'
/usr/share/metasploit-framework/lib/metasploit/framework/credential_collection.rb:132:in `block in each'
/usr/share/metasploit-framework/lib/metasploit/framework/credential_collection.rb:130:in `each_line'
/usr/share/metasploit-framework/lib/metasploit/framework/credential_collection.rb:130:in `each'
/usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/base.rb:141:in `each_credential'
/usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/base.rb:205:in `scan!'
/usr/share/metasploit-framework/modules/auxiliary/scanner/smb/smb_login.rb:113:in `run_host'
/usr/share/metasploit-framework/lib/msf/core/auxiliary/scanner.rb:135:in `block (2 levels) in run'
/usr/share/metasploit-framework/lib/msf/core/thread_manager.rb:100:in `block in spawn'
[05/20/2017 16:05:19] [e(0)] core: Auxiliary failed: NoMethodError undefined method `disconnect!' for nil:NilClass
[05/20/2017 16:05:19] [d(3)] core: Call stack:
/usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/smb.rb:136:in `ensure in attempt_login'
/usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/smb.rb:136:in `attempt_login'
/usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/base.rb:231:in `block in scan!'
/usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/base.rb:154:in `block in each_credential'
/usr/share/metasploit-framework/lib/metasploit/framework/credential_collection.rb:132:in `block in each'
/usr/share/metasploit-framework/lib/metasploit/framework/credential_collection.rb:130:in `each_line'
/usr/share/metasploit-framework/lib/metasploit/framework/credential_collection.rb:130:in `each'
/usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/base.rb:141:in `each_credential'
/usr/share/metasploit-framework/lib/metasploit/framework/login_scanner/base.rb:205:in `scan!'
/usr/share/metasploit-framework/modules/auxiliary/scanner/smb/smb_login.rb:113:in `run_host'
/usr/share/metasploit-framework/lib/msf/core/auxiliary/scanner.rb:135:in `block (2 levels) in run'
/usr/share/metasploit-framework/lib/msf/core/thread_manager.rb:100:in `block in spawn'

Expected behavior

The module should proceed through the entire password list without crashing

Current behavior

The module stops after the first non-ASCII word and crashes as above.

System stuff

Kali VM updated as of 5-20-2017 running on VirtualBox

Metasploit version

msf auxiliary(smb_login) > version Framework: 4.14.17-dev Console : 4.14.17-dev

I installed Metasploit with:

OS

What OS are you running Metasploit on? Kali

KyleHanslovan commented 7 years ago

Independently ran into this same issue today. The password "Mau’dib" triggered this bug from the common_roots.txt word list (not an ASCII apostrophe).

I confirmed that removing this password from the word list no longer triggered this bug.

OJ commented 7 years ago

For funzies, a few friends and I decided to look into this issue to see if we could come up with an answer and submit a PR that fixes the problem. What ensued was a painful trip down the Rabbit Hole, and I'm not sure I came out with my sanity.

There are two issues going on here.

Incorrect closing of the SMB client

The scanner code makes use of the Ruby SMB client to make an SMB connection to the endpoint. The code in question can be found here:

          begin

            realm       = credential.realm   || ""
            username    = credential.public  || ""
            password    = credential.private || ""
            client      = RubySMB::Client.new(self.dispatcher, username: username, password: password, domain: realm)
            status_code = client.login

            if status_code == WindowsError::NTStatus::STATUS_SUCCESS
              # Windows SMB will return an error code during Session
              # Setup, but nix Samba requires a Tree Connect. Try admin$
              # first, since that will tell us if this user has local
              # admin access. Fall back to IPC$ which should be accessible
              # to any user with valid creds.
              begin
                tree = client.tree_connect("\\\\#{host}\\admin$")
                # Check to make sure we can write a file to this dir
                if tree.permissions.add_file == 1
                  access_level = AccessLevels::ADMINISTRATOR
                end
              rescue Exception => e
                client.tree_connect("\\\\#{host}\\IPC$")
              end
            end

            case status_code.name
              when *StatusCodes::CORRECT_CREDENTIAL_STATUS_CODES
                status = Metasploit::Model::Login::Status::DENIED_ACCESS
              when 'STATUS_SUCCESS'
                status = Metasploit::Model::Login::Status::SUCCESSFUL
              when 'STATUS_ACCOUNT_LOCKED_OUT'
                status = Metasploit::Model::Login::Status::LOCKED_OUT
              when 'STATUS_LOGON_FAILURE', 'STATUS_ACCESS_DENIED'
                status = Metasploit::Model::Login::Status::INCORRECT
              else
                status = Metasploit::Model::Login::Status::INCORRECT
            end
          rescue ::Rex::ConnectionError, Errno::EINVAL => e
            status = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
            proof = e
          rescue RubySMB::Error::UnexpectedStatusCode => e
            status = Metasploit::Model::Login::Status::INCORRECT
          ensure
            client.disconnect!
          end

The problem is that an exception is being thrown during the construction of the RubySMB::Client object due to incorrect/invalid handling of character encoding, and the client variable remains nil. As a result the ensure case is invoked, and client.disconnect! is called when client is still nil. This is what's causing the stack trace that we're looking at, and so the disconnect line should be changed to this:

client.disconnect! if client

Easy done.

Handling of character encoding when reading files is a mess

The PASS_FILE datastore item references a file on disk that's used to load all the passwords into credential instances. While parsing this file, the following code is invoked:

   if pass_file.present?
      pass_fd = File.open(pass_file, 'r:binary')
    end

    prepended_creds.each { |c| yield c }

    if username.present?
      if password.present?
        yield Metasploit::Framework::Credential.new(public: username, private: password, realm: realm, private_type: private_type(password))
      end
      if user_as_pass
        yield Metasploit::Framework::Credential.new(public: username, private: username, realm: realm, private_type: :password)
      end
      if blank_passwords
        yield Metasploit::Framework::Credential.new(public: username, private: "", realm: realm, private_type: :password)
      end
      if pass_fd
        pass_fd.each_line do |pass_from_file|
          pass_from_file.chomp!
          yield Metasploit::Framework::Credential.new(public: username, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))
        end
        pass_fd.seek(0)
      end
      additional_privates.each do |add_private|
        yield Metasploit::Framework::Credential.new(public: username, private: add_private, realm: realm, private_type: private_type(add_private))
      end
    end

Note that the file in question is being opened as r:binary, which is the equivalent of 8bit ASCII. If the file contains UTF8 characters (as is the case with the reports listed above) then later on we see the code fail in the ruby_smb gem, at this location:

@password          = password.encode("utf-8") || ''.encode("utf-8")

This is because the characters in the password can't be converted to UTF8 from ASCII. Exceptions about, everyone becomes sad.

The first thing I tried was to change the file mode from r:binary to r:utf-8, and that did indeed bypass the first issue. However, the inverse of this probablem (ie. the inability encode the UTF8 string to 8bit ASCII) appeared as soon as this line was hit:

pass_fd.each_line do |pass_from_file|

The use of each_line here for some reasons forces the underling string to ASCII, and causes an exception. I have no idea why this is happening, so perhaps someone with more Ruby chops can get in here and explain (perhaps @egypt?).

After thinking about it for a while I came up with the following:

The way I ended up "fixing" this in my local branch was to change the above line in ruby_smb to this:

@password          = password.force_encoding("utf-8") || ''.encode("utf-8")

This meant that the password was forced to be UTF8 and didn't actually go through an encoding process from ASCII 8bit.

I do not like this solution as it's really only hitting the problem in one location. Needless to say this issue could (and probably will) manifest itself in other spots and in other ways. One example would be that the user names can suffer the same problem.

So we're at the point where we should figure out the best option and apply it as close to the 'bottom' of the chain as possible so that it has impact in all the places it needs to.

Please let me know what you think of this rambling, and what you think we should do to resolve it.

Thanks to Pipes, Rick, Multi and Lockyc for putting up with my ramblings while we tried to figure this out!

github-actions[bot] commented 3 years ago

Hi!

This issue has been left open with no activity for a while now.

We get a lot of issues, so we currently close issues after 60 days of inactivity. It’s been at least 30 days since the last update here. If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open!

As a friendly reminder: the best way to see this issue, or any other, fixed is to open a Pull Request.

p01terge1st commented 2 years ago

[*] 192.168.1.104:445 - Authenticating to 192.168.1.104:445 as user 'Администратор'... [-] 192.168.1.104:445 - Exploit failed [no-access]: Rex::Proto::SMB::Exceptions::LoginError Login Failed: "\xD0" from ASCII-8BIT to UTF-8