The general attack scenario:
In 2013 Django had a general vulnerability as an attacker could create extremely intense CPU calculations via very large pa
The password is limited to 72 characters internally in the crypt algorithm.
To see why, let's look at crypt()
's source: ext/standard/crypt.c
} else if (
salt[0] == '$' &&
salt[1] == '2' &&
salt[3] == '$') {
char output[PHP_MAX_SALT_LEN + 1];
memset(output, 0, PHP_MAX_SALT_LEN + 1);
crypt_res = php_crypt_blowfish_rn(password, salt, output, sizeof(output));
if (!crypt_res) {
ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
return NULL;
} else {
result = zend_string_init(output, strlen(output), 0);
ZEND_SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
return result;
}
The password
field is a simple char*
field. So there's no length information. All that's passed is a normal pointer.
So if we follow that through, we'll eventually land at BF_set_key.
The important part is the loop:
for (i = 0; i < BF_N + 2; i++) {
tmp[0] = tmp[1] = 0;
for (j = 0; j < 4; j++) {
tmp[0] <<= 8;
tmp[0] |= (unsigned char)*ptr; /* correct */
tmp[1] <<= 8;
tmp[1] |= (BF_word_signed)(signed char)*ptr; /* bug */
if (j)
sign |= tmp[1] & 0x80;
if (!*ptr)
ptr = key;
else
ptr++;
}
diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */
expanded[i] = tmp[bug];
initial[i] = BF_init_state.P[i] ^ tmp[bug];
}
BF_N
is defined to be 16. So the outer loop will loop 18 times (BF_N + 2
).
The inner loop will loop 4 times. 4 * 18 == 72.
And there you have it, only 72 characters of the key will be read. No more.
Now, there's an interesting side-effect to that algorithm. Because it uses C-Strings (strings terminated by a \0
null byte), it's impossible for it to use anything past \0
. So a password that contains a null-byte will lose any entropy past it. Example: http://3v4l.org/Y6onV