Username enumeration techniques and their value

Introduction

One of the first steps when looking to gain access to a host, system, or application is to enumerate usernames. Once usernames are guessed or enumerated targeted password based attacks can then be launched against those found usernames. 

In this blog post, we discuss common techniques that are used to enumerate usernames. While these techniques are not new, there is still some value in discussing them, as they are such an important part of the process of gaining access to systems.

Previous Work

This blog is a continuation from Ben Williams’ presentation [1] The L@m3ne55 of Passw0rds:
Notes from the Field
, in which he discusses password-based attacks, a sometimes-underused method of gaining access to a host or system.

Username Enumeration Techniques

A number of useful and often used techniques for enumerating valid usernames currently exist; they can be categorised into two broad categories, web application and infrastructure-based username enumeration, although others may exist.  The following examples are by no means exhaustive; they serve as examples of the types of issues which provide consultants and threat actors alike the ability to enumerate usernames.

Web Application

Standard Authentication:

In standard authentication, a user is required to enter a username and password into a form to gain access to the web application.  When entering an invalid username along with a password, a generic message such as “incorrect password” is often returned, suggesting that the username does not exist.  However, when entering a valid username and an incorrect password, we will often see a message such as “password incorrect for this user”, suggesting that the username is valid.  A malicious user can use automated tools to gather a list of valid usernames using this method.  Once valid usernames have been successfully enumerated, a brute-force attempt to retrieve passwords can be used against those usernames.

Forgotten Password

When using a recovery facility such as a forgotten password function, a vulnerable application might return a message that reveals if a username exists or not.   Entering a valid username or email address may return something along the lines of:

Your password has been successfully sent to the email address you registered with

where we can assume that we have identified a valid username; however, an invalid username or email address would return something along the lines of:

email address is not valid or the specified user was not found

Predictable Username Formats

In some cases user IDs are created with specific predictable sequences or formats. For example, we can view users with IDs created in sequential order:

  • CN000100
  • CN000101

Armed with this information, an automated attack can take place by increment the value and using a technique such as the forgotten password functionality (discussed above) to determine if a username is valid.

It is worth noting that that the above methods can be expanded to include more than simply the message that is returned; other factors should also be analysed when attempting to enumerate valid usernames [2], including:

  • The error code received on login pages for valid and non-valid credentials;
  • URLs and URL redirections for valid and non-valid credentials;
  • Web page titles for valid and non-valid credentials;

WordPress

Default Installation

WordPress is a free and open-source content management system based on PHP and MySQL.  Features include a plugin architecture and a template system. WordPress was used by more than 23.3% of the top 10 million websites as of January 2015.  WordPress is the most popular blogging system in use on the Web, at more than 60 million websites. [3]

