New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Underscores in MD5 salts rejected #105
Comments
Hello, and thanks for your question. Having an underscore character Let me explain a bit: An MFC string is formatted as At the time the hash implementations of glibc's libcrypt have been written, considerations have been users of the Sadly the libcrypt in glibc is not that actively developed, I mean it is maintained, but does not that frequent development as other parts of glibc do. For this (and some other reasons) some people (including me) came up to revive and vastly improve libxcrypt as a (drop in) replacement. From what I can see here: The behaviour you describe is a bug in glibc's libcrypt which has been fixed for its implementation of I conclude libxcrypt has the correct behaviour of refusing any MFC string input that is not valid BASE64 encoded, while the behaviour of glibc libcrypt is faulty. @zackw Do you agree with me? |
@besser82 You didn't ask me, but FWIW I would have treated this rejection of historically accepted unusual (not even invalid) md5crypt salts as a libxcrypt bug. Like you say, "there is no official technical specification" on what characters the salt string may contain, except with hashes like bcrypt and descrypt where the salt is decoded before being used. (I also wouldn't have changed glibc's descrypt to reject invalid salts like was done in that commit you reference. Instead, we could have enhanced libxcrypt's FreeSec to handle invalid salts in the same way that old glibc's UFC crypt handled them prior to that commit. I have a revision of FreeSec implementing that. However, now that glibc already went the other way, I am not sure it's a good idea to reintroduce that for descrypt.) |
I agree that the behaviour here is in terms of rejecting non-base64 characters is correct according to the spec (whether written down or generally understood), however the aim of this project is to be a drop-in replacement for glibc's libcrypt then this is not happening here. Centos 6 & 7 glibc have this behaviour so it would seem to be a fairly mainstream 'bug' - perhaps 'feature' or 'implementation edge case' would be a better description of it? |
Note also that these password salts were generated by older cpanel versions (they now use sha512 or so by default), however many of these hashes are still around. cpanel does not currently support centos 8 so most hosters are using centos 7. However I can imagine this bug being fairly widely seen once the cpanel centos 8 upgrade is available. |
Let me setup a RHEL 7 VM and do some tests how @solardiz Sorry for not asking you about this, too. I know you are a quiet busy person, and didn't want to bother you in the first place. Good to know I can explicitly put you in the loop for other reports like this also. |
Okay, it seems libcrypt on RHEL / CentOS 7 takes any chars (non-printable as well as printable) as a valid salt for |
No problem about not asking me - I didn't mean to complain. Maybe it makes sense to deliberately reject |
It turned out, that some implementations of the md5crypt, sha256crypt, and sha512crypt hashing methods allow any UTF-8 character to be present in the salt string. This applies to the implementations of these hasing methods present in the libcrypt library supplied by glibc. Some server applications, which are certified to run on major enterprise Linux distributions, cPanel for instance, make use of this 'feature' when computing salts for their internal user passwords. For this particular reasons we decided to add a build-time configurable option to libxcrypt to make the md5crypt, sha256crypt, and sha512crypt hash methods accept salt strings, that have been crafted in such a way. The only not allowed exceptions for our permisseve implementation are the dollar sign ($) character, which seperates the sections of a Modular Crypt Format string, the colon (:) character, which seperates the sections of the Unix user table, the new-line (\n) character, and the CR (\r) character. Also adapt the badsalt test to know about this behaviour, when building libxcrypt with this configure option.
Looks great, thanks for your hard work! |
np, you're welcome! |
Did you check if all the other characters actually need to be accepted because something is really using them as salt or if _ is enough? Since ~everybody will enable this then maybe it's a good idea to be conservative and start from just allowing the characters which we know that are being used. |
It turned out, all other existing implementations of the md5crypt, sha256crypt, and sha512crypt hashing methods allow any printable ASCII character to be present in the salt string. We changed our implementation of these hash methods to be compliant with the general behaviour, except for the following character, which serve special purposes in the Unix shadow file: * '!' --> User account is locked. * '*' --> No initial password set for this user. * ':' --> Seperates the different columns.
It turned out, all other existing implementations of the md5crypt, sha256crypt, and sha512crypt hashing methods allow any printable ASCII character to be present in the salt string. We changed our implementation of these hash methods to be compliant with the general behaviour, except for the following characters, which serve special purposes in the Unix shadow file: * '!' --> User account is locked. * '*' --> No initial password set for this user. * ':' --> Seperates the different columns.
It turned out, all other existing implementations of the md5crypt, sha256crypt, and sha512crypt hashing methods allow any printable ASCII character to be present in the salt string. We changed our implementation of these hash methods to be compliant with the general behaviour, except for the colon ':' character, which serves a special purpose in the Unix shadow file. Also adapt test/badsalt to expect this difference in behaviour from the other hashing methods available in libxcrypt.
It turned out, all other existing implementations of the md5crypt, sha256crypt, and sha512crypt hashing methods allow any printable ASCII character to be present in the salt string. We changed our implementation of these hash methods to be compliant with the general behaviour, except for the colon ':' character, which serves a special purpose in the Unix shadow file. Also adapt test/badsalt to expect this difference in behaviour from the other hashing methods available in libxcrypt.
All other existing implementations of the md5crypt, sha256crypt, and sha512crypt hashing methods allow any ASCII character to be present in the salt string. We changed our implementation of these hash methods to be compliant with the general behaviour, except for the colon ':' character, which serves a special purpose in the Unix shadow file. Also adapt test/badsalt to expect this difference in behaviour from the other hashing methods available in libxcrypt. Fixes #105
All other existing implementations of the md5crypt, sha256crypt, and sha512crypt hashing methods allow any ASCII character to be present in the salt string. We changed our implementation of these hash methods to be compliant with the general behaviour, except for the colon ':' character, which serves a special purpose in the Unix shadow file. Also adapt test/badsalt to expect this difference in behaviour from the other hashing methods available in libxcrypt. Fixes #105
All other existing implementations of the md5crypt, sha256crypt, and sha512crypt hashing methods allow any ASCII character to be present in the salt string. We changed our implementation of these hash methods to be compliant with the general behaviour, except for the colon ':' character, which serves a special purpose in the Unix shadow file. Also adapt test/badsalt to expect this difference in behaviour from the other hashing methods available in libxcrypt. Fixes #105
All other existing implementations of the md5crypt, sha256crypt, and sha512crypt hashing methods allow any ASCII character to be present in the salt string. We changed our implementation of these hash methods to be compliant with the general behaviour, except for the colon ':' character, which serves a special purpose in the Unix shadow file. Also adapt test/badsalt to expect this difference in behaviour from the other hashing methods available in libxcrypt. Fixes #105
All other existing implementations of the md5crypt, sha256crypt, and sha512crypt hashing methods allow any ASCII character to be present in the salt string. We changed our implementation of these hash methods to be compliant with the general behaviour, except for the colon ':' character, which serves a special purpose in the Unix shadow file. Also adapt test/badsalt to expect this difference in behaviour from the other hashing methods available in libxcrypt. Fixes #105
All other existing implementations of the md5crypt, sha256crypt, and sha512crypt hashing methods allow any ASCII character to be present in the salt string. We changed our implementation of these hash methods to be compliant with the general behaviour, except for the colon ':' character, which serves a special purpose in the Unix shadow file. Also adapt test/badsalt.c to expect the colon characters is an invalid salt character for all hashing methods. Fixes #105
Pfew, this one lead to an erratic error during user authentication of our ldap users. The hashes were generated some time ago with another lib for >6.5k users (rehashing is no option). And some few of them have unsupported chars in their salt (+). |
Test patterns are now mostly generated rather than manually coded into a big table. Not reading past the end of the “setting” part of the string is tested more thoroughly (this would have caught the sunmd5 $$ bug if it had been available at the time). Test logs are tidier. This change exposed a lacuna in handling of text beyond the end of the setting: we were not rejecting e.g. ‘$3$__not_used_:c809a450df09a3’ because nthash ignores everything after ‘$3$’ and nothing else detects the colon. Now setting strings containing ‘:’ or a newline are rejected in generic code. (I would like to expand this to match the rule laid out in crypt(5), Hashed passphrases are always entirely printable ASCII, and do not contain any whitespace or the characters ‘:’, ‘;’, ‘*’, ‘!’, or ‘\’. (These characters are used as delimiters and special markers in the passwd(5) and shadow(5) files.) but I am worried that that will trip up people with stored passphrases with weird salt, like in <#105>, so I’m not doing it yet.
We have been bitten by the issue of user logins being rejected on CentOS 8.4 (and all other EL8 variants) as well as FC34, which was caused by the user's SHA512 password hash having one or more "+" characters in the salt. Such SHA512 hashes are generated by the university's central IT service on Windows systems, and such hashes ought to be valid according to libxcrypt's crypt(5) man-page:
One is lead to believe that almost any ASCII character is OK in the salt for SHA512 and other hashes, but that is unfortunately not true with the libxcrypt implementation in EL8. Quoting the comment above (June 26, 2020):
Question: Could you please change the crypt(5) man-page so it becomes obvious that only the characters [a-zA-Z0-9./] are permitted in salts? I note that there is in fact an official technical specification of this: The POSIX.1-2017 standard declares in
So it seems that we are on a solid standards-based ground. We only need the documentation to be consistent with the libxcrypt implementation. |
I have a trivial test code for the crypt() function on a CentOS 8.4 system:
When executing this code we get a core dump when non-standard characters are used in the salt:
CentOS 8.4 has a libxcrypt-4.1.1-4.el8.x86_64 RPM. Interestingly, the code does not core dump on CentOS 8 Stream:
with a slightly newer libxcrypt-4.1.1-6.el8.x86_64 RPM. |
@OleHolmNielsen First off, I'm pretty sure your test program segfaults because We have been allowing Let's move further discussion of what punctuation should be accepted in salts to #135. |
I confirm that crypt() returns NULL on CentOS 8.4. I added code to display the error:
The same binary works on CentOS 8 Stream:
So CentOS and RHEL 8.4 don't like "+" in the salt strings, whereas CentOS 8 Stream accepts "+" happily. |
However, our generic code currently only enforced the absence of ‘:’ and newline. Prior to 2112d2b, there was no generic check at all. Prior to b2b813a, some but not all hashing methods implemented much more restrictive rules (allowing only their own base64 alphabet in the salt part of a setting); these were found to conflict with existing salts (see #105). The setting-parsing code in each hashing method is ad hoc and difficult to audit for what syntax it actually accepts; there might be other quirks hiding. The change in this commit enforces salt strings to not contain characters that are present in the ‘badsalt_chars’ array. The ‘crypt_checksalt’ function now also validates the passed salt string for the absense of such invalid characters. For now we consider the following characters to be valid for any salt string in the generic check: 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z . / + - _ = , $ " # % & ' ( ) < > ? @ [ ] ^ ` { | } ~ Fixes #135.
However, our generic code currently only enforced the absence of ‘:’ and newline. Prior to 2112d2b, there was no generic check at all. Prior to b2b813a, some but not all hashing methods implemented much more restrictive rules (allowing only their own base64 alphabet in the salt part of a setting); these were found to conflict with existing salts (see #105). The setting-parsing code in each hashing method is ad hoc and difficult to audit for what syntax it actually accepts; there might be other quirks hiding. The change in this commit enforces salt strings to not contain characters that are present in the ‘badsalt_chars’ array. The ‘crypt_checksalt’ function now also validates the passed salt string for the absense of such invalid characters. For now we consider the following characters to be valid for any salt string in the generic check: 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z . / + - _ = , $ " # % & ' ( ) < > ? @ [ ] ^ ` { | } ~ Fixes #135.
However, our generic code currently only enforced the absence of ‘:’ and newline. Prior to 2112d2b, there was no generic check at all. Prior to b2b813a, some but not all hashing methods implemented much more restrictive rules (allowing only their own base64 alphabet in the salt part of a setting); these were found to conflict with existing salts (see #105). The setting-parsing code in each hashing method is ad hoc and difficult to audit for what syntax it actually accepts; there might be other quirks hiding. The change in this commit enforces salt strings to not contain characters that are present in the ‘badsalt_chars’ array. The ‘crypt_checksalt’ function now also validates the passed salt string for the absense of such invalid characters. For now we consider the following characters to be valid for any salt string in the generic check: 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z . / + - _ = , $ " # % & ' ( ) < > ? @ [ ] ^ ` { | } ~ Fixes #135.
However, our generic code currently only enforced the absence of ‘:’ and newline. Prior to 2112d2b, there was no generic check at all. Prior to b2b813a, some but not all hashing methods implemented much more restrictive rules (allowing only their own base64 alphabet in the salt part of a setting); these were found to conflict with existing salts (see #105). The setting-parsing code in each hashing method is ad hoc and difficult to audit for what syntax it actually accepts; there might be other quirks hiding. The change in this commit enforces salt strings to not contain characters that are present in the ‘badsalt_chars’ array. The ‘crypt_checksalt’ function now also validates the passed salt string for the absense of such invalid characters. For now we consider the following characters to be valid for any salt string in the generic check: 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z . / + - _ = , $ " # % & ' ( ) < > ? @ [ ] ^ ` { | } ~ Fixes #135.
However, our generic code currently only enforced the absence of ‘:’ and newline. Prior to 2112d2b, there was no generic check at all. Prior to b2b813a, some but not all hashing methods implemented much more restrictive rules (allowing only their own base64 alphabet in the salt part of a setting); these were found to conflict with existing salts (see #105). The setting-parsing code in each hashing method is ad hoc and difficult to audit for what syntax it actually accepts; there might be other quirks hiding. The change in this commit enforces salt strings to not contain characters that are present in the ‘badsalt_chars’ array. The ‘crypt_checksalt’ function now also validates the passed salt string for the absense of such invalid characters. For now we consider the following characters to be valid for any salt string in the generic check: 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z . / + - _ = , $ " # % & ' ( ) < > ? @ [ ] ^ ` { | } ~ Fixes #135.
However, our generic code currently only enforced the absence of ‘:’ and newline. Prior to 2112d2b, there was no generic check at all. Prior to b2b813a, some but not all hashing methods implemented much more restrictive rules (allowing only their own base64 alphabet in the salt part of a setting); these were found to conflict with existing salts (see #105). The setting-parsing code in each hashing method is ad hoc and difficult to audit for what syntax it actually accepts; there might be other quirks hiding. The change in this commit enforces salt strings to not contain characters that are present in the ‘badsalt_chars’ array. The ‘crypt_checksalt’ function now also validates the passed salt string for the absense of such invalid characters. For now we consider the following characters to be valid for any salt string in the generic check: 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z . / + - _ = , $ " # % & ' ( ) < > ? @ [ ] ^ ` { | } ~ Fixes #135.
However, our generic code currently only enforced the absence of ‘:’ and newline. Prior to 2112d2b, there was no generic check at all. Prior to b2b813a, some but not all hashing methods implemented much more restrictive rules (allowing only their own base64 alphabet in the salt part of a setting); these were found to conflict with existing salts (see #105). The setting-parsing code in each hashing method is ad hoc and difficult to audit for what syntax it actually accepts; there might be other quirks hiding. The change in this commit enforces salt strings to not contain characters that are present in the ‘badsalt_chars’ array. The ‘crypt_checksalt’ function now also validates the passed salt string for the absense of such invalid characters. For now we consider the following characters to be valid for any salt string in the generic check: 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z . / + - _ = , $ " # % & ' ( ) < > ? @ [ ] ^ ` { | } ~ Fixes #135.
However, our generic code currently only enforced the absence of ‘:’ and newline. Prior to 2112d2b, there was no generic check at all. Prior to b2b813a, some but not all hashing methods implemented much more restrictive rules (allowing only their own base64 alphabet in the salt part of a setting); these were found to conflict with existing salts (see #105). The setting-parsing code in each hashing method is ad hoc and difficult to audit for what syntax it actually accepts; there might be other quirks hiding. The change in this commit enforces salt strings to not contain characters that are present in the ‘badsalt_chars’ array. The ‘crypt_checksalt’ function now also validates the passed salt string for the absense of such invalid characters. For now we consider the following characters to be valid for any salt string in the generic check: 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z . / + - _ = , $ " # % & ' ( ) < > ? @ [ ] ^ ` { | } ~ Fixes #135.
However, our generic code currently only enforced the absence of ‘:’ and newline. Prior to 2112d2b, there was no generic check at all. Prior to b2b813a, some but not all hashing methods implemented much more restrictive rules (allowing only their own base64 alphabet in the salt part of a setting); these were found to conflict with existing salts (see #105). The setting-parsing code in each hashing method is ad hoc and difficult to audit for what syntax it actually accepts; there might be other quirks hiding. The change in this commit enforces salt strings to not contain characters that are present in the ‘badsalt_chars’ array. The ‘crypt_checksalt’ function now also validates the passed salt string for the absense of such invalid characters. For now we consider the following characters to be valid for any salt string in the generic check: 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z . / + - _ = , $ " # % & ' ( ) < > ? @ [ ] ^ ` { | } ~ Fixes #135.
So, on a glibc-2.17 based Centos 7 system the following happens:
But on ubuntu (libxcrypt 4.4.10-10ubuntu4):
and on centos 8 (libxcrypt-devel-4.1.1-4.el8), it produces an empty string and I think an 'invalid argument' message.
If this library is meant to be backwards-compatible it should presumably emulate the behaviour of the centos 7 algo? I don't know much about the salt, but I can't see that substituting the underscroe with something else is going to replicate it correctly?
The text was updated successfully, but these errors were encountered: