I created a hash which is encrypted like this: $What_i_made=BCrypt(MD5(Plain Text Password)) and I wonder if it can be cracked. Currently, I thought of two ways:
- Brute force
$What_i_madeto get the MD5 Hash then do a dictionary attack on the MD5 Hash. However, this will take ages as Bcrypt is so slow and a MD5 is 32 characters long. $result=Bcrypt(MD5(random combination))and compare$resultto$What_i_madeuntil they match. This will be much faster, but I am not really sure how to do this. I tried John and Hashcat but I am not really sure how you can do this with them, so I am turning to the community for help. Thanks. :)
BTW, any other tools that work will also do and I would prefer a method which allows for trying every single combinations instead of dictionary attaks.
12 Answers
As a password cracker, I encourage all of my targets to use this technique. ;)
It seems like a good idea, but it turns out that against real-world attacks, wrapping an unsalted hash with bcrypt is demonstrably weaker than simply using bcrypt.
This is because attackers can do this:
- Acquire existing MD5 passwords - even ones that haven't been cracked yet
- Run these MD5s as a wordlist against your
bcrypt(md5($pass))corpus, to identify bcrypts with known MD5s - crack the MD5s outside of bcrypt at much higher speed
In other words, in many cases you can simply crack the inner hash first. And for a fast hash like MD5, that means that for any password that can be cracked first, bcrypt's resistance to brute-force attack is dramatically weakened.
(I can't take credit for the technique, but it's very effective - especially when users reuse passwords across multiple sites, and the attacker has access to leaked password data.)
Here's a more specific, single-user scenario:
- User has an account on Site B that uses bcrypt(md5($pass))
- Site B is compromised, and its password-hash lists is leaked online
- Attacker acquires the Site B leak
- Attacker does a fast run against the Site B dump and determines from testing that the site is using bcrypt(md5($pass))
- Attacker first checks to see if any other known leaks in their collection contain
- If so, and any of those other leaks use MD5, Attacker simply tries 's other MD5s to see if Jo was reusing her password, and it's the one "inside" the bcrypt
- If Jo's MD5 is inside that bcrypt, Attacker can now attack that MD5 at massive speeds until they find a crack. Attacker now knows 's original password on Site B
Now, imagine that Attacker wants to attack all 100,000 bcrypt hashes on Site B ... but Attacker also has access to thousands of other leaks:
- Attacker writes a script to check all MD5 leaks for email addresses that match Site B
- Attacker first tries the user-specific MD5s against each specific user's bcrypt from Site B. (This is a "correlation attack"). Attacker quickly cracks about 20% (which also removes their salts from the overall attack, increasing attack speed for the remaining hashes)
- Attacker next tries other known MD5s from common passwords, and similarly removes those bcrypts from the attack
- Attacker then tries other unknown MD5s from those leaks. If they find one, they can then attack those MD5 as direct MD5s (without involving bcrypt at all)
And yes, the attack can also be done directly - either by MD5'ing the candidate passwords yourself, or using a tool that natively supports bcrypt(md5($pass)), such as MDXfind:
$ echo "password" | tee bcrypt-md5.dict
password
$ echo -n 'password' | md5sum | awk '{print $1}'
5f4dcc3b5aa765d61d8327deb882cf99
$ htpasswd -bnBC 10 "" 5f4dcc3b5aa765d61d8327deb882cf99 | tr -d ':\n' | tee bcrypt-md5.hash; echo
$2y$10$
$ mdxfind -h '^BCRYPTMD5$' -f bcrypt-md5.hash bcrypt-md5.dict
Working on hash types: BCRYPTMD5
Took 0.00 seconds to read hashes
Searching through 0 unique hashes from bcrypt-md5.hash
Searching through 1 unique BCRYPT hashes
Maximum hash chain depth is 0
Minimum hash length is 512 characters
Using 4 cores
BCRYPTMD5 $2y$10$
Done - 1 threads caught
1 lines processed in 0 seconds
1.00 lines per second
0.07 seconds hashing, 2 total hash calculations
0.00M hashes per second (approx)
1 total files
1 BCRYPTMD5x01 hashes found
1 Total hashes foundUnfortunately (for the attacker ;) ), it looks like John the Ripper "jumbo" edition doesn't support this algorithm using its dynamic syntax:
$ john --format=dynamic='bcrypt(md5($pass))' --test
Error: dynamic hash must start with md4/md5/sha1 and NOT a *_raw version. This expression one does notBut for a focused attacker, it's much more efficient to simply dig out those MD5s from your hashes, and then attack those MD5s at speeds of billions of candidates per second on GPU.
If you want to do something like this - for example, to work around bcrypt's 72-character maximum - use a per-plain salt, a site-wide pepper, or true encryption in the MD5 step.
7This composite hash has no benefit over plain bcrypt. It could be marginally weaker due to MD5 collisions, but I don't think one could actually exploit it to make this attack faster. Anyway, there's no reason to pre-hash passwords with MD5. Now, to the actual answer.
You approach #2 won't work because bcrypt uses integrated random salts. Hashing the same input twice will produce two different hashes because different salts will be generated.
The result of bcrypt is actually a data structure containing actual hash and salt. To verify if a password is correct, you have to extract original salt from original hash structure and use it to hash the password to be verified. If the resulting hash matches the original, the password is valid. This feature is usually provided by bcrypt implementations.
If you're going to perform a dictionary attack, simply pre-hash all entries in your dictionary with MD5 and then run a bcrypt dictionary attack with the dictionary of MD5s. Reversing MD5s for successfully cracked passwords will be very easy and I'm leaving it for you to figure out.
A basic brute-force attack where all combinations are tested is impractical against bcrypt and MD5 is irrelevant.
10