Do you recognize this password hashing format? - php

I'm trying to reverse-engineer a password scheme on a legacy PHP application, so I can port the passwords to a new system which will be replacing it. The application has passwords stored in two formats, a newer and an older one. The newer one simply uses crypt() with salt. The older one doesn't seem to have any supporting code any more (at least not in version control), and no hint of what may have been used. I have one account in the old style for which I may know the password, but I don't know how to check it.
The password is stored in the following format:
$1$f1KtBi.v$nWwBN8CP3igfC3Emo0OB8/
It appears to be three fields, delimited by $: 1, f1KtBi.v, and nWwBN8CP3igfC3Emo0OB8/. The first field is always 1. The second and third fields always match the regular expression [a-zA-Z0-9/.]+. The second field always has 8 characters, the third field always has 22.
Have you seen this password storage scheme before? Any idea what hashing mechanism might have been used?

This is the output of the crypt()[docs] function.
The 1 means that it used the MD5 algo internally.

That's most likely produced with crypt(), especially with MD5:
CRYPT_MD5 - MD5 hashing with a twelve character salt starting with $1$
Good luck finding the salt.

Related

Using password_hash PASSWORD_DEFAULT legacy concerns

From the php page: http://php.net/manual/en/function.password-hash.php
PASSWORD_DEFAULT - Use the bcrypt algorithm (default as of PHP 5.5.0). Note that this constant is designed to change over time as new and stronger algorithms are added to PHP. For that reason, the length of the result from using this identifier can change over time. Therefore, it is recommended to store the result in a database column that can expand beyond 60 characters (255 characters would be a good choice).
Would this mean that if I password_verify a password with a later version e.g. PHP8, it might not be able to decipher the stored password correctly? Or is all that information safely embedded already and all I have to do is store the output of password_hash into my DB and just call password_verify for validation without worries?
Would this mean that if I password_verify a password with a later version e.g. PHP8, it might not be able to decipher the stored password correctly?
No. The password hash includes information on which specific algorithm was actually used. If a future version of PHP supports more than one password hashing algorithm, it will be able to read that information from an old hash to figure out how to reproduce it.
(Specifically, the $2y$ prefix currently present on all hashes indicates that they were generated using the PASSWORD_BCRYPT algorithm. Any future algorithm will use a different prefix.)
Yes, all the information is safely embedded, provided that your database doesn't truncate the encrypted password. Which is pretty much a given, I'd think.
What that warning is driving home is that, for your future self's sanity, you store the information in a field whose length is flexible. Historically, like with MD5, one might have chosen CHAR(32). But with bcrypt, you need to choose something more flexible. That might be:
VARCHAR(255)
CHAR(60), with this column being the last one in the table, so that extending its length might not require the RDBMS to reshuffle table.

What will happen if they changed PASSWORD_DEFAULT in PHP Password library?

Consider this line of code by using PHP:
$password = password_hash($password, PASSWORD_DEFAULT);
What will happen if they changed the default password hashing algorithm? I mean I will be having hashed password inside the database. Then, from my own understanding, it will be impossible to check the password because the hashing algorithm will be totally changed.
What would happen is that newly-hashed passwords will be using the new algorithm - obviously.
However, you shouldn't be concerned about this, because the whole thing is designed with forward-compatibility in mind - your code won't be broken when the default algorithm changes, as long as you're using the password_*() functions correctly.
By correctly, I mean use password_verify().
password_verify() accepts a plain-text password and a hash, and it can easily determine what the used algorithm is by looking at the hash that you feed it. Therefore, it would also still be able to verify a password that has been hashed using an old algorithm - not just the previous one, but any algorithm that is supported.
In fact, the sole purpose of the PASSWORD_DEFAULT constant is that you can easily migrate older hashes to a new algorithm (once one is added). This happens the following way:
When a user logs in, you verify their password via password_verify() (any hashing algorithm that has a PASSWORD_<name> constant will work).
You call password_needs_rehash(), and if the password you just verified is using an older algorithm (or a lower 'cost' paramater) - it will return boolean TRUE.
If boolean TRUE was indeed returned, you can now replace the old hash with one that uses the new algorithm; you can do that during a login, because the user just gave you the password and you verified that it is correct.
In summary - it's a really, really well-designed API and it solves problems for you that you haven't even thought about. Don't worry about it.
Edit (noted in the comments):
It should be noted, however, that new algorithms will quite probly result in longer hash lengths, so if you're storing the passwords in a database - don't limit the field's length (i.e. use a varchar(255) field).
Just for clarification, I would like to add to the answer that PHP uses the following structure. Thus, the password_needs_rehash() and password_verify() functions will check the algorithm and cost and do their work to keep everything compatible and correct.
Source: http://php.net/manual/en/faq.passwords.php

Create and verify sha-512 password hashes with crypt() and PHP

