Rediscovering Heaven's Gate on Linux
While poking around for new and spicy Linux malware techniques, I stumbled across an old(er) post from Red Canary about Heaven’s Gate on Linux. Being familiar with the equivalent technique on Windows, it piqued my interest and inspired me to slightly further the technique, resulting in a library (which will be released alongside my SAINTCON talk in October) to send all syscalls through the gate.
Heavens Gate
For those unfamiliar (or need a refresh) with the Heaven’s Gate technique, it was first published by Roy G Biv in issue 1 of the VALHALLA e-zine in late 2009, as a way to confuse antiviruses and sandboxes by taking advantage of the WoW64 subsystem to call 32-bit code in 64-bit processes and vice-versa. Though it is not quite as common on Linux (due to kprobes and such), many EDRs on Windows have historically used userland hooking of ntdll in order to detect malicious API calls.
The technique abuses the GDT (Global Descriptor Table), a structure utilized by modern CPUs to understand their current execution mode.
Building a Linux 64-bit PoC
Luckily for us, there are backwards-compatibility layers (found in arch/x86/entry/entry_64_compat.S in kernel source) that makes it super easy to call 32-bit system calls inside 64 bit binaries.
It’s fairly fast and simple to get our first proof-of-concept, that doesn’t rely off the GDT or any long jump. We just need to copy all of our arguments into the lower 2GB of the process’s memory (so the 32-bit system call handler can understand it), or else the call will gracefully fail. There is not too much extra leg work, and we can write a little inline assembly to get us the rest of the way there.
Building a Linux 32-bit PoC
Calling 64 bit syscalls from 32 bit programs is a bit trickier. The OS-provided compatibility layers don’t exist, instead, we need to get familiar with the Global Descriptor Table (GDT). The GDT is essentially a little table of contents that defines how certain memory is accessed and protected. When a value is loaded into the “Code Segment” register $CS, it tells the processor perform a decriptor lookup in the GDT, where it says to execute in a certain mode. In our case, the value 0x33 tells the processor to execute the code in 64-bit mode. We cannot directly manipulate the $CS register (with pop/mov), however, it is changed when doing long jumps/calls, and we’ll be able to utilize that to call back up to 64-bit land, and have the syscall executed there.
In order to pull this off, we will have to set up two handwritten ASM functions, one 64-bit and one 32-bit. Our 32-bit function will move all of our arguments onto the stack, perform our long jump, and then restore the stack once we return. Meanwhile, our 64-bit function will read our arguments out of rbx and move them into the lower 32-bits of each register, perform the actual syscall, grab the error code, and do a far return (retf) back into 32-bit land. The resulting assembly code is as follows.
Putting those two functions together, alongside a similar C harness that we utilized for the 64-bit PoC, gives us the ability to perform our syscalls through a 32-to-64-bit call gate, which we can verify with strace! Unfortunately, the original “confuse strace” technique originally published by Red Canary no longer works, and strace can correctly detect “personalities” (architecture-specific execution).
Conclusion
Thank you to Carl Petty from Red Canary for first publicizing this technique on the Linux front. This version closely resembles his original research, but slightly improves upon it by:
- Correctly passing arguments to 64-bit mode through the lower 32-bits (to allow addresses to be passed)
- Extracting the logic out into simple copy-pastable functions to allow arbitrary calls through the gate without relying on complex makefiles
While Heaven’s Gate isn’t quite as useful on Linux as it was on Windows, it can still confuse some security products and make our system calls invisible. The full source code for this research is available on GitHub.
Happy Hacking!
References
https://www.malwaretech.com/2014/02/the-0x33-segment-selector-heavens-gate.html
https://redcanary.com/blog/threat-detection/heavens-gate-technique-on-linux/


