• No results found

Code Injection

In document Snow_unc_0153D_14970.pdf (Page 41-45)

The basic idea of code injection is straight-forward: bytes representing pre-compiled machine code are placed into an executable (and writable) memory region of a running process. The instruction pointer is then loaded with the address of the bytes via a control-flow hijack and it executes.

On the No-Execute Bit for Exploit Mitigation: DEP (Microsoft, 2006) was introduced to prevent code injection attacks. This is done by leveraging the “no-execute” bit in the memory management unit (MMU). Like the “read” and “write” bits that control which per-page chunks of virtual memory can be accessed by the application, the no-execute bit controls which pages can be executed. DEP, by default, allocates application stack and heap regions without the execute permission, while application code has the execute permission, but not write permission. Without DEP, one could replace the string of ‘A’s in Figure 2.6 with bytes representing instructions and replace the expectedRETwith the starting address of that string. The function would return to that injected code rather than the function that called it. DEP mitigates this exploit due to the fact that the program stack will not be granted the execute permission.

This seemingly fool-proof mitigation, however, is defeated by several methods and circumstances. First, application developers are not required to adhere to the default policy, creating a gap in protection wherein end-users may be protected for one application, but not another. For example, early just-in-time (JIT) compilers for JavaScript, ActionScript, and Java would allocate chunks of memory as writable (to write the newly compiled code) and executable (to allow it to then run) without subsequently removing the write permission. DEP is essentially “turned off” in those regions. Continuing with the JIT example, another method of bypassing DEP is the so-called JIT-spray

attack (Blazakis, 2010). The basic concept of JIT-spraying is to massage the JIT-compiler into unknowingly constructing the code injection payload on the adversary’s behalf. As an example, one can create a JavaScript with a sequence of arithmetic operations. The JavaScript JIT engine compiles these operations into instructions and marks them as executable. One can carefully construct the arithmetic operands such that if one redirects program control-flow to the start of an operand it is decoded as a valid instruction. Thus, an entire code injection payload may be encoded by “spraying” a sequence of arithmetic operations in a script. JIT-spraying, of course, is highly architecture and application specific.

Due to the wide-spread adoption of DEP the most practiced method of executing code after a control-flow hijack is ROP. However, the difficulty and specificity of ROP has led adversaries to leverage code reuse merely for allocating a writable and executable region of memory to facilitate a secondary code injection payload. Although not strictly required, this approach decreases the adversary’slevel-of-effort. Minimal, application specific, ROP payloads are used to disable DEP and load platform-independent, reusable, code injection payloads. In turn, these code injection payloads download and execute malware or perform other malicious actions (see Chapter 6).

Challenges Unique to Code Injection: When constructing code injection payloads, there are several considerations that make their development difficult. One challenge stems from the fact that injected code lacks the standard procedure of being loaded by the operating system loader routines. These routines normally handle mapping of code and data virtual addresses, code and data relocation fix-ups, and dynamic linking. Developers of code injection payloads must handle these problems by managing their own code and data sections with custom schemes, using position-independent code (PIC), and performing their own resolution of dynamic libraries and functions. Since no widely used compiler supports all of these requirements, code injection payloads must be developed in assembly language or with custom tools. Another challenge lies in the fact that, depending on the specifics of a particular vulnerability, the content of the buffer of bytes may be restricted in which bytes are “allowed”. The canonical example of this restriction are buffer overflow vulnerabilities that involve a Cstring copy. InC, strings are terminated by a null (‘\0’) byte. Therefore, any null-bytes within a payload used in such an exploit will result in only the portion of the payload before the null-byte being copied, breaking execution of the injected code. The bytes allowed are application-specific, but can range from allowing all bytes, to ASCII-only bytes, or only those bytes within the Unicode character set, etc. To deal with this restriction, injected code must either be carefully written so as to avoid restricted characters all-together, or polymorphic code can be used. A polymorphic payload means that the code is encoded,e.g. byxor’ing it, etc. When a polymorphic payload is executed a small bit of code will dynamically decode the injected code’s body before jumping to it. Polymorphic payloads are used to meet any byte-value restrictions, but also make multiple instances of the same payload unique and camouflage payloads to blend in with benign data. Mason et al. (2009), for

example, demonstrate that fully functional arbitrary code injection payloads can take the form of English text.

Unfortunately, polymorphism makes it impractical for defenders to statically decide if a particular chunk of data represents injected code. Even without extreme examples like the use of English text-based code, polymorphic payloads are well-known for being problematic for both signature and learning-based intrusion detection systems (Song et al., 2010). Chapter 5 takes a more dynamic approach to detecting these code injection payloads.

CHAPTER 3: JUST-IN-TIME CODE REUSE

In light of the code reuse payload paradigm, whether return-oriented (Shacham, 2007), jump- oriented (Bletsch et al., 2011), or some other form of “borrowed code” (Krahmer, 2005), skilled adversaries have been actively searching for ever more ingenious ways to leverage memory disclo- sures as part of their arsenal (Sotirov and Dowd, 2008b; Serna, 2012a; VUPEN Security, 2012; Larry and Bastian, 2012). At the same time, defenders have been busily working to fortify perimeters by designing “enhanced” randomization strategies (Bhatkar et al., 2005; Kil et al., 2006; Pappas et al., 2012; Hiser et al., 2012; Wartell et al., 2012; Giuffrida et al., 2012) for repelling the next generation of wily hackers. This chapter questions whether this particular line of thinking (regarding fine-grained code randomization) offers a viable alternative in the long run. In particular, this chapter examines the folly of recent exploit mitigation techniques, and shows that memory disclosures are far more damaging than previously believed. Just as the introduction of SEH overwrites bypassed protection provided by stack canaries, code reuse undermines DEP, and memory disclosures defied the basic premise of ASLR, this chapter assails the assumptions embodied by fine-grained ASLR.

The primary contribution of this chapter is in showing that fine-grained ASLR for exploit mitigation, even considering an ideal implementation, is not any more effective than traditional ASLR implementations. Strong evidence for this is provided by implementing a framework wherein one can automatically adapt an arbitrary memory disclosure to one that can be used multiple times to reliably map a vulnerable application’s memory layout, then just-in-time compile the attacker’s program, re- using (finely randomized) code. The workflow of the framework takes placeentirelywithin a single script (e.g., as used by browsers) for remote exploits confronting application-level randomization, or a single binary for local privilege escalation exploits battling kernel-level randomization.

The power of this framework is demonstrated by using it in conjunction with a real-world exploit against Internet Explorer, and also by providing extensive evaluations that demonstrate the practicality of just-in-time code reuse attacks. In light of these findings, this chapter argues that the trend toward fine-grained ASLR strategies may be short-sighted. It is hoped that, moving forward,

this work spurs discussion and inspires others to explore more comprehensive defensive strategies than what exists today.

In document Snow_unc_0153D_14970.pdf (Page 41-45)