The PHP function crypt() creates a hash for a password. It requires a second parameter for a random salt. This salt can also include additional instructions about which algorithm to use. Seems like a hack, but that's the API. So I add the $6$ prefix to get the SHA-512 hash I need. (Can't use bcrypt for the external non-PHP application that shall also be able to verify the hash. The hash must be verified from PHP and other applications with limited algorithms support.)
Now there are two problems that the PHP manual leaves me alone with:
1) Where do I get a random salt and what requirement does it need to satisfy? I've read about openssl_random_pseudo_bytes and I thought I'd just base64-encode it. But how many source bytes or encoded characters do I need?
2) How can I verify such a hash in PHP again? There doesn't seem to be a single function to do that. I believe I can call crypt again with the same salt as before, but to do that, I need to extract the hash and algorithm and whatever else is needed from the stored password hash. I've seen hashes with variable number of $ characters, so that doesn't seem like a good delimiter to split by.
I've read about phpass but it doesn't support sha-512, so I can't use that.
PHP version is 5.5.9 so some functions may not be available.

Where 2x prefix are used in BCrypt?

The question is the same title, Where $2x$ is used in BCrypt?
The following scenario is right?
We have a set of passwords that hashed with $2a$ prefix already, when the Server PHP version was earlier 5.3.7. Now we upgraded the PHP to 5.3.7+, now we must firstly verify previous passwords with $2x$ algorithm then rehash the password with $2y$ prefix. That's right?
BCrypt variants
$2$
BCrypt was designed by the OpenBSD people. It was designed to hash passwords for storage in the OpenBSD password file. Hashed passwords are stored with a prefix to identify the algorithm used. BCrypt got the prefix $2$.
This is in contrast to the other algorithm prefixes:
$1$: MD5
$3$: NTHASH
$5$: SHA-256
$6$: SHA-512
$7$: scrypt
$2a$
The original BCrypt specification did not define how to handle non-ASCII characters, or how to handle a null terminator. The specification was revised to specify that when hashing strings:
the string must be UTF-8 encoded
the null terminator must be included
$2x$, $2y$ (June 2011)
A bug was discovered in crypt_blowfish🕗, a PHP implementation of BCrypt. It was mis-handling characters with the 8th bit set.
They suggested that system administrators update their existing password database, replacing $2a$ with $2x$, to indicate that those hashes are bad (and need to use the old broken algorithm). They also suggested the idea of having crypt_blowfish emit $2y$ for hashes generated by the fixed algorithm. Nobody else, including canonical OpenBSD, adopted the idea of 2x/2y. This version marker was was limited to crypt_blowfish🕗.
The versions $2x$ and $2y$ are not "better" or "stronger" than $2a$. They are remnants of one particular buggy implementation of BCrypt.
$2b$ (February 2014)
A bug was discovered in the OpenBSD implementation of BCrypt. They were storing the length of their strings in an unsigned char. If a password was longer than 255 characters, it would overflow and wrap at 255.
BCrypt was created for OpenBSD. When they have a bug in their library, they decided its ok to bump the version. This means that everyone else needs to follow suit if you want to remain current to "their" specification.
http://undeadly.org/cgi?action=article&sid=20140224132743 🕗
http://marc.info/?l=openbsd-misc&m=139320023202696 🕗
The version $2b$ is not "better" or "stronger" than $2a$. It is a remnant of one particular buggy implementation of BCrypt. But since BCrypt canonically belongs to OpenBSD, they get to change the version marker to whatever they want.
There is no difference between 2a, 2x, 2y, and 2b. If you wrote your implementation correctly, they all output the same result.
And if you were doing the right thing from the beginning (storing strings in utf8 and also hashing the null terminator) then: there is no difference between 2, 2a, 2x, 2y, and 2b. If you wrote your implementation correctly, they all output the same result.
The only people who need to care about 2x and 2y are those you may have been using crypt_blowfish. And the only people who need to care about 2b are those who may have been running OpenBSD.
All other correct implementations are identical and correct.

How to make a permanent tripcode?

I mean, crypt()'s return is always different.
So how do websites like 4chan do to give a permanent tripcode to a password?
Are they stored in a database?
4chan's tripcodes are created using a specific formula, and are a shorter version of a hash. You can achieve the same effect by using MD5 or SHA1.
Encrypt string to MD5 (PHP):
$md5 = md5("$string");
Encrypt string to SHA1 (PHP):
$sha1 = sha1("$string");
There is no way to reverse the hashing process (just like tripcodes), but with time and power they can be "bruteforced" back to plain text.
It's quite common to salt a password, then hash it using DES, MD5, SHA, or newer hashes. The salt is then stored as part of the password.
PHP's crypt works this way, although the exact algorithm it uses to hash the password may be different between versions of PHP... and even between operating systems, although the latter supposedly changed in PHP 5.3. (PHP now includes its own hashing library instead of relying on the OS library, which is really, really important if you're using Windows, as crypt function on Windows only supported DES with 2-byte salt prior to this)
Edit:
Note: crypt has an optional second argument. Passing the encrypted password as the second argument will usually get PHP to detect the salt and algorithm used to originally hash the password, namely because everything other than DES start with $#$ where # is a number.
You pass the salt to crypt() as the second argument. This causes the output to use that salt instead of generating one on the fly.
The salt being randomly generated is why crypt("something") returns different results each time. If I run crypt("something", "ab"), it'll be identical every time. I don't have PHP here to check what the value is, though.
Wikipedia has an article about Tripcodes.
I think there's a table "tripcodes" where tripcodes were generated with the Wikipedia's and they are associated with strings they come from, no?
Yes password are stored in a database but without the use of crypt(). They use sha1() or encryption database function like AES_ENCRYPT() in mysql.

Categories