Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GatherProof advanced option to ssh_login* #12024

Merged
merged 2 commits into from Jul 10, 2019

Conversation

@wvu-r7
Copy link
Contributor

commented Jun 28, 2019

Quick and dirty fix for Brocade.

Since datastore options don't belong in LoginScanner, I've added an accessor, so proof gathering is no longer opaque to modules.

msf5 > use ssh_login

Matching Modules
================

   #  Name                                    Disclosure Date  Rank    Check  Description
   -  ----                                    ---------------  ----    -----  -----------
   0  auxiliary/scanner/ssh/ssh_login                          normal  Yes    SSH Login Check Scanner
   1  auxiliary/scanner/ssh/ssh_login_pubkey                   normal  Yes    SSH Public Key Login Scanner


msf5 > use 0
msf5 auxiliary(scanner/ssh/ssh_login) > setg rhosts 172.28.128.3
rhosts => 172.28.128.3
msf5 auxiliary(scanner/ssh/ssh_login) > setg username vagrant
username => vagrant
msf5 auxiliary(scanner/ssh/ssh_login) > set password vagrant
password => vagrant
msf5 auxiliary(scanner/ssh/ssh_login) > run

[+] 172.28.128.3:22 - Success: 'vagrant:vagrant' ''
[*] Command shell session 1 opened (172.28.128.1:64447 -> 172.28.128.3:22) at 2019-06-27 21:46:42 -0500
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf5 auxiliary(scanner/ssh/ssh_login) > set gatherproof true
gatherproof => true
msf5 auxiliary(scanner/ssh/ssh_login) > run

[+] 172.28.128.3:22 - Success: 'vagrant:vagrant' 'uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant) Linux ubuntu-xenial 4.4.0-141-generic #167-Ubuntu SMP Wed Dec 5 10:40:15 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux '
[*] Command shell session 2 opened (172.28.128.1:64452 -> 172.28.128.3:22) at 2019-06-27 21:46:50 -0500
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf5 auxiliary(scanner/ssh/ssh_login) > use 1
msf5 auxiliary(scanner/ssh/ssh_login_pubkey) > set key_pa
set key_pass  set key_path
msf5 auxiliary(scanner/ssh/ssh_login_pubkey) > set key_path [redacted]
key_path => [redacted]
msf5 auxiliary(scanner/ssh/ssh_login_pubkey) > run

[*] 172.28.128.3:22 SSH - Testing Cleartext Keys
[*] 172.28.128.3:22 - Testing 1 keys from [redacted]
[+] 172.28.128.3:22 - Success: 'vagrant:-----BEGIN RSA PRIVATE KEY-----
[redacted]
-----END RSA PRIVATE KEY-----
' ''
[*] Command shell session 3 opened (172.28.128.1:64457 -> 172.28.128.3:22) at 2019-06-27 21:48:03 -0500
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf5 auxiliary(scanner/ssh/ssh_login_pubkey) > set gatherproof true
gatherproof => true
msf5 auxiliary(scanner/ssh/ssh_login_pubkey) > run

[*] 172.28.128.3:22 SSH - Testing Cleartext Keys
[*] 172.28.128.3:22 - Testing 1 keys from [redacted]
[+] 172.28.128.3:22 - Success: 'vagrant:-----BEGIN RSA PRIVATE KEY-----
[redacted]
-----END RSA PRIVATE KEY-----
' 'uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant) Linux ubuntu-xenial 4.4.0-141-generic #167-Ubuntu SMP Wed Dec 5 10:40:15 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux '
[*] Command shell session 4 opened (172.28.128.1:64460 -> 172.28.128.3:22) at 2019-06-27 21:48:18 -0500
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf5 auxiliary(scanner/ssh/ssh_login_pubkey) >

Specifically addresses the Brocade issue in #11905. Does not close the ticket unless @sempervictus thinks so.

@wvu-r7 wvu-r7 requested a review from h00die Jun 28, 2019
@bcoles

This comment has been minimized.

Copy link
Contributor

commented Jun 30, 2019