Under a non-hardened WordPress installation, it is possible to enumerate usernames.  For example, wpscan [4] can be used to enumerate WordPress usernames. Below is an example of the type of output received from the enumerate users module (ruby ./wpscan.rb –url http://www.example.com –enumerate u):

+----+---------------+------+
| Id | Login | Name |
+----+---------------+------+
| 1 | administrator | |
| 2 | edward | |
| 3 | gareth | |
| 5 | dylan | |
| 6 | dafydd | |
| 7 | sarah | |
+----+---------------+------+

Coupled with the ability to identify the /wp-admin/ admin page, we could then use usernames to perform a targeted brute-force attack.

Bespoke WordPress Username Enumeration

There are instances when standard tools, like wpscan do not work when looking to enumerate usernames.

It is worth taking the time to look at the structure at the website and determine the possibility of a bespoke method of enumerating usernames.  The following is an example, redacted, script that has been used in the past to enumerate valid usernames for WordPress sites when standard tools were not found to be successful:

#!/usr/bin/python
#EDW NCCGroup.trust
#enumerate usernames from authors url from urllib2 import Request, urlopen, URLError, HTTPError
import re
import optparse p = optparse.OptionParser("usage: %prog -f file", version="%prog 0.1")
p.add_option("-f", "--file", dest="file", type="str", help="name of file for usernames")
(options,args) = p.parse_args() if len(args) != 0:
parser.error("parser error")
exit() file = options.file
filename = file
try:
f = open(filename,'a')
except:
print "unable to open file" req = Request('http://XXXXX.XXX.XXX.XX/authors/') try:
response = urlopen(req)
except HTTPError as e:
print 'The server couldn\'t fulfill the request.'
print 'Error code: ', e.code
except URLError as e:
print 'We failed to reach a server.'
print 'Reason: ', e.reason
else:
if response.getcode() == 200:
for a in response:
m = re.search("http://XXXXX.XXX.XXX.XX/authors//(.*)/\" title=", a)
if m:
f.write(m.group(1)+"\n")

Infrastructure Based Enumeration

Default/Common Usernames

A number of common, well-known, usernames exist on default installations of operating systems and software.  This information can be used to create a list of usernames.  For example, it is reasonable to assume, unless there is evidence to the contrary, that a Windows host will have an account called administrator.  The following is a sample list of common, well-known usernames:

  • administrator
  • root
  • guest
  • backup
  • test
  • Service accounts, for example:
    • SophosManagement
    • SophosUpdateMgr
Username Enumeration through Port Identification

When conducting an internal pen test, it is common practice to profile hosts; this is normally done through a port scan, as open ports can often lead themselves to username enumeration.  For example, identifying TCP port 1521 on a host will, in more cases than not, indicate that the host has an oracle user. Similarly, an open TCP port of 5432 will often have a user named postgres.

Guessable Usernames

It is often the case that usernames are guessable, because they are all created using a common well-known format.  After discovering the format, for example through open source intelligence (OSINT), it would be possible to generate a list of possible usernames. 

Figure 1: OSINT to Discover Username Format [5]

Common examples of username formats include:

Username FormatExample
firstnameedward
firstname.surnameedward.williams
firstnamesurnameedwardwilliams
first letter of first name and surnameewilliams
surname and first letter of firstnamewilliamse
incremental number00001

RID Cycling

Through a process of RID cycling it is possible to enumerate all domain users from a Windows 2003 domain controller.  This method will work on Windows 2003 domain controllers, as the SID of the “domain users” group can then be enumerated; this was done to ensure a good level of compatibility and the same technique will not work on Windows 2008 domain controllers. With this information it is then possible to iterate through the RIDs to enumerate users.  The following is an example of automated username enumeration using GetAcct [6] from a Windows 2003 domain controller:

Figure 2: RID Cycling Results

Kerberos Username Validation

With RID cycling becoming less common, it is possible to elicit valid usernames from the Kerberos service of a domain controller.  When an invalid username is requested, the server will respond using the Kerberos error code KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN, allowing us to determine that the user name was invalid. Valid user names will elicit either the TGT or an AS-REP response or the error KRB5KDC_ERR_PREAUTH_REQUIRED, signalling that the user is required to perform pre-authentication and that the user is valid [6].

Using the krb5-enum-users [6] nmap nse script, users can be enumerated from a domain controller, as shown in the following example:

Figure 3: Kerberos Username Enumeration

OpenSSH Username Enumeration

Certain versions of OpenSSH suffer from a timing-based attack: if a valid username with a long password is given, the time taken to return is noticeably longer than for an invalid username with a long password.

The following is an example of this, for which a custom script was written; as can be seen both the “root” and “ed” users have been enumerated. The figure on the right-hand side is the time it took to respond, and for the enumerated usernames the time is significantly greater than for non-enumerated users:

Figure 4: OpenSSH Username Enumeration

SMTP Username Enumeration

Several methods exist that can be used to abuse SMTP to enumerate valid usernames and addresses; namely VRFY, EXPN, and RCPT TO. 

VRFY

This command will request that the receiving SMTP server verify that a given email username is valid. The SMTP server will reply with the login name of the user. This feature can be turned off in sendmail, because allowing it can be a security hole. VRFY commands can be used to probe for login names on a system.

An example of this using VRFY is given below, where the root user is enumerated:

$ telnet 10.0.0.1 25
Trying 10.0.0.1...
Connected to 10.0.0.1.
Escape character is '^]'.
220 myhost ESMTP Sendmail 8.9.3
HELO
501 HELO requires domain address
HELO x
250 myhost Hello [10.0.0.99], pleased to meet you
VRFY root
250 Super-User <root@myhost>
VRFY blah
550 blah... User unknown
EXPN

EXPN is similar to VRFY, except that when used with a distribution list, it will list all users on that list. This can be a bigger problem than the “VRFY” command since sites often have an alias such as “all”.

An example of this using EXPN is given below:

$ telnet 10.0.10.1 25
Trying 10.0.10.1...
Connected to 10.0.10.1.
Escape character is '^]'.
220 myhost ESMTP Sendmail 8.9.3
HELO
501 HELO requires domain address
HELO x
EXPN test
550 5.1.1 test... User unknown
EXPN root
250 2.1.5 <ed.williams@myhost>
EXPN sshd
250 2.1.5 sshd privsep <sshd@mail2>
RCPT TO

This identifies the recipient of the email message. This command can be repeated multiple times for a given message in order to deliver a single message to multiple recipients.  The RCPT TO: technique is extremely effective at enumerating local user accounts on most Sendmail servers.

An example of using the RCPT TO method is given next with the ed user enumerated:

$ telnet 10.0.10.1 25
Trying 10.0.10.1...
Connected to 10.0.10.1.
Escape character is '^]'.
220 myhost ESMTP Sendmail 8.9.3
HELO x
250 myhost Hello [10.0.0.99], pleased to meet you
MAIL FROM:test@test.org
250 2.1.0 test@test.org... Sender ok
RCPT TO:test
550 5.1.1 test... User unknown
RCPT TO:admin
550 5.1.1 admin... User unknown
RCPT TO:ed
250 2.1.5 ed... Recipient ok

ACF2

ACF2 (Access Control Facility) is a commercial, discretionary, access control software security system developed for the MVS (z/OS), VSE (z/VSE) and VM (z/VM) IBM mainframe operating systems [7].

Through the responses received when connecting to the mainframe, AS400 in this instance, it is possible to enumerate valid usernames.  The following is a PoC that was created to automate this process:

#!/usr/bin/python
# EDW NCCGroup.trust
# ACF2 Username Enumeration import sys
import time
import optparse
import re
import signal
from telnetlib import Telnet
from socket import * p = optparse.OptionParser("usage: %prog host user port", version="%prog 0.1")
p.add_option("-H", "--host", dest="host", type="string", help="specify hostname to run on")
p.add_option("-u", "--userfile", dest="user", type="string", help="file of usernames")
p.add_option("-p", "--port", dest="port", type="int", default=23, help="port number, default is 23") (options, args) = p.parse_args() host = options.host
user = options.user
port = options.port def main():
try:
u = open(user).read().splitlines()
except IOError as e:
print "I/O error({0}): {1}".format(e.errno, e.strerror)
sys.exit() for n in u:
tn = Telnet(host, 23, 120)
tn.write('test\r\n')
tn.read_some()
tn.write(n+'\r\n')
tn.read_some()
tn.write('pass\r\n')
data = tn.read_until('test',1)
if re.search (r"SUSPENDED BECAUSE OF PASSWORD VIOLATIONS",data):
print ("user found: "+str(n))
elif re.search (r"PASSWORD NOT MATCHED",data):
print ("user found: "+str(n))
tn.close() def signal_handler(signal, frame):
print "\nCtrl+C pressed.. aborting..."
exit()if __name__ == '__main__':
signal.signal(signal.SIGINT, signal_handler)
main()

 The results of running this are shown in figure 5:

Figure 5: ACF2 Username Enumeration

Generic Mitigations

  • For web applications, create a generic message, preventing the ability for users to elicit usernames.
  • When creating Active Directory usernames, consider an element of randomness; for example;
    • dylan.williams could be dylan.williams3280 and
    • dafydd.williams could be dafydd.williams6782
    • For WordPress, there exist a number of plugins that can be used to stop the enumeration of usernames.
      • Restrict access to /wp-admin by means of IP restriction.
      • Implement two-factor authentication (Authy, Google)
      • Change easily guessable and default usernames to more complex, less guessable values.
      • Ensure all software is running at the latest, stable release.
      • Harden services such that null binds cannot be established and remote root authentication is not allowed.

Summary

While username enumeration is not new, it is still an important technique when looking to gain access to a host or system. Broad techniques for enumerating usernames from web applications, and from infrastructure including Windows, *nix, and mainframe environments, have been discussed.  The importance of being able to enumerate usernames from a security consultant’s or threat actor’s perspective cannot be underestimated; while mitigating username enumeration is by no means a silver bullet, it should be included within an organisation’s risk assessment along with strong passwords, robust patching, and appropriate segregation.

References

[1] https://www.nccgroup.trust/en/learning-and-research-centre/presentations/the-l4mene55-of-passw0rds-notes-from-the-field/

[2] https://www.owasp.org/index.php/Testing_for_User_Enumeration_and_Guessable_User_Account_%28OWASP-AT-002%29

[3] http://en.wikipedia.org/wiki/WordPress

[4] http://wpscan.org/

[5] http://community.spiceworks.com/topic/224816-logon-names-what-format-do-you-use

[6] http://www.securityfriday.com/tools/GetAcct.html

[7] https://nmap.org/nsedoc/scripts/krb5-enum-users.html

[8] http://en.wikipedia.org/wiki/ACF2

Published date:  10 June 2015

Written by:  Information Security Expert