Project on Heap Canaries (or something like them, based on randstruct) for Linux (original) (raw)
Hello LLVM community,
This is an odd topic, but I wanted to “test the water” and get some ideas of how to best go about the subject, as well as better understand some of the challenges involved.
Many may be familiar with __randomize_layout
, a struct attribute that is being used as part of the Linux KSPP to prevent data-only attacks on critical OS components. One of the drawbacks to this is that even under the circumstances of a randomized layout, an adversary with a read gadget can “infer” the correct address to write to within an allocated mutable data structure.
Following up on recent work from ARM, myself, and a bunch of other cool (imo) people regarding protecting these data structures using mechanisms operating at the page granularity (e.g. POE, see [RFC PATCH v3 00/15] pkeys-based page table hardening - Kevin Brodsky, or HVCI [RFC PATCH v3 0/5] Hypervisor-Enforced Kernel Integrity - CR pinning - Mickaël Salaün, and more), we still run into this problem of 20,000+ different types in the kernel, with all sorts of different semantics and allocation patterns.
After talking with Kees Cook and Kevin Brodsky about the subject in some more depth and spending many hours in thought on the topic, I have the following idea.
Bootstrap off of __randomize_struct with a flag that also introduces a canary value, not into the data structure but into a separate set of read-only code pages (these may be protected by the kernel or by HVCI), and XOR this canary value into fields of the data structure that we would like to mark “immutable” / protect. Then, have the code refer to this canary value when accessing protected fields of this data structure, but in general, memcpy and other movement/allocation/de-allocation operations can proceed as expected.
The adversary, then, to use a “write gadget” which aims to accomplish a data-only attack, would also need to change the canary value in the read-only memory page, but this is presumed difficult, and so they would need to leak the canary value in order to determine the “right” update. To avoid this there are several possible mechanisms, but my feeling is the seed value used for randstruct could also be used to partially randomize the location of the “ground truth” canary within the “canary region”, forcing the adversary to try all N (~20,000) canaries that are allocated for the data structures in the kernel.
I expect there may be significant issues related to type-casting, specifically, and that said protected types would need a warning on implicit conversion at the pointer level, and during direct assignment.
Is something like this even feasible? I will also do some digging to figure out how much it makes sense, but I wanted to post here and engage the Clang community on the ideas, get some critiques, additional ideas, etc..
Hopefully this all makes sense.
Regards,
Maxwell Bland