[Bug 568803] Vulnerability in TinyDTLS
Bugzilla Link | 568803 |
Status | NEW |
Importance | P3 normal |
Reported | Nov 13, 2020 14:49 EDT |
Modified | Jul 06, 2021 17:59 EDT |
Description
From the Security Team inbox (abridged):
The original poster included attachments which we have not opened. I will check to see if we can get the original poster to join the conversation here.
--
Tinydtls uses the default pseudo random number generator for the
affected systems.
I will explain the bug based on POSIX. TinyDTLS uses '/dev/urandom' as a
source of entropy to seed a PRNG using srand(entropy). I assume, this is
done in response to a complaint about unsafe cookies on the tinydtls
mailing list[1]. Afterwards it uses the libc's rand() function to obtain
pseudo random bits in the function 'dtls_prng':
int dtls_prng(unsigned char *buf, size_t len) {
while (len--)
*buf++ = rand() & 0xFF;
return 1;
}
It is not sufficient to seed a PRNG with entropy. In all major C
standard libraries, the rand() function outputs states of a simple PRNG,
such as a linear congruence generator.
As the name says, all states are in a linear relationship. Which is why
one state (output) is usually enough to compute all future outputs (and
preceding ones). Even if the output is only partially available (as in
the function 'dtls_prng'), few outputted states are enough to recover
the internal PRNG state.
The direct relationship between outputs is a property all major PRNGs
have (such as the mersenne twister or LSFRs).
Also, the default internal state size of PRNGs in most standard
libraries (e.g. glibc or dietlibc) is only around 32 bits.
Cryptographically secure pseudo-random number generator (CSPRNG) do not
have this linear property, an output does not give hints about the
internal state.
However, such a CSPRNG is not used in tinydtls.
My exploit works as follows:
-
Observe a DTLS handshake. Included (and publicly visible) is the value
server_random, which functions as a nonce. -
Since the server_random value gets its entropy from 28 calls to the
'dtls_prng' function, its bytes correspond to outputs of the PRNG. -
The secret scalar used for the elliptic curve computation is then
obtained by calling 'dtls_prng' 32 times. Note that obtaining this
secret scalar on one side of the connection is enough to compromise the
entire communication, since all derived symmetric keys are based on it,
see [2]. -
As an attacker: Recover the internal state of the PRNG using the
publicly known server_random value. -
Then the attacker just sets the state of his PRNG to this recovered
internal state -
By calling rand() 32 times on his PRNG, the attacker obtains the
secret scalar (key) used by the server
The same attack works equivalently for the client side. Meaning that it
is enough if one side of the connection is using tinydtls.
Attached is the exploit code with a README file, explaining how to use
it. There is also a network dump included, against which the attack can
be tested.
Please note that my exploit just brute forces through all possible PRNG
states on a current glibc. This takes about 0.014 seconds on my CPU for
the current master branch.
HOWEVER, making the seed value (initial state of the PRNG) wider, as it
is in the develop branch does not fix the problem! The state still leaks
out through the server_random/client_random.
The only solution would be to either include a CSPRNG in tinydtls or
replace dtls_prng with always reading bytes out of '/dev/urandom' (if
available).
I think this flaw calls for a CVE to inform possible users.
I found out about this bug because a coworker uses tinydtls in an
embedded project.