A couple of weeks ago I accidentally installed Civ 6 again, which reduced the time I have had available for other non-essential activity. Fortunately, I have just about had enough Civ for this round, which means that I should shortly have more time for things like RustRAT again. Progress has not halted to a complete stop, however, and rustrat-server is now more or less ready to receive incoming connections over HTTP. The server is not very usable at the moment, and before I have something even remotely ready for testing, basic logging and some interface to actually interact with the rats is needed. With that said, this post will not be about the server, but rather the rat. More specifically my plans to seed the rat’s PRNG for key generation.
There are two recommended ways I have looked at to do this, either with Rust’s getrandom crate, or the WIN32 API function BCryptGenRandom (which is was getrandom uses on Windows). As you might have guessed, I have chosen to not use any of these excellent options. I do not have any good reasons for this, but in general I want to try to avoid API calls whenever I can. And besides, who knows, it might be a fun exercise to see what I can do to try to cobble up a good (enough) random seed.
The random seed will be used by rats to generate keys for key exchanges and therefore it should be reasonably secure. While it may sound silly, I am not too concerned about the quality of the keys. HTTPS will be the main form of communication, and the additional encryption layer is more to prevent anyone intercepting the traffic to (easily) read and edit it. In the future I might support various ways to seed the PRNG (including BCryptGenRandom) to help make sure that RustRAT can be customized for different needs.
Perfect security might not be a concern right now, but I still want to avoid having too many key collisions where rats generate key pairs that are already in use. The plan is therefore to dig up entropy where I can find it, possibly hash it to remove biases or something like that, and then feed it to a PRNG. I have not conducted any actual research on good entropy sources, but rather resorted to random google searches and wild speculation, with the only requirement that I want to keep things simple-ish.
Entropy sources
RDRAND
RDRAND is a x86 instruction that gives you more or less random data. There are two potential issues with RDRAND, the first being that not all CPUs might support RDRAND. This can be worked around by using the CPUID instruction to check whether the appropriate flag is set before attempting to call RDRAND to avoid programs crashing due to illegal instructions on CPUs that do not have the instruction. In addition, there have been several flaws in RDRAND implementations that have caused it to give bad (or no) entropy, but I am not too concerned about that since it will not be the only source of entropy.
RDTSC
From something that is intended to be used for random numbers, to something that is not, RDTSC is an instruction that returns the number of CPU cycles since its last reset. While this is a simple counter and should probably never be used for serious cryptography, it will hopefully be enough for some more unpredictability in the random keys generated and help decrease the chance of duplicate keys.
Pointers (functions, heap)
As you probably can tell, I started with the entropy source I had the most belief in, and the amount of entropy in my suggestions is rapidly declining. The idea behind this is that RustRAT will be compiled with support for ASLR, which should somewhat randomize the address of functions and items on the heap. To be honest, I have no idea of how good this will be and will probably have to look at it some more, but it seems to me to be something that should change every time the rat is executed and result in numbers that are not the same every time. As a bonus, this is probably the simplest to implement, as it simply can be done by getting the address of a function or an element on the heap.
The Win32 Thread Information Block
The TIB is a data structure Windows uses to store information about processes and threads. This is one of those things that I have read about, but never actually gotten around to playing with, which is why I have included it. The upside to this is that it is also easy to read from the TIB using the GS segment register (or FS if you are in a 32-bit context). The downside is that I do not know what most of the values stored in the TIB are, or how suitable they are for random seed. One “obvious” answer is the process and thread id which can be read directly from the TIB, which to me seems like worse versions of RDTSC. I guess one option could be to hash parts of, or the entire TIB.
Anything else?
This is about as far as I have gotten in my brainstorming, and will be what I will implement for now. There are some additional things that I though of, such as using the environment variables as well, but for some reason I have chosen not to include that. If you have any questions or comments, I would love to hear them, see the about page for contact details.
I am looking forward to implementing this. The implementation will require me to do some unsafe, inline assembly, in Rust, which is not something I have attempted yet. And who knows, maybe I will revisit this post later and do some actual measurements of how much entropy I manage to cough up.