Inspiration
In 2013, Taylor Hornby demonstrated that a hypervisor controlling RDRAND could make a Linux guest's randomness predictable. The kernel was patched to prevent a single source from dominating the entropy pool. We wanted to explore what happens when a hypervisor manipulates RDRAND, virtio-rng, and the guest's sense of time simultaneously. This combination had not been properly revisited against a modern kernel, so we saw an opportunity to reproduce the attack and build a defense against it.
What it does
Linux relies on /dev/urandom for unpredictable data used in SSH host keys, TLS session keys, password salts, and nonces. We built a 32-line QEMU patch that makes RDRAND deterministic, feeds virtio-rng from a fixed file, and uses icount mode to control the guest's sense of time. With all three compromised, we can predict the guest's exact SSH host key before boot completes.
We then built a Rust kernel module to defend against this attack. It uses Bayesian belief updating to evaluate entropy sources across multiple rounds, rejects sources that remain above a 40% suspicion threshold, and distributes trust based on how expensive each source is to fake. It also injects a trusted seed through a channel outside the attacker's control. We verified the defense by repeating the same attack with the module loaded, where the predicted key never matched the actual one.
How I built it
The QEMU patch modifies TCG's software RDRAND emulation to return a deterministic splitmix64 stream based on an attacker-controlled seed. The kernel module is written in Rust and built against a mainline 6.18 kernel using Rust-for-Linux.
To make sure the vulnerability wasn't specific to one environment, we tested it using real Alpine, Arch, and Debian userspaces. We also used QEMU's icount record/replay functionality to reproduce identical inputs and verify that the same attack produces the same key. Every round of the module's health checks is logged directly to the kernel console, making its decisions visible in real time. The 40% safety threshold is also checked at compile time using the module's own belief-update logic against the worst-case scenario.
Challenges I ran into
My first scoring model actually ended up trusting RDRAND after a handful of successful rounds, which was the exact failure we wanted to prevent. I only caught it by watching a live boot rather than relying on the math alone. Plain QEMU icount also turned out not to be deterministic across separate launches, so I had to switch to recording and replaying attacker-controlled traces.
I also spent time building and calibrating a timing-based jitter test before deciding its results weren't reliable enough to include. Even the compile-time safety proof had a bug in its first version, with a reversed comparison causing the build to fail exactly as intended. Each of these issues reinforced why real testing matters more than assuming something works because it looks correct on paper.
Accomplishments that I'm proud of
The biggest accomplishment was successfully defeating the attack we built. The predicted SSH host key never matched the real one across repeated, independently seeded boots. We also made the safety threshold a compile-time proof rather than something users simply have to trust. Other achievements include real-time kernel logging, reproducing the vulnerability across three Linux distributions, and catching issues through actual testing rather than relying entirely on code review.
What I learned
I learned how Bayesian belief updating can be applied to real security problems and how the cost of compromising an entropy source can influence how much trust it deserves. I also explored how a safety threshold and proportional trust allocation can work together without relying on a simple all-or-nothing policy.
On the implementation side, I gained experience writing a real Rust kernel module with Rust-for-Linux, including working across low-level FFI boundaries. Most importantly, I learned the limitations of detecting compromised randomness from inside a virtual machine. No amount of in-guest testing can fully verify a channel controlled by the hypervisor, which is why the defense relies on cost-based distrust rather than assuming better detection alone can solve the problem.
Built With
- busybox
- linuxkernel
- qemu
- rust