Would you care to fix this abomonation while you're at it? (perhaps in a separate PR).

        # This method attempts to gather proof that we successfuly logged in.
        # @return [String] The proof of a connection, May be empty.
        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
                if (proof =~ /JUNOS /)
                  # We're in the SSH shell for a Juniper JunOS, we can pull the version from the cli
                  # line 2 is hostname, 3 is model, 4 is the Base OS version
                  proof = ssh_socket.exec!("cli show version\n").split("\n")[2..4].join(", ").to_s
                elsif (proof =~ /Linux USG /)
                  # Ubiquiti Unifi USG
                  proof << ssh_socket.exec!("cat /etc/version\n").to_s.rstrip
                end
                temp_proof = ssh_socket.exec!("grep unifi.version /tmp/system.cfg\n").to_s.rstrip
                if (temp_proof =~ /unifi\.version/)
                  proof << temp_proof
                  # Ubiquiti Unifi device (non-USG), possibly a switch.  Tested on US-24, UAP-nanoHD
                  # The /tmp/*.cfg files don't give us device info, however the info command does
                  # we dont call it originally since it doesnt say unifi/ubiquiti in it and info
                  # is a linux command as well
                  proof << ssh_socket.exec!("grep board.name /etc/board.info\n").to_s.rstrip
                end
              else
                # Cisco IOS
                if proof =~ /Unknown command or computer name/
                  proof = ssh_socket.exec!("ver\n").to_s
                # Juniper ScreenOS
                elsif proof =~ /unknown keyword/
                  proof = ssh_socket.exec!("get chassis\n").to_s
                # Juniper JunOS CLI
                elsif proof =~ /unknown command: id/
                  proof = ssh_socket.exec!("show version\n").split("\n")[2..4].join(", ").to_s
                else
                  proof << ssh_socket.exec!("help\n?\n\n\n").to_s
                end
              end
            end
          rescue ::Exception
          end
          proof
        end
  • successfully is spelt incorrectly.
  • A level of indentation can be removed by using the rescue-from-method technique. Indention is 18 spaces which takes up a quarter of the screen on my VT-100.
  • The only code which lives outside of the begin / rescue block is proof = '' and the return proof. It's incredibly unlikely that moving these inside the block will cause an issue.
  • The begin / rescue block is used, presumably, to catch both Timeout and any other errors which happen to occur. It also rescues Exception which is frowned upon. StandardError is preferred over Exception. It also silently swallows the exception.
  • rescue Timeout::Error => e should be defined explicitly as it is a known, and arguably expected, error. Swallow silently if you must.
  • if (proof =~ /id=/) could be a simple string comparison with proof.include?('id='). While =~ is nil safe, it is redundant in this instance, as proof is cast to_s on the preceding line.
  • Similarly, temp_proof =~ /unifi\.version/ would be easier to read as a string comparison with temp_proof.include?('unifi.version').

Here's an untested cleanup:

        # This method attempts to gather proof that we successfully logged in.
        # @return [String] The proof of a connection, May be empty.
        def gather_proof
          proof = ''
          Timeout.timeout(5) do
            proof = ssh_socket.exec!("id\n").to_s
            if proof.include?('id=')
              proof << ssh_socket.exec!("uname -a\n").to_s
              if (proof =~ /JUNOS /)
                # We're in the SSH shell for a Juniper JunOS, we can pull the version from the cli
                # line 2 is hostname, 3 is model, 4 is the Base OS version
                proof = ssh_socket.exec!("cli show version\n").split("\n")[2..4].join(", ").to_s
              elsif (proof =~ /Linux USG /)
                # Ubiquiti Unifi USG
                proof << ssh_socket.exec!("cat /etc/version\n").to_s.rstrip
              end

              temp_proof = ssh_socket.exec!("grep unifi.version /tmp/system.cfg\n").to_s.rstrip
              if temp_proof.include?('unifi.version')
                proof << temp_proof
                # Ubiquiti Unifi device (non-USG), possibly a switch.  Tested on US-24, UAP-nanoHD
                # The /tmp/*.cfg files don't give us device info, however the info command does
                # we dont call it originally since it doesnt say unifi/ubiquiti in it and info
                # is a linux command as well
                proof << ssh_socket.exec!("grep board.name /etc/board.info\n").to_s.rstrip
              end
            else
              # Cisco IOS
              if proof =~ /Unknown command or computer name/
                proof = ssh_socket.exec!("ver\n").to_s
              # Juniper ScreenOS
              elsif proof =~ /unknown keyword/
                proof = ssh_socket.exec!("get chassis\n").to_s
              # Juniper JunOS CLI
              elsif proof =~ /unknown command: id/
                proof = ssh_socket.exec!("show version\n").split("\n")[2..4].join(", ").to_s
              else
                proof << ssh_socket.exec!("help\n?\n\n\n").to_s
              end
            end
          end

          proof
        rescue StandardException, Timeout::Error
          proof
        end

