Saturday, June 16, 2012

Password Hashing: Best Practice

Last week I read a post on Brian Krebs’ blog where security researcher Thomas Ptacek was interviewed about his thoughts on the current landscape of password hashing. I found Thomas’ insights into this topic quite pertinent and would like to reiterate his sentiments by talking a little about the importance of choosing the right password hashing scheme.

The idea of storing passwords in a “secret” form (as opposed to plain-text) is no new notion. In 1976 the Unix operating system would store password hash representations using the crypt one-way cryptographic hashing function.  As one can imagine, the processing power back then was significantly less than that of current day standards. With crypt only being able to hash fewer than 4 passwords per second on 1976 hardware, the designers of the Unix operating system decided there was no need to protect the password file as any attack would, by enlarge, be computationally infeasible. Whilst this assertion was certainly true in 1976, an important oversight was made when implementing this design decision: Moore’s Law.

Moore’s Law states that computing power will double approximately every two years. To date, this notion has held true since it was first coined by Gordon Moore in 1965. Because of this law, we find that password hashing functions (such as crypt) do not withstand the test of time as they have no way of compensating for the continual increase in computing power. The problem we find with cryptographic hashing functions is that whilst computing power continually increases over time, password entropy (or randomness) does not.

If we fast-forward to the current day environment we see that not much has changed since the early implementations of password hashing.  One-way cryptographic hashing functions are still in widespread use today, with SHA-1, SHA-256, SHA-512, MD5 and MD4 all commonly used algorithms. Like crypt on the old Unix platform, these functions have no way of compensating for an increase in computing power and therefore will become increasingly vulnerable to password-guessing attacks in the future.

Fortunately, this problem was solved in 1999 by two researches working on the OpenBSD Project, Niels Provos and David Mazieres. In their paper, entitled ‘A Furture-Adaptable Password Scheme’, Provos and Mazieres describe a cost-adaptable algorithm that adjusts to hardware improvements and preservers password security well into the future.

Based on Bruce Shneier’s blowfish algorithm, EksBlowfish (or expensive key scheduling blowfish), is a cost-parameterizable and salted block cipher which takes a user-chosen password and key and can resist attacks on those keys. This algorithm was implemented in a password hashing scheme called bcrypt.

Provos and Mazieres identified that in order for their bcrypt password scheme to be hardened against password guessing, it would need the following design criteria:
  1. Finding partial information about the password is as hard as guessing the password itself. This is accomplished by using a random salt.
  2. The algorithm should guarantee collision resistance.
  3. Password should not be hashed by a single function with a fixed computational cost, but rather by of a family of functions with arbitrarily high cost.
The first two points here are fairly standard among password hashing algorithms. The last point, however, is somewhat of a different concept to what we are used to seeing.

If we think about cryptographic hashing functions in use today - be it MD5, SHA-1, or MD4 - we notice they all have one thing in common: speed. Each of these algorithms is incredibly quick at computing its respective output. As hardware gets better, so too does the speed at which these algorithms can operate. Whilst this may potentially be seen as a positive thing where usability is concerned, it is certainly an undesirable trait from a security standpoint. The quicker these algorithms are able to compute their output, the less time it takes for the passwords to be cracked.

Provos and Mazieres were able to solve this problem by deliberately designing bcrypt to be computationally expensive. In addition, they introduced a tunable cost parameter which is able to increase the algorithm’s completion time by a factor of 2 each time it is incremented(effectively doubling the time it takes to compute the result). This means that bcrypt can be tuned (i.e. incremented every 2 years) to keep up with Moore’s Law.

Let’s have a quick look at how bcrypt compares against other hashing algorithms. Let’s say we have a list of 1000 salted SHA-1 hashes that we wish to crack. Because they are salted, we can’t use a pre-computed list of hashes against this list. We can, however, crack the hashes one by one. Let’s say on modern hardware it takes a microsecond (000000.1 seconds) to make a guess at the password. This means that per second we could generate 1 million password guesses per hash.

In contrast, say we have the same list but instead of SHA-1 the hashes have been derived using bcrypt. If the hashes were say computed with a cost of 12, we are able to compute a password guess roughly every 500 milliseconds. That is, we can only guess 2 passwords a second. Now if you think we have 1000 password hashes, we can only generate 7,200 guesses per hour. As it would be more efficient targeting all 1000 hashes (as opposed to just one), we would be limited to approximately 7 guesses per hash.

Below is the output of a quick program I created to display the time it takes to compute a bcrypt comparison as the cost value is incremented. The test was conducted on a RHEL system running a Xeon X5670 @ 2.9Ghz with 1024MB of memory.

The source code for this program can be found here.

The bcrypt password hashing scheme is currently available in many languages including Java, C#, Objective-C, perl, python, and Javascript among others.

I hope this post highlights the importance of using stronger password hashing schemes for modern day applications. 

Tuesday, May 22, 2012

Exploiting the Windows Domain

A common recommendation I often come across is that Internet-facing systems should not be a part of an active Windows domain. As an exercise of interest, I have decided to look at this topic a little deeper and explore what advantage (if any) access to a domain member really provides.

