mailing list archives
Re: CVE request: crypt_blowfish 8-bit character mishandling
From: Solar Designer <solar () openwall com>
Date: Fri, 8 Jul 2011 03:13:31 +0400
On Thu, Jul 07, 2011 at 01:33:18AM +0400, Solar Designer wrote:
On Tue, Jun 28, 2011 at 02:05:35PM +0200, Michael Matz wrote:
If so, treating passwords containing 0xff special seems sensible.
I gave some thought to what action to take on a would-be collision.
Simply failing the password hashing operation didn't sound great - it'd
be prone to side-channel leaks of this property of the attempted
password, and users wouldn't be able to set those weird passwords if the
system happens to use $2a$ instead of the now more correct $2y$ for new
hashes. This would in turn reveal properties of system setup to a
non-root user. So I chose to alter the hashing method in those cases
such that the collision is avoided and no other collision may be easily
found (without breaking Blowfish).
Zefram, the maintainer of Crypt::Eksblowfish, CC'ed on this message,
expressed his disapproval for the above (via private e-mail to me).
More specifically, Zefram approves of the new prefixes $2x$ and $2y$,
but not of the slight deliberate deviation from OpenBSD's algorithm for
the $2a$ prefix.
Zefram's proposed alternative is "to reject certain passwords without
giving much away through side channels". This was the alternative I had
been considering too. However, my concern is not only information leaks
via timings, etc., but also more direct leaks via the password hashes
themselves - if they're ever leaked or stolen. Another concern is
program behavior on "rejection of certain passwords" by our library
Zefram suggested a way to implement such rejection. It's similar to
what I had been considering - do the hashing either way, but then if we
want to reject a password alter the output string such that it can't
possibly match the setting string. There are more details to this, such
as to deal with more subtle timing leaks, but that's the gist of it.
(My current code does something similar on runtime self-test failure,
although it also indicates the error explicitly to callers who are good
enough to check the return value.)
Unfortunately, those strings would likely be trivial to identify in a
shadow file, etc., thereby revealing that those specific users tried to
set the affected passwords. If the users reuse such passwords
elsewhere, this may give an attacker a way to run more focused attacks
on other/external accounts of those specific users (more focused in
terms of both which users to attack and what passwords to try).
Another scenario is that the rejection is somehow recognized by programs
setting passwords and/or doing authentication (maybe not by all, but by
some of them). In that case we have those extra side-channel leaks
again, and we're revealing this detail of the system setup to a non-admin.
Let's suppose we could produce strings indistinguishable from normal
hashes, but practically uncrackable (unless a real collision is hit).
However, in order for us to fully reject risky passwords rather than
just hash them differently (which I had implemented and which Zefram
objects to), such strings, when re-used as setting strings and passed
into crypt(3), would need to produce different strings. This
contradicts that they're indistinguishable from normal. OK, we can
relax this requirement to "indistinguishable from normal until the
correct password is tested". Perhaps we could come up with an algorithm
that would achieve it, but another issue is that it means crypt(3) will
have to alter the setting string, which is not indistinguishable from
its normal behavior - so we're back to the scenario discussed above for
some apps. (For example, as we've just learned, mkpasswd would notice
this difference in behavior.)
Thus, it still feels like my approach with slight deviation from correct
hashing for $2a$ might be the least evil and have the least potential
for nasty surprises.
If it were the very first deviation from the correct algorithm for $2a$,
I would likely refrain from it (and there would be no need for it). But
since things were/are already far worse, this seems acceptable to me.
It affects a very small fraction of passwords, and only weird ones,
which are in practice unlikely to be seen outside of an attack.
On the other hand, this is also a reason why we might find side-channel
leaks acceptable - and go for outright rejection of such passwords, with
explicit error indication by our library code. Unfortunately, not all
programs check for errors, and in fact in order to avoid surprises to
programs that don't expect a NULL return from crypt(3) (which is allowed
formally, but is uncommon and generally not expected in practice) the
crypt_blowfish wrapper code includes translation of NULL returns from
its underlying functions to "*0" / "*1" magic strings. So with many
systems/programs we'd get those magic strings in shadow files and the
like, which would result in the focused attacks problem I mentioned above.
And if we remove this wrapper code, we'd introduce a way to segfault
I'd appreciate any further comments.
P.S. My expectation is that we'd move to something substantially more
suitable than both bcrypt and SHA-crypt in a few years from now.
Keywords: scrypt concepts, inherent parallelism (attackers have lots of
it anyway, but authentication servers need more of it), GPUs (fast
SHA-crypt cracking later this year), AVX2 VSIB (faster bcrypt cracking
in a couple of years from now).