Bytes saved: 53; but still a mess. Rewriting to use return-early-return-often style may be cleaner.

Additionally, it is unlikely that this code block should be executed for every SSH connection which returns id=:

temp_proof = ssh_socket.exec!("grep unifi.version /tmp/system.cfg\n").to_s.rstrip

ie, when is a JUNOS device also a Ubiquiti device?

Additionally, why does this line redefine proof, rather than append? This line, and the subsequent code block, are super flimsy, as they rely on rescuing, and silently swallowing, exceptions, so as to preserve the existing value of proof.

proof = ssh_socket.exec!("cli show version\n").split("\n")[2..4].join(", ").to_s

In every other instance when the server replies with id=, data is appended to proof, so as to preserve the id output. But in this instance, proof is instead overwritten with the results of a cli command with flimsy error-prone string splitting and line selection. If it succeeds, the value of id is lost (maybe intentional?). If it fails, it will raise an error, which gets swallowed silently, and the value of id is preserved.

irb(main):001:0> 'oof'.split("\n")[2..4].join(", ").to_s
Traceback (most recent call last):
        2: from /usr/bin/irb:11:in `<main>'
        1: from (irb):1
NoMethodError (undefined method `join' for nil:NilClass)

Also, in every other instance, calls to ssh_socket.exec! are immediately cast to_s. In this instance, there's no cast, and the returned value (which presumably could be nil) is passed straight to split, which introduces potential for yet another raise, which gets swallowed silently.

irb(main):001:0> nil.split("\n")
Traceback (most recent call last):
        2: from /usr/bin/irb:11:in `<main>'
        1: from (irb):1
NoMethodError (undefined method `split' for nil:NilClass)
@h00die

This comment has been minimized.

Copy link
Contributor

commented Jul 1, 2019

The one overwrite of proof is intentional. id would come back as something like 'command not found' and was therefore not needed

@bcoles

This comment has been minimized.

Copy link
Contributor

commented Jul 3, 2019

The one overwrite of proof is intentional. id would come back as something like 'command not found' and was therefore not needed

That makes sense, but I think we're talking about different blocks?

My bad. This line was ambiguous. It was in reference to the following line(s), related to the JUNOS block (below), inside the id= true branch, rather than the unifi block.

              if (proof =~ /JUNOS /)
                # We're in the SSH shell for a Juniper JunOS, we can pull the version from the cli
                # line 2 is hostname, 3 is model, 4 is the Base OS version
                proof = ssh_socket.exec!("cli show version\n").split("\n")[2..4].join(", ").to_s

Overwriting of proof outside of this branch, inside the id= false branch, do make sense, as obviously the output from id was useless.

@wvu-r7

This comment has been minimized.

Copy link
Contributor Author

commented Jul 9, 2019

I had taken a stab at refactoring the gather_proof method but gave up in this PR. Let's do it in another.

@wvu-r7

This comment has been minimized.

Copy link
Contributor Author

commented Jul 9, 2019

@h00die: Can you respond with your review or a merge when you get a chance?

@h00die

This comment has been minimized.

Copy link
Contributor

commented Jul 9, 2019

I got this confused with #12025 , oops! Looks sane, will throw it at the Brocade, and some other network equipment a little later just to confirm there aren't any unexpected issues, then will land if its good.

