Uninformed: Informative Information for the Uninformed

Vol 8» 2007.Sep


Leaking CS

With the introduction of protected mode into the x86 architecture, the concept of separate privilege levels, or rings, was born. Lesser privileged rings (such as ring 3) were designed to be restricted from accessing resources associated with more privileged rings (such as ring 0). To support this concept, segment descriptors are able to define access restrictions based on which rings should be allowed to access a given region of memory. The processor derives the Current Privilege Level (CPL) by looking at the low order two bits of the CS segment selector when it is loaded. If all bits are cleared, the processor is running at ring 0, the most privileged ring. If all bits are set, then processor is running at ring 3, the least privileged ring.

When certain events occur that require the operating system's kernel to take control, such as an interrupt, the processor automatically transitions from whatever ring it is currently executing at to ring 0 so that the request may be serviced by the kernel. As part of this transition, the processor saves the value of the a number of different registers, including the previous value of CS, to the stack in order to make it possible to pick up execution where it left off after the request has been serviced. The following structure describes the order in which these registers are saved on the stack:

typedef struct _SAVED_STATE
{
    ULONG_PTR Eip;
    ULONG_PTR CodeSelector;
    ULONG     Eflags;
    ULONG_PTR Esp;
    ULONG_PTR StackSelector;
} SAVED_STATE, *PSAVED_STATE

Potential security implications may arise if there is a condition where some code can alter the saved execution state in such a way that the saved CS is modified from a lesser privileged CS to a more privileged CS by clearing the low order bits. When the saved execution state is used to restore the active processor state, such as through an iret, the original caller immediately obtains ring 0 privileges.

Category: Undefined; this approach does not fit into any of the defined categories as it simply takes advantage of hardware behavior relating around how CS is used to determine the CPL of a processor. If code patching is used to be able to modify the saved CS, then the implementation is Type I.

Origin: Leaking CS to user-mode has been known to be dangerous since the introduction of protected mode (and thus rings) into the x86 architecture with the 80286 in 1982[22]. This approach therefore falls into the category of obvious due to the documented hardware implications of leaking a kernel-mode CS when transitioning back to user-mode.

Capabilities: Kernel-mode code execution.

Considerations: Leaking the kernel-mode CS to user-mode may have undesired consequences. Whatever code is to be called in user-mode must take into account that it will be running in a kernel-mode context. Furthermore, the kernel attempts to be as rigorous as possible about checking to ensure that a thread executing in user-mode is not allowed a kernel-mode CS.

Covertness: Depending on the method used to intercept and alter the saved execution state, this method has the potential to be fairly covert. If the method involves secondary hooking in order to modify the state, then it may be detected through some of the same techniques as described in the section on image patching.