In this scenario I will demonstrate how to gain privilege within a Windows domain using only the tools available on a default Windows install. I will be working under the assumption that:
  1. I have access to a public terminal (or something similar) with up-to-date anti-virus.
  2. I do not have administrative access on the host.
  3. I do not have access to any third-party tools.
Once connected to a Windows workstation, the first piece of information I want to find is the domain namespace. This can be done a couple of different ways:
nbtstat –A <IP-Address>

net config workstation

Next, because I am working from a domain member, I can query the domain controller and check whether it’s aware of any additional domains:
net view /domain

*Note: It is often advantageous to target other domains such as those used for testing and development. These environments will often contain hosts where less emphasis is placed on security.

Next I am interested in knowing what hosts exist on the domain. For this, I can query the domain controller:
net view /domain:<DOM> > hosts.txt

Depending on the size of the network this listing can get quite large. I have redirected the output into a text file to prevent continually query the domain controller.

*Note: This command will not typically respond with every Windows host on the network. Only hosts available via NETBIOS are known to the domain controller.

Because I’m on a Windows domain, I can be fairly certain some machines will have file sharing turned on.  Large networks are often host to a myriad of file shares with “interesting” data on them. It’s not uncommon to find personally identifiable information or even login credentials sitting on workstation or server shares.

To obtain a list of systems with active shares I can query each domain member by using:
for /f %i in (hosts.txt) do @(net view \\%i >> shares.txt 2>nul)

This command will loop over the file containing the domain members (obtained from the previous command) and query each host for open shares. Any errors (i.e. inaccessible systems) are discarded.

As previously mentioned, in some cases you may get lucky and find exactly what you’re looking for within these shares. For the sake of this exercise, however, let’s assume there was nothing interesting found.

Next I am interested in what users and groups exist on the domain. The goal here is to elevate my privileges on the domain (remember, I only have ‘User’ rights on one system thus far). To obtain a list of domain users and groups I can query the domain controller as follows:
net user /domain > users.txt 
net group /domain > groups.txt

Once this information is gathered I want to look for “interesting” user accounts. Usernames containing the words “temp”, “test”, “tst”, “tmp”, "helpdesk", "ftp" are all of interest here as testing and temporary accounts often have simple passwords.
type users.txt | find “test”

If there are no stand-out usernames in the list above I can direct my efforts on querying the groups:
 type groups.txt | find “helpdesk”

The domain controller can then be queried to dump the users belonging to that group:
net group “helpdesk” /domain

*NOTE: I have chosen to target non-administrative accounts here as they typically have weaker passwords. However, it is certainly not unheard of to have weak passwords on account belonging to “Domain Admins”. Always worth checking :)

Having chosen a number of domain users to target I can now attempt to compromise these accounts through password guessing. I can accomplish this through SMB connection attempts against a host on the domain. Which host I choose here doesn’t matter as authentication occurs against the domain controller and not the host itself.
for /f %i in (users.txt) do @(for /f %j in (passwords.txt) do @(echo Trying %i:%j... >> success.txt && net use \\wombat /u:%i %j 1>>success.txt && net use \\wombat /del))

This script will loop over a list of targeted usernames (in users.txt) and try simple password attempts against each account from the passwords.txt file. Success.txt will keep a log of successful password guesses. Keep in mind here I am only targeting the low hanging fruit. Only a few passwords are being attempted for each account as to not lock out any accounts.

With a small adaptation this script I can choose to target every account in the entire domain:
for /f %i in (users.txt) do @( echo Trying %i:%j... >> success.txt && net use \\wombat /u:%i %i 1>>success.txt && net use \\wombat /del)

Here I am scanning the entire domain for accounts that use their username as their password. This is sometimes known as a horizontal password scan.

The benefits of horizontal scanning become apparent in environments with a large user base. An increase in the number of accounts often improves the chances of discovering an account with a weak password. The chance of locking out an account is also significantly decreased as only a small number of password guesses are attempted against each account.

The above script could also be tweaked to guess the password of the default local administrative account (RID 500) on the current machine. Providing this account is active, it cannot be locked out (meaning infinite password guesses).

Once credentials have been acquired for a domain account the next step is to find out what access the account has. Indeed, it may be the case that only a particular service on a particular host is accessible from the captured account.

The next step in this process is to find out what services are available on the domain. This would be quite simple with nmap... but remember, we don’t have access to any third-party tools! To get around this I have adapted Ed Skoudis’ FTP command line port scanner:
for /f %i in (hosts.txt) do @(for /f %j in (ports.txt) do @(echo Checking %i:%j... & echo %i:%j >> success.txt & echo open %i %j > commands.txt & echo quit >> commands.txt & ftp -n -s:commands.txt 1>>success.txt))

This script attempts to make a connection to each port in ports.txt (I've chose 21\ftp) for every domain member in hosts.txt (found at the beginning of this exercise). It uses the built-in Windows FTP client to read in commands (-s flag) from commands.txt to make each connection. Logging information is stored in success.txt.

Once the available network services have been mapped I can then attempt to exploit / gain access to these services:

In this post I have demonstrated several examples of how domain membership can be abused to gain privilege on a Windows' domain. Due to the inherent verbose nature of Windows’ domains, attackers have the advantage of gaining valuable information about a target network in a relatively short period of time. That said, however, a skilled attacker always has (and always will) be able to penetrate a network’s defences regardless of having domain membership or not.