A useful reference is Niels Ferguson's The Windows 10
random number generation infrastructure, October 2019, despite that
it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information presented after the date of publication.
That tells CryptGenRandom
use ProcessPrng function to produce random bytes
and about ProcessPrng:
This is the primary interface to the user-mode per-processor PRNGs. It is implemented in BCryptPrimitives.dll.
and
All PRNGs in the system are SP800-90 AES_CTR_DRBG with 256-bit security strength using the df() function for seeding and re-seeding (see SP 800-90 for details). (…) The Basic PRNGs are not used directly, but rather through a wrapping layer that adds several features.
- A small buffer of random bytes to improve performance for small requests.
- A lock to support multi-threading.
- A seed version.
(…) The buffering is straightforward. There is a small buffer (currently 128 bytes). If a request for random bytes is 128 bytes or larger, it is generated directly from AES_CTR_DRGB. If it is smaller than 128 bytes it is taken from the buffer. The buffer is re-filled from the AES_CTR_DRBG whenever it runs empty. So, if the buffer contains 4 bytes and the request is for 8 bytes, the 4 bytes are taken from the buffer, the buffer is refilled with 128 bytes, and the first 4 bytes of the refilled buffer are used to complete the request, leaving 124 bytes in the buffer.
If my understanding is correct that this buffer is in user space, and nothing prevents the process using CryptGenRandom to access it, then the program that uses CryptGenRandom to generate 4 bytes can be written so that it "predicts" the 124 bytes that it will later obtain from CryptGenRandom, merely by peeking at these bytes in memory. A debugger would allow the same.
Further, if my understanding of other parts of the document is correct, administrative privileges or breaching some other barrier (e.g. by exploiting a kernel-trusted device driver or some data leak enabled by a CPU cache/branch predictor) would allow to predict more (but I believe not indefinitely many) bytes that have not been generated yet, and also would allow other processes to predict what CryptGenRandom will supply in a process they spy.