@h00die

This comment has been minimized.

Copy link
Contributor

commented Jul 10, 2019

Brocade PRE

msf5 auxiliary(scanner/ssh/ssh_login) > set username brocade
username => brocade
msf5 auxiliary(scanner/ssh/ssh_login) > set password Brocade
password => Brocade
msf5 auxiliary(scanner/ssh/ssh_login) > set rhosts 2.2.2.2
rhosts => 2.2.2.2
msf5 auxiliary(scanner/ssh/ssh_login) > run

[+] 2.2.2.2:22 - Success: 'brocade:Brocade' 'Protocol error, doesn't start with scp! '
[*] Command shell session 1 opened (?? -> ??) at 2019-07-09 20:11:18 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf5 auxiliary(scanner/ssh/ssh_login) > exit
[*] You have active sessions open, to exit anyway type "exit -y"
msf5 auxiliary(scanner/ssh/ssh_login) > sessions -i 1
[*] Starting interaction with 1...

ls
id
?
^C
Abort session 1? [y/N]  y

Brocade POST

msf5 auxiliary(scanner/ssh/ssh_login) > set rhosts 2.2.2.2
rhosts => 2.2.2.2
msf5 auxiliary(scanner/ssh/ssh_login) > set password Brocade
password => Brocade
msf5 auxiliary(scanner/ssh/ssh_login) > set username brocade
username => brocade
msf5 auxiliary(scanner/ssh/ssh_login) > run

[+] 2.2.2.2:22 - Success: 'brocade:Brocade' ''
[*] Command shell session 1 opened (1.1.1.1:44561 -> 2.2.2.2:22) at 2019-07-09 20:13:40 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf5 auxiliary(scanner/ssh/ssh_login) > sessions -i 1
[*] Starting interaction with 1...

SSH@ICX6430-24 Switch>show version
show version

  Copyright (c) 1996-2016 Brocade Communications Systems, Inc. All rights reserved.
    UNIT 1: compiled on May 19 2016 at 01:15:45 labeled as ICX64S08030h
		(8500344 bytes) from Primary ICX64S08030h.bin
        SW: Version 08.0.30hT311 
  Boot-Monitor Image size = 786944, Version:10.1.05T310 (kxz10105)
  HW: Stackable ICX6430-24
==========================================================================
UNIT 1: SL 1: ICX6430-24 24-port Management Module
 	 Serial  #: BZN3202L04Z
 	 License: BASE_SOFT_PACKAGE   (LID: dbpIHFHnFJb)
 	 P-ENGINE  0: type E7FE, rev 01
==========================================================================
UNIT 1: SL 2: ICX6430-SFP 4port 4G Module
==========================================================================
  500 MHz ARM processor ARMv5TE, 400 MHz bus
32768 KB flash memory
  256 MB DRAM
STACKID 1  system uptime is 2 hour(s) 19 minute(s) 15 second(s) 
The system : started=cold start	 

SSH@ICX6430-24 Switch>

@h00die

This comment has been minimized.

Copy link
Contributor

commented Jul 10, 2019

Cisco UC520

msf5 auxiliary(scanner/ssh/ssh_login) > set username cisco
username => cisco
msf5 auxiliary(scanner/ssh/ssh_login) > set password cisco
password => cisco
msf5 auxiliary(scanner/ssh/ssh_login) > set rhosts 2.2.2.2
rhosts => 2.2.2.2
msf5 auxiliary(scanner/ssh/ssh_login) > run

[+] 2.2.2.2:22 - Success: 'cisco:cisco' ''
[*] Command shell session 2 opened (1.1.1.1:45687 -> 2.2.2.2:22) at 2019-07-09 20:17:13 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf5 auxiliary(scanner/ssh/ssh_login) > sessions -i 2
[*] Starting interaction with 2...


UC520>show version
show version
Cisco IOS Software, UC500 Software (UC500-ADVIPSERVICESK9-M), Version 12.4(20)T2, RELEASE SOFTWARE (fc4)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2009 by Cisco Systems, Inc.
Compiled Sun 01-Feb-09 00:18 by prod_rel_team

