Uninformed: Informative Information for the Uninformed

Vol 8» 2007.Sep


Next: SSDT Up: Descriptor Tables Previous: IDT   Contents

GDT / LDT

The Global Descriptor Table (GDT) and Local Descriptor Table (LDT) are used to store segment descriptors that describe a view of a system's address space1. Segment descriptors include the base address, limit, privilege information, and other flags that are used by the processor when translating a logical address (seg:offset) to a linear address. Segment selectors are integers that are used to indirectly reference individual segment descriptors based on their offset into a given descriptor table. Software makes use of segment selectors through segment registers, such as CS, DS, ES, and so on. More detail about the behavior on segmentation can be found in the x86 and x64 system programming manuals[1].

In Phrack 55, Greg Hoglund described the potential for abusing conforming code segments[19]. A conforming code segment, as opposed to a non-conforming code segment, permits control transfers where CPL is numerically greater than DPL. However, the CPL is not altered as a result of this type of control transfer. As such, effective privileges of the caller are not changed. For this reason, it's unclear how this could be used to access kernel-mode memory due to the fact that page protections would still prevent lesser privileged callers from accessing kernel-mode pages when paging is enabled.

Derek Soeder identified an awesome flaw in 2003 that allowed a user-mode process to create an expand-down segment descriptor in the calling process' LDT[40]. An expand-down segment descriptor inverts the meaning of the limit and base address associated with a segment descriptor. In this way, the limit describes the lower limit and the base address describes the upper limit. The reason this is useful is due to the fact that when kernel-mode routines validate addresses passed in from user-mode, they assume flat segments that start at base address zero. This is the same thing as assuming that a logical address is equivalent to a linear address. However, when expand-down segment descriptors are used, the linear address will reference a memory location that can be in stark contrast to the address that's being validated by kernel-mode. In order to exploit this condition to escalate privileges, all that's necessary is to identify a system service in kernel-mode that will run with escalated privileges and make use of segment selectors provided by user-mode without properly validating them. Derek gives an example of a MOVS instruction in the int 0x2e handler. This trick can be abused in the context of a local kernel-mode backdoor to provide a way for user-mode code to be able to read and write kernel-mode memory.

In addition to abusing specific flaws in the way memory can be referenced through the GDT and LDT, it's also possible to define custom gate descriptors that would make it possible to call code in kernel-mode from user-mode[23]. One particularly useful type of gate descriptor, at least in the context of a backdoor, is a call gate descriptor. The purpose of a call gate is to allow lesser privileged code to call more privileged code in a secure fashion[45]. To abuse this, a backdoor can simply define its own call gate descriptor and then make use of it to run code in the context of the kernel.

Category: Type IIa; with the exception of the LDT. The LDT may be better classified as Type II considering it exposes an API to user-mode that allows the creation of custom LDT entries (NtSetLdtEntries).

Origin: It's unclear if there were some situational requirements that would be needed in order to abuse the issue described by Greg Hoglund. The flaw identified by Derek Soeder in 2003 was an example of a recurrence of an issue that was found in older versions of other operating systems, such as Linux. For example, a mailing list post made by Morten Welinder to LKML in 1996 describes a fix for what appears to be the same type of issue that was identified by Derek[44]. Creating a custom gate descriptor for use in the context of a backdoor has been used in the past. Greg Hoglund described the use of call gates in the context of a rootkit in 1999[19]

Capabilities: In the case of the expand-down segment descriptor, access to kernel-mode data is possible. This can also indirectly lead to kernel-mode code execution, but it would rely on another backdoor technique. If a gate descriptor is abused, direct kernel-mode code execution is possible.

Covertness: It is entirely possible to write have code that will detect the addition or alteration of entries in the GDT or each individual process LDT. For example, PatchGuard will currently detect alterations to the GDT.