B - ESTRUCTURA DE LA ADMINISTRACIÓN DE LA SOCIEDAD
F. Ultimo nombram
While our current low-level monitor implementations are both effective and practical, there are several possible improvements that could be made. First, it may be possible to improve the performance and effectiveness of the VMM-based monitor by more tightly integrating it with the hypervisor itself. For example, the hypervisor could be modified to directly enforce a more strict environment, thereby reducing the set of checks required by the monitor. Additionally, the monitor could be modified to be more “event driven,” checking only when certain key guest VM events occur, as signaled by the hypervisor.
Second, as support for new trusted computing hardware becomes more widespread, the VMM-based monitor could be moved to an isolated partition outside of domain 0 and protected using attestation and late launch technologies, which are described in more detail in Chapter 7. Loscocco et al. have independently developed a Xen-based system similar to ours and propose these added protection measures [63].
Third, our current Xen implementation supports only unmodified guest ker- nels. It would be trivial to extend our technique to monitor paravirtualized guests, as demonstrated by the XenAccess project [82].
Finally, a hybrid VMM/coprocessor system has the potential to strike an ap- pealing balance between the advantages of each approach. When combined with the control and visibility of a hypervisor (or SMM-based monitor, described in Chap- ter 7), the performance and isolation benefits of a coprocessor make it an excellent choice for offloading valuable cycles from application CPUs and network cards.
Chapter 5
State-Based Control-Flow Integrity
Having described the low-level mechanisms by which our property-based mon- itor gains secure access to the kernel’s state, we now turn our attention to the details of property enforcement — that is, which properties of the kernel’s state should be checked and how to effectively implement those checks. This chapter presents the first of two property modules that we have developed. This module enforces a prop- erty that we call state-based control-flow integrity (SBCFI).1
A violation of SBCFI suggests that an attacker has introduced illicit functionality into the kernel by mak- ing a persistent modification to its normal execution. We begin our discussion by describing control-flow integrity (CFI), a stronger property on which SBCFI is based, and argue that violations of CFI are highly-correlated with an attacker’s goals. As we explain, complete CFI monitoring is impractical for OS kernels. Therefore, we propose SBCFI as an alternative and present the details of its implementation.
5.1 Control-Flow Integrity
A program P satisfies the CFI property so long as its execution only follows paths according to a control-flow graph (CFG), determined in advance. If this graph approximates the control flow of the unmodified P , then a violation of CFI signals
1
that P ’s integrity has been violated. CFI enforcement has been shown to be effective against a wide range of common attacks on user programs, including stack-based buffer-overflow attacks, heap-based “jump-to-libc” attacks, and others [2].
More generally, the goals of rootkits are squarely at odds with CFI. Since the attacker’s main goal is to add surreptitious functionality to the system, then either this functionality or the means to hide it are most easily enabled by modifying the kernel’s CFG to call injected code. Our analysis of Linux rootkits described in Chapter 2 shows that an overwhelming majority of them, 24 out of 25 (96%), violate the control-flow integrity of the kernel in some way. As far as we are aware, we are the first to make this observation. Additionally, our preliminary analysis of about a dozen Windows kernel rootkits demonstrates a similar trend among those threats. This suggests that CFI is a useful property to monitor in the kernel.
Abadi et al. [2] enforce CFI for a program P by rewriting P ’s binary. The tar- get of each non-static branch is given a tag, and each branch instruction is prepended with a check that the target’s tag is in accord with the CFG. This strategy provides protection against an attacker with access to P ’s memory under three conditions: (1) tags must not occur anywhere else in P ’s code; (2) P ’s code must be read-only; and (3) P ’s data must be non-executable. These assumptions are easily discharged for applications. The first assumption is discharged by rewriting the entire appli- cation at once, preventing conflicts, and the latter two are discharged by setting page table entries and segmentation descriptors appropriately; as the page tables can only be modified within the kernel, it is assumed they are inaccessible to the attacker.
Unfortunately, these assumptions cannot be discharged as easily when moni- toring the kernel itself. An attacker with access to kernel memory could overwrite page table entries to make code writable or data executable, violating assumptions (2) and (3). It is also unrealistic to expect to rewrite all core kernel code and LKMs at the outset, and thus it is difficult to discharge the first assumption and avoid tag conflicts. Moreover, it is nontrivial to compute a precise CFG for the kernel in advance, due to its rich control structure, with several levels of interrupt handling and concurrency.