Hi, The bcrypt hash in htpasswd files can be abused for denial of service, for a background see [1].
I had discussed this in a couple of mails with the apache security team, but they asked me to move further discussions here on the public mailing list. (I don't want to further discuss whether or not this is a security vuln, I think we won't come to an agreement here.) Anyway: In apr there was a commit to limit the running time of bcrypt to 17 (which is a couple of seconds on a modern system and much saner than the previous maximum of 31): https://svn.apache.org/viewvc?view=revision&revision=1772803 However that's only half of it. This limits the creation of hashes, but not the verification. This was patched in a wrong way here (sorry for that, patch came from me): https://svn.apache.org/viewvc?view=revision&revision=1773929 and subsequently reverted here: https://svn.apache.org/viewvc?view=revision&revision=1774976 A check in BF_crypt needs to happen, but a few lines above (one could argue that this if clause itself is very ugly and should be replaced with some atoi-stuff, but I've not done that for now): --- crypto/crypt_blowfish.c (revision 1778111) +++ crypto/crypt_blowfish.c (working copy) @@ -675,9 +675,9 @@ setting[2] < 'a' || setting[2] > 'z' || !flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a'] || setting[3] != '$' || - setting[4] < '0' || setting[4] > '3' || + setting[4] < '0' || setting[4] > '1' || setting[5] < '0' || setting[5] > '9' || - (setting[4] == '3' && setting[5] > '1') || + (setting[4] == '1' && setting[5] > '7') || setting[6] != '$') { __set_errno(EINVAL); return NULL; Attached a patch for apr trunk with this change and a patch for the 1.5 branch with both changes. Please apply. [1] https://blog.fuzzing-project.org/56-htpasswDoS-Local-Denial-of-Service-via-Apache-httpd-password-hashes.html -- Hanno Böck https://hboeck.de/ mail/jabber: ha...@hboeck.de GPG: FE73757FA60E4E21B937579FA5880072BBB51E42
Index: crypto/crypt_blowfish.c =================================================================== --- crypto/crypt_blowfish.c (revision 1778111) +++ crypto/crypt_blowfish.c (working copy) @@ -675,9 +675,9 @@ setting[2] < 'a' || setting[2] > 'z' || !flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a'] || setting[3] != '$' || - setting[4] < '0' || setting[4] > '3' || + setting[4] < '0' || setting[4] > '1' || setting[5] < '0' || setting[5] > '9' || - (setting[4] == '3' && setting[5] > '1') || + (setting[4] == '1' && setting[5] > '7') || setting[6] != '$') { __set_errno(EINVAL); return NULL;
--- a/crypto/crypt_blowfish.c 2012-07-06 13:41:24.000000000 +0200 +++ apr-util-1.5.4/crypto/crypt_blowfish.c 2017-01-10 12:05:56.449895464 +0100 @@ -675,9 +675,9 @@ setting[2] < 'a' || setting[2] > 'z' || !flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a'] || setting[3] != '$' || - setting[4] < '0' || setting[4] > '3' || + setting[4] < '0' || setting[4] > '1' || setting[5] < '0' || setting[5] > '9' || - (setting[4] == '3' && setting[5] > '1') || + (setting[4] == '1' && setting[5] > '7') || setting[6] != '$') { __set_errno(EINVAL); return NULL; @@ -877,7 +877,7 @@ const char *input, int size, char *output, int output_size) { if (size < 16 || output_size < 7 + 22 + 1 || - (count && (count < 4 || count > 31)) || + (count && (count < 4 || count > 17)) || prefix[0] != '$' || prefix[1] != '2' || (prefix[2] != 'a' && prefix[2] != 'y')) { if (output_size > 0) output[0] = '\0';