ROM: System Bootstrap, Version 12.4(11r)XW, RELEASE SOFTWARE (fc1)

UC520 uptime is 12 weeks, 1 day, 7 hours, 30 minutes
System returned to ROM by power-on
System restarted at 09:40:02 PST Tue Jul 11 2000
System image file is "flash:uc500-advipservicesk9-mz.124-20.T2.bin"


This product contains cryptographic features and is subject to United
States and local country laws governing import, export, transfer and
use. Delivery of Cisco cryptographic products does not imply
third-party authority to import, export, distribute or use encryption.
Importers, exporters, distributors and users are responsible for
compliance with U.S. and local country laws. By using this product you
agree to comply with applicable laws and regulations. If you are unable
to comply with U.S. and local laws, return this product immediately.

 --More-- q
        
UC520>
UC520>exit
exit

[*] 2.2.2.2 - Command shell session 2 closed.  Reason: User exit
msf5 auxiliary(scanner/ssh/ssh_login) > set gatherproof true
gatherproof => true
msf5 auxiliary(scanner/ssh/ssh_login) > run

[+] 2.2.2.2:22 - Success: 'cisco:cisco' '  Line has invalid autocommand "id  "'
[*] Command shell session 3 opened (?? -> ??) at 2019-07-09 20:17:49 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf5 auxiliary(scanner/ssh/ssh_login) > sessions -i 3
[*] Starting interaction with 3...

id
show version
^C
Abort session 3? [y/N]  y
""

@h00die

This comment has been minimized.

Copy link
Contributor

commented Jul 10, 2019

Juniper SSG5

msf5 auxiliary(scanner/ssh/ssh_login) > run

[+] 2.2.2.2:22 - Success: 'admin:<<< %s(un='%s') = %u' ''
[*] Command shell session 4 opened (?? -> ??) at 2019-07-09 20:27:44 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf5 auxiliary(scanner/ssh/ssh_login) > sessions -i 4
[*] Starting interaction with 4...

?
id
^C
Abort session 4? [y/N]  y
""

[*] 2.2.2.2 - Command shell session 4 closed.  Reason: User exit
msf5 auxiliary(scanner/ssh/ssh_login) > set gatherproof false
gatherproof => false
msf5 auxiliary(scanner/ssh/ssh_login) > run

[+] 2.2.2.2:22 - Success: 'admin:<<< %s(un='%s') = %u' ''
[*] Command shell session 5 opened (1.1.1.1:44217 -> 2.2.2.2:22) at 2019-07-09 20:28:10 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf5 auxiliary(scanner/ssh/ssh_login) > sessions -i 5
[*] Starting interaction with 5...

Remote Management Console
ssg5-serial-> show version
show version
              ^-------unknown keyword show

@h00die

This comment has been minimized.

Copy link
Contributor

commented Jul 10, 2019

Ubuntu 18.04 works fine

@h00die

This comment has been minimized.

Copy link
Contributor

commented Jul 10, 2019

Works as advertised. While not the best solution, working > not-working

@h00die h00die merged commit dc81adb into rapid7:master Jul 10, 2019
3 checks passed
3 checks passed
Metasploit Automation - Sanity Test Execution Successfully completed all tests.
Details
Metasploit Automation - Test Execution Successfully completed all tests.
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
h00die added a commit that referenced this pull request Jul 10, 2019
@h00die

This comment has been minimized.

Copy link
Contributor

commented Jul 10, 2019

Release Notes

The gatherproof option has been added to ssh_login modules. It will not gather proof by default, which will prevent causing bugs on non-standard devices, such as Brocade.

msjenkins-r7 added a commit that referenced this pull request Jul 10, 2019
@h00die h00die self-assigned this Jul 10, 2019
@wvu-r7

This comment has been minimized.

Copy link
Contributor Author

commented Jul 10, 2019

@h00die: What is the best solution?

@wvu-r7 wvu-r7 deleted the wvu-r7:bug/loginscanner branch Jul 10, 2019
@h00die h00die referenced this pull request Jul 16, 2019
1 of 6 tasks complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.