[9.20] [CVE-2025-40780] sec: usr: Cache-poisoning due to weak pseudo-random number generator

It was discovered during research for an upcoming academic paper that a
xoshiro128\*\* internal state can be recovered by an external 3rd party,
allowing the prediction of UDP ports and DNS IDs in outgoing queries.
This could lead to an attacker spoofing the DNS answers with great
efficiency and poisoning the DNS cache.

The internal random generator has been changed to a cryptographically
secure pseudo-random generator.

ISC would like to thank Prof. Amit Klein and Omer Ben Simhon from Hebrew
University of Jerusalem for bringing this vulnerability to our
attention.

Backport of !831

Closes isc-projects/bind9#5484

Merge branch '5484-security-make-isc_random-csprng-9.20' into 'v9.20.14-release'

See merge request isc-private/bind9!845
This commit is contained in:
Michał Kępień 2025-10-02 14:26:38 +02:00
commit 34af35c2df
11 changed files with 66 additions and 204 deletions

View file

@ -653,7 +653,7 @@ AS_IF([test "$enable_pthread_rwlock" = "yes"],
])
AM_CONDITIONAL([USE_ISC_RWLOCK], [test "$enable_pthread_rwlock" != "yes"])
CRYPTO=OpenSSL
AC_CHECK_FUNCS_ONCE([arc4random])
#
# OpenSSL/LibreSSL is mandatory

View file

@ -21,7 +21,6 @@ libisc_la_HEADERS = \
include/isc/dir.h \
include/isc/dnsstream.h \
include/isc/endian.h \
include/isc/entropy.h \
include/isc/errno.h \
include/isc/error.h \
include/isc/file.h \
@ -127,7 +126,6 @@ libisc_la_SOURCES = \
counter.c \
crc64.c \
dir.c \
entropy.c \
errno.c \
errno2result.c \
errno2result.h \
@ -165,7 +163,6 @@ libisc_la_SOURCES = \
net.c \
netaddr.c \
netscope.c \
nonce.c \
openssl_shim.c \
openssl_shim.h \
os.c \

View file

@ -1,24 +0,0 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <isc/entropy.h>
#include <isc/types.h>
#include <isc/util.h>
#include <isc/uv.h>
void
isc_entropy_get(void *buf, size_t buflen) {
int r = uv_random(NULL, NULL, buf, buflen, 0, NULL);
UV_RUNTIME_CHECK(uv_random, r);
}

View file

