David Fifield <david@bamsoftware.com>
I initially reported this bug in email to squid-bugs@lists.squid-cache.org. The people there advised me to create a pull request, not mentioning any security issues, which I did as #491. After being merged, security update advisory SQUID-2019:11 was announced. The corresponding CVE is CVE-2019-18679. Later, I made pull request #549 to remove the possibility of recovering a pointer by reversing the hash.
I am looking at commit 50921dfc2aba018c57c522e7281069e9c2632e49.
authenticateDigestNonceNew
and authDigestNonceEncode
create nonces by
base64-encoding the raw bytes of a _digest_nonce_data
struct. The struct
contains a pointer to itself, which could be useful as part of an
exploit chain to help bypass ASLR, for example. A comment in the code
says "our nonce looks like base64(H(timestamp,pointertohash,randomdata))"
(https://github.com/squid-cache/squid/blob/50921dfc2aba018c57c522e7281069e9c2632e49/src/auth/digest/Config.cc#L155),
but in fact there is no hashing step and the bytes are returned to the
client directly; it's actually
base64(timestamp||padding||pointertohash||randomdata||padding).
As a demonstration, make a squid.conf file containing the following:
Run squid as:
Grab a nonce:
This particular nonce decodes to hexadecimal 14c5475d00000000906265836d550000e557da5000000000. The attached pull-nonces script grabs nonces and recovers the original structure, including the padding between fields:
Besides disclosing pointers, revealing randomdata probably makes it
possible to recover the internal state of the random number generator,
because it is only seeded with a 32-bit timestamp.
https://github.com/squid-cache/squid/blob/50921dfc2aba018c57c522e7281069e9c2632e49/src/auth/digest/Config.cc#L162
It looks like authDigestNonceEncode
should hash its input string before
base64 encoding. It would also be a good idea to include only the fields
of _digest_nonce_data
, not the raw bytes of the struct, which could
possibly contain unrelated heap data in padding.
Here is a draft patch to hash the noncedata before encoding it and returning it to the client.
There's also a second patch to use hex encoding in place of base64 encoding. Note that the hex-encoded hashed nonces are the same length as the base64-encoded unhashed nonces.
I will suggest an alternate implementation that would require some
refactoring but would be simpler and more secure: just use completely
random nonces. I.e., hex-encode 16 bytes from /dev/urandom and make that
the nonce. The reason RFC 2617 includes a timestamp, and hashes an ETag
and a private random value, is to enable an implementation that doesn't
require storing all valid nonces in memory. But squid already remembers
all valid nonces in digest_nonce_cache
, so there's no reason for the
nonce key to contain any meaningful information--all you need is for
nonces not to repeat, and a random nonce gives you that. Even a hashed
nonce doesn't prevent recovering a preimage--it only takes an offline
attack over the bits of entropy in the creationtime
(few), randomdata
(few), and self
pointer (depends on ASLR). Random nonces would prevent
that attack.