Supervisor Mode Execution Prevention is a CPU security feature which aims to prevent execution of untrusted memory while operating at a greater privilege level. In short, it detects so-called “ring0” (kernelspace) code that is running in “ring3” (userspace).
SMEP was first introduced in 2011 by Intel on the Ivy Bridge Architecture. It was designed in order to address classes of Local privilege Escalation (LPE) sometimes also known as Escalation of Privilege (EoP) attacks. Historically, access permission rules for code execution were as follows:
- Supervisor mode (ring 0-2): user or supervisor pages allowed (u/s==*)
- User mode (ring 3): user only (u/s==1)
But with the introduction of SMEP (and when its active) the permissions are as follows:
- Supervisor mode: supervisor only (u/s==0)
- User mode: user only (u/s==1)
What this actually means, we’ll get into shortly. Effectively, a new bit indicates a “supervisor” page in the page tables.In a talk given by Stephen Fischer of Intel, he mentioned a number of vulnerabilities which would have been unexploitable with SMEP in place.
- US-CERT 362981
- US-CERT 537223
You can find more specific details on each of these advisories in his slides.
High Level of How SMEP Stops LPE
Imagine you have a vulnerability in a kernel driver, you want to execute some shellcode. So you store that shellcode in the userspace and you wish to execute the code in the userspace in kernelspace, with no SMEP this is absolutely okay, nothing will prevent this execution from taking place.
However, with SMEP in place this attack will be mitigated. When the redirection to the userspace code occurs, a pagefault will be raised by SMEP and sent back to the OS, this will then block that execution from occuring.
The implementation begins with the CR4 control register. Bit 20 of the CR4 register is reserved for “SMEP”. If the SMEP bit is set to 1 and we’re currently in supervisor mode then instructions cannot be executed from an address for which the U/S flag is set to 1 (usermode) without a pagefault being raised.
It is worth noting that although the SMEP bit itself is set in the CR4 register, it is enforced through the PTE of a memory address. As discussed, this works by checking the User/Supervisor bit of a PTE. I’ve featured two diagrams below which helps to understand this concept.
This first diagram above shows the CR4 control register, as you can see the 20th bit is reserved for SMEP. Setting this bit to 1 enables SMEP and enforces the mentioned protection. Setting this bit to 0 turns SMEP off. In the next diagram (below) you can see a page structure with the User/Supervisor bit, which is used to designate whether a page table belongs to usermode or supervisor mode, this tracking is extremely important for not just SMEP.
As shown in the above diagram, bit 2 is designated for User/Supervisor this bit is how SMEP knows whether a page fault is required during execution of userspace memory when in kernelspace.
As mentioned, a page fault will occur if CR4.SMEP = 1, the Code Privilege Level (CPL) < 3 and an instruction is fetched from usermode page. The structure of a page fault is as follows.
As you can see in the diagram above, the page fault structure has a bit reserved for “U/S” also, if this bit is set to 0 then the CPU knows that a supervisor-mode access caused the fault. If this bit is set to 1 then a user-mode accessed caused the fault.
Implementation on Operating Systems
Windows first introduced SMEP to be enabled by default on Windows 8.0 for both 32 and 64-bit systems. This meant that from Windows 8 onwards LPE exploits had to consider this feature for successful exploitation.
As with all things Linux, SMEP was introduced on Linux much sooner. The first patch to the upstream Linux kernel was submitted by Fenghua Yu on May 16th, 2011. One interesting point regarding this protection on Linux is that before SMEP was implemented, a feature called PAX_UDEREF was introduced in 2010 (before SMEP) by the PaX project, I’ll make a separate post on this in the future but it is interesting to note that in-fact someone was already working on a software implementation of this feature before Intel.
Bypassing SMEP is somewhat platform dependent, however there are a few general suggestions. The first is to jump to the kernel heap, in the case of Windows this only works on 32-bit and in the case of Linux, I am not 100% sure if that is viable.
The second suggested method, which is quite common is ROPing in kernelspace to form a chan that can set the SMEP bit to 0 thus turning it off.
There are many other suggested techniques, however these are two of the common ones, you could spend all day looking over different bypasses.
SMEP is a really good idea, but implementation matters. Because SMEP by itself is (was) quite easy to defeat, Intel later released SMAP, another protection which aims to prevent the SMEP bypasses. I’ll talk about this in another post.
At some point, I’ll try and implement SMEP & SMAP in my osdev project and build some test cases for it. I’ll make a blog post about that when I do it.
If you’d like more information on the CPU implementation of SMEP, I’d suggest reading Intel Volume 3A: Part 1