How can I prove this site has a huge security weakness?
The password format in the userpassword attribute looks like the standard format used by various unix services, such as the default system password service which stores hashed passwords in /etc/shadow
. The format is basically:
$ type $ salt $ hash
In your example, the type is 1, which designates an md5 hash. There are other well-known types, such as various sizes of the sha-family hash functions.
Breaking an md5 hash is almost trivial today, even when it's salted, because md5 is so fast. It's not a hash function which is safe to use for hashing passwords; hashcat and other password crackers can literally hash millions or even billions of password candidates per second.
So this data store service isn't just horrible because it sends user password hashes back, it's even more horrible because it actually uses md5 hashes to store passwords.
To prove to your boss that this really isn't secure, you could download hashcat and a few password lists (you can easily find them online), and then run hashcat on a computer with a very powerful GPU on all the passwords you get from the service. Make a note of how many passwords hashcat can crack (without looking at the passwords themselves - they may reveal private information about the people who chose them, and you don't want to actually know their passwords), and inform the people whose passwords were compromised that they need to choose a new password. You'll probably need to get permission to do all this first, because depending on where you live and work, this might actually be considered a malicious attack, or even be illegal.
Aside: If you were stuck on using this service even after voicing your misgivings, I'd suggest that setting up an automated password cracker that runs without human intervention and informs people (by sending them an e-mail) when it manages to crack their password would be a way to protect your users from this kind of bad design. It would help to weed out weak passwords and increase the amount of time needed by a real attacker to crack passwords.
Edit: Zach pointed out that the aside isn't common practice and shouldn't be attempted by someone not able to assess the risks. I fully agree with that. At the very least, if you did something like that, you should have management okay it.
Still, NOT doing this doesn't make the resulting system more secure. It's the equivalent of sticking your head in the sand out of fear that if you actually did something to improve password security, it might backfire on you. We know that people choose bad passwords, and we know that md5 is not a good way to hash passwords. If we can't change these two facts, and we are in a position to weed out weak passwords, then we should probably do it to make our users safer.
One important reason this isn't seen much, or even considered much, is that we usually aren't in a position to implement it. Good systems don't use fast hash functions to hash passwords, and we usually don't get access to the whole hashed password database.
This password hash seems to use the crypt()
format (which, despite its name and what some documentations say, including that very man page, has absolutely nothing to do with encryption; it is hashing). When it starts with $1$
, this means that it is a password hashing function based on MD5. Its exact specification is "whatever glibc does". A look at the source code shows some enlightening passages:
201 /* The original implementation now does something weird: for every 1
202 bit in the key the first 0 is added to the buffer, for every 0
203 bit the first character of the key. This does not seem to be
204 what was intended but we have to follow this to be compatible. */
205 for (cnt = key_len; cnt > 0; cnt >>= 1)
206 md5_process_bytes ((cnt & 1) != 0
207 ? (const void *) alt_result : (const void *) key, 1,
208 &ctx, nss_ctx);
209
210 /* Create intermediate result. */
211 md5_finish_ctx (&ctx, nss_ctx, alt_result);
212
213 /* Now comes another weirdness. In fear of password crackers here
214 comes a quite long loop which just processes the output of the
215 previous round again. We cannot ignore this here. */
216 for (cnt = 0; cnt < 1000; ++cnt)
217 {
From this, we can conclude that this hashing function is not very well documented.
Anyway, as password hashing functions go, it's not very good. It uses a salt, and that's good, because it should prevent use by attackers of precomputed tables (including rainbow tables). It also includes a loop with many (1000) iterations, as an attempt to make password hashing slower, thereby making it harder for attackers to try many potential passwords until a match is found (a process known as "brute force" or "dictionary attack").
Unfortunately, it's not very good either. MD5 is fast, and a thousand nested MD5, while still 1000x slower, is still fast. An off-the-shelf PC with a basic gaming-oriented GPU can compute millions of such hashes per second; an average user password won't last long; and by that, I mean that an attacker spending one minute of computation per hashed password will still crack half of them.
Apart from the hashed password, simply revealing user's email addresses is already a pretty damnable offence -- I mean legally speaking. For instance, in Canada, this is known as "personal information" and all users would be entitled to sue you.
Stop. Go no further. Do no more testing, demonstration, etc. until you have been explicitly asked to do so in writing, and even then beg off and suggest that there are many more qualified people than you to do an audit/pentest/etc.
I know of 2 people who did similar with less information (no passwords, just details that they didn't need access to and shouldn't have had access to) just a few months ago and they are still going through the process of dealing with felony charges.