@ -16,7 +16,6 @@
#include <stddef.h>
#include <isc/ascii.h>
#include <isc/entropy.h>
#include <isc/hash.h> /* IWYU pragma: keep */
#include <isc/random.h>
#include <isc/result.h>
@ -35,7 +34,7 @@ isc__hash_initialize(void) {
*/
uint8_t key[16] = { 1 };
#if !FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
isc_entropy_get(key, sizeof(key));
isc_random_buf(key, sizeof(key));
#endif /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
STATIC_ASSERT(sizeof(key) >= sizeof(isc_hash_key),
"sizeof(key) < sizeof(isc_hash_key)");

View file

@ -32,7 +32,6 @@
#include <isc/ascii.h>
#include <isc/atomic.h>
#include <isc/entropy.h>
#include <isc/hash.h>
#include <isc/hashmap.h>
#include <isc/magic.h>

View file

@ -1,35 +0,0 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#pragma once
#include <stdlib.h>
#include <isc/lang.h>
/*! \file isc/entropy.h
* \brief Implements wrapper around CSPRNG cryptographic library calls
* for getting cryptographically secure pseudo-random numbers.
*
* Uses synchronous version of uv_random().
*/
ISC_LANG_BEGINDECLS
void
isc_entropy_get(void *buf, size_t buflen);
/*!<
* \brief Get cryptographically-secure pseudo-random data.
*/
ISC_LANG_ENDDECLS

View file

@ -16,6 +16,7 @@
#include <stdlib.h>
#include <isc/lang.h>
#include <isc/random.h>
/*! \file isc/nonce.h
* \brief Provides a function for generating an arbitrarily long nonce.
@ -23,8 +24,10 @@
ISC_LANG_BEGINDECLS
void
isc_nonce_buf(void *buf, size_t buflen);
static inline void
isc_nonce_buf(void *buf, size_t buflen) {
isc_random_buf(buf, buflen);
}
/*!<
* Fill 'buf', up to 'buflen' bytes, with random data from the
* crypto provider's random function.

View file

@ -20,25 +20,18 @@
#include <isc/types.h>
/*! \file isc/random.h
* \brief Implements wrapper around a non-cryptographically secure
* \brief Implements wrapper around a cryptographically secure
* pseudo-random number generator.
*
*/
ISC_LANG_BEGINDECLS
uint8_t
isc_random8(void);
/*!<
* \brief Returns a single 8-bit random value.
*/
uint16_t
isc_random16(void);
/*!<
* \brief Returns a single 16-bit random value.
*/
#if HAVE_ARC4RANDOM && !defined(__linux__)
#define isc_random32() arc4random()
#define isc_random_buf(buf, buflen) arc4random_buf(buf, buflen)
#define isc_random_uniform(upper_bound) arc4random_uniform(upper_bound)
#else /* HAVE_ARC4RANDOM && !defined(__linux__) */
uint32_t
isc_random32(void);
/*!<
@ -68,4 +61,21 @@ isc_random_uniform(uint32_t upper_bound);
* when upper_bound is UINT32_MAX/2.
*/
#endif /* HAVE_ARC4RANDOM && !defined(__linux__) */
static inline uint8_t
isc_random8(void) {
return (uint8_t)isc_random32();
}
/*!<
* \brief Returns a single 8-bit random value.
*/
static inline uint16_t
isc_random16(void) {
return (uint16_t)isc_random32();
}
/*!<
* \brief Returns a single 16-bit random value.
*/
ISC_LANG_ENDDECLS

View file

@ -1,20 +0,0 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <isc/entropy.h>
#include <isc/nonce.h>
void
isc_nonce_buf(void *buf, size_t buflen) {
isc_entropy_get(buf, buflen);
}

View file

@ -30,126 +30,55 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#if !HAVE_ARC4RANDOM || defined(__linux__)
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <isc/entropy.h>
#include <isc/os.h>
#include <isc/random.h>
#include <isc/result.h>
#include <isc/thread.h>
#include <isc/types.h>
#include <isc/util.h>
#include <isc/uv.h>
/*
* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
*
* To the extent possible under law, the author has dedicated all
* copyright and related and neighboring rights to this software to the
* public domain worldwide. This software is distributed without any
* warranty.
*
* See <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#define ISC_RANDOM_BUFSIZE (ISC_OS_CACHELINE_SIZE / sizeof(uint32_t))
/*
* This is xoshiro128** 1.0, our 32-bit all-purpose, rock-solid generator.
* It has excellent (sub-ns) speed, a state size (128 bits) that is large
* enough for mild parallelism, and it passes all tests we are aware of.
*
* The state must be seeded so that it is not everywhere zero.
*/
static thread_local bool initialized = false;
static thread_local uint32_t seed[4] = { 0 };
static uint32_t
rotl(const uint32_t x, int k) {
return (x << k) | (x >> (32 - k));
}
static uint32_t
next(void) {
uint32_t result_starstar, t;
result_starstar = rotl(seed[0] * 5, 7) * 9;
t = seed[1] << 9;
seed[2] ^= seed[0];
seed[3] ^= seed[1];
seed[1] ^= seed[2];
seed[0] ^= seed[3];
seed[2] ^= t;
seed[3] = rotl(seed[3], 11);
return result_starstar;
}
static void
isc__random_initialize(void) {
if (initialized) {
return;
}
#if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
/*
* A fixed seed helps with problem reproduction when fuzzing. It must be
* non-zero else xoshiro128starstar will generate only zeroes, and the
* first result needs to be non-zero as expected by random_test.c
*/
seed[0] = 1;
#endif /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
while (seed[0] == 0 && seed[1] == 0 && seed[2] == 0 && seed[3] == 0) {
isc_entropy_get(seed, sizeof(seed));
}
initialized = true;
}
uint8_t
isc_random8(void) {
isc__random_initialize();
return (uint8_t)next();
}
uint16_t
isc_random16(void) {
isc__random_initialize();
return (uint16_t)next();
}
thread_local static uint32_t isc__random_pool[ISC_RANDOM_BUFSIZE];
thread_local static size_t isc__random_pos = ISC_RANDOM_BUFSIZE;
uint32_t
isc_random32(void) {
isc__random_initialize();
return next();
#if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
/*
* A fixed stream of numbers helps with problem reproduction when
* fuzzing. The first result needs to be non-zero as expected by
* random_test.c (it starts with ISC_RANDOM_BUFSIZE, see above).
*/
return (uint32_t)(isc__random_pos++);
#endif /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
if (isc__random_pos == ISC_RANDOM_BUFSIZE) {
isc_random_buf(isc__random_pool, sizeof(isc__random_pool));
isc__random_pos = 0;
}
return isc__random_pool[isc__random_pos++];
}
void
isc_random_buf(void *buf, size_t buflen) {
REQUIRE(buf != NULL);
REQUIRE(buflen > 0);
REQUIRE(buflen == 0 || buf != NULL);
int i;
uint32_t r;
isc__random_initialize();
for (i = 0; i + sizeof(r) <= buflen; i += sizeof(r)) {
r = next();
memmove((uint8_t *)buf + i, &r, sizeof(r));
if (buf == NULL || buflen == 0) {
return;
}
r = next();
memmove((uint8_t *)buf + i, &r, buflen % sizeof(r));
return;
int r = uv_random(NULL, NULL, buf, buflen, 0, NULL);
UV_RUNTIME_CHECK(uv_random, r);
}
uint32_t
isc_random_uniform(uint32_t limit) {
isc__random_initialize();
/*
* Daniel Lemire's nearly-divisionless unbiased bounded random numbers.
*
@ -161,7 +90,7 @@ isc_random_uniform(uint32_t limit) {
* integer part (upper 32 bits), and we will use the fraction part
* (lower 32 bits) to determine whether or not we need to resample.
*/
uint64_t num = (uint64_t)next() * (uint64_t)limit;
uint64_t num = (uint64_t)isc_random32() * (uint64_t)limit;
/*
* In the fast path, we avoid doing a division in most cases by
* comparing the fraction part of `num` with the limit, which is
@ -213,7 +142,7 @@ isc_random_uniform(uint32_t limit) {
* our valid range, it is superfluous, and we resample.
*/
while ((uint32_t)(num) < residue) {
num = (uint64_t)next() * (uint64_t)limit;
num = (uint64_t)isc_random32() * (uint64_t)limit;
}
}
/*
@ -221,3 +150,5 @@ isc_random_uniform(uint32_t limit) {
*/
return (uint32_t)(num >> 32);
}
#endif /* HAVE_ARC4RANDOM && !defined(__linux__) */

View file

@ -320,7 +320,9 @@ random_test(pvalue_func_t *func, isc_random_func test_func) {
}
break;
case ISC_RANDOM_BYTES:
isc_random_buf(values, sizeof(values));
for (i = 0; i < ARRAY_SIZE(values); i++) {
values[i] = isc_random32();
}
break;
case ISC_RANDOM_UNIFORM:
uniform_values = (uint16_t *)values;