246 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			246 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<html>
 | 
						|
<head>
 | 
						|
<title>XFI</title>
 | 
						|
</head>
 | 
						|
<body>
 | 
						|
 | 
						|
<h1>XFI</h1>
 | 
						|
 | 
						|
<p>Required reading: XFI: software guards for system address spaces.
 | 
						|
 | 
						|
<h2>Introduction</h2>
 | 
						|
 | 
						|
<p>Problem: how to use untrusted code (an "extension") in a trusted
 | 
						|
program?
 | 
						|
<ul>
 | 
						|
<li>Use untrusted jpeg codec in Web browser
 | 
						|
<li>Use an untrusted driver in the kernel
 | 
						|
</ul>
 | 
						|
 | 
						|
<p>What are the dangers?
 | 
						|
<ul>
 | 
						|
<li>No fault isolations: extension modifies trusted code unintentionally
 | 
						|
<li>No protection: extension causes a security hole
 | 
						|
<ul>
 | 
						|
<li>Extension has a buffer overrun problem
 | 
						|
<li>Extension calls trusted program's functions
 | 
						|
<li>Extensions calls a trusted program's functions that is allowed to
 | 
						|
  call, but supplies "bad" arguments
 | 
						|
<li>Extensions calls privileged hardware instructions (when extending
 | 
						|
  kernel)
 | 
						|
<li>Extensions reads data out of trusted program it shouldn't.
 | 
						|
</ul>
 | 
						|
</ul>
 | 
						|
 | 
						|
<p>Possible solutions approaches:
 | 
						|
<ul>
 | 
						|
 | 
						|
<li>Run extension in its own address space with minimal
 | 
						|
  privileges. Rely on hardware and operating system protection
 | 
						|
  mechanism.
 | 
						|
 | 
						|
<li>Restrict the language in which the extension is written:
 | 
						|
<ul>
 | 
						|
 | 
						|
<li>Packet filter language.  Language is limited in its capabilities,
 | 
						|
  and it easy to guarantee "safe" execution.
 | 
						|
 | 
						|
<li>Type-safe language. Language runtime and compiler guarantee "safe"
 | 
						|
execution.
 | 
						|
</ul>
 | 
						|
 | 
						|
<li>Software-based sandboxing.
 | 
						|
 | 
						|
</ul>
 | 
						|
 | 
						|
<h2>Software-based sandboxing</h2>
 | 
						|
 | 
						|
<p>Sandboxer. A compiler or binary-rewriter sandboxes all unsafe
 | 
						|
  instructions in an extension by inserting additional instructions.
 | 
						|
  For example, every indirect store is preceded by a few instructions
 | 
						|
  that compute and check the target of the store at runtime.
 | 
						|
 | 
						|
<p>Verifier. When the extension is loaded in the trusted program, the
 | 
						|
  verifier checks if the extension is appropriately sandboxed (e.g.,
 | 
						|
  are all indirect stores sandboxed? does it call any privileged
 | 
						|
  instructions?).  If not, the extension is rejected. If yes, the
 | 
						|
  extension is loaded, and can run.  If the extension runs, the
 | 
						|
  instruction that sandbox unsafe instructions check if the unsafe
 | 
						|
  instruction is used in a safe way.
 | 
						|
 | 
						|
<p>The verifier must be trusted, but the sandboxer doesn't.  We can do
 | 
						|
  without the verifier, if the trusted program can establish that the
 | 
						|
  extension has been sandboxed by a trusted sandboxer.
 | 
						|
 | 
						|
<p>The paper refers to this setup as instance of proof-carrying code.
 | 
						|
 | 
						|
<h2>Software fault isolation</h2>
 | 
						|
 | 
						|
<p><a href="http://citeseer.ist.psu.edu/wahbe93efficient.html">SFI</a>
 | 
						|
by Wahbe et al. explored out to use sandboxing for fault isolation
 | 
						|
extensions; that is, use sandboxing to control that stores and jump
 | 
						|
stay within a specified memory range (i.e., they don't overwrite and
 | 
						|
jump into addresses in the trusted program unchecked).  They
 | 
						|
implemented SFI for a RISC processor, which simplify things since
 | 
						|
memory can be written only by store instructions (other instructions
 | 
						|
modify registers).  In addition, they assumed that there were plenty
 | 
						|
of registers, so that they can dedicate a few for sandboxing code.
 | 
						|
 | 
						|
<p>The extension is loaded into a specific range (called a segment)
 | 
						|
  within the trusted application's address space.  The segment is
 | 
						|
  identified by the upper bits of the addresses in the
 | 
						|
  segment. Separate code and data segments are necessary to prevent an
 | 
						|
  extension overwriting its code.
 | 
						|
 | 
						|
<p>An unsafe instruction on the MIPS is an instruction that jumps or
 | 
						|
  stores to an address that cannot be statically verified to be within
 | 
						|
  the correct segment.  Most control transfer operations, such
 | 
						|
  program-counter relative can be statically verified.  Stores to
 | 
						|
  static variables often use an immediate addressing mode and can be
 | 
						|
  statically verified.  Indirect jumps and indirect stores are unsafe.
 | 
						|
 | 
						|
<p>To sandbox those instructions the sandboxer could generate the
 | 
						|
  following code for each unsafe instruction:
 | 
						|
<pre>
 | 
						|
  DR0 <- target address
 | 
						|
  R0 <- DR0 >> shift-register;  // load in R0 segment id of target
 | 
						|
  CMP R0, segment-register;     // compare to segment id to segment's ID
 | 
						|
  BNE fault-isolation-error     // if not equal, branch to trusted error code
 | 
						|
  STORE using DR0
 | 
						|
</pre>
 | 
						|
In this code, DR0, shift-register, and segment register
 | 
						|
are <i>dedicated</i>: they cannot be used by the extension code.  The
 | 
						|
verifier must check if the extension doesn't use they registers.  R0
 | 
						|
is a scratch register, but doesn't have to be dedicated.  The
 | 
						|
dedicated registers are necessary, because otherwise extension could
 | 
						|
load DR0 and jump to the STORE instruction directly, skipping the
 | 
						|
check.
 | 
						|
 | 
						|
<p>This implementation costs 4 registers, and 4 additional instructions
 | 
						|
  for each unsafe instruction. One could do better, however:
 | 
						|
<pre>
 | 
						|
  DR0 <- target address & and-mask-register // mask segment ID from target
 | 
						|
  DR0 <- DR0 | segment register // insert this segment's ID
 | 
						|
  STORE using DR0
 | 
						|
</pre>
 | 
						|
This code just sets the write segment ID bits.  It doesn't catch
 | 
						|
illegal addresses; it just ensures that illegal addresses are within
 | 
						|
the segment, harming the extension but no other code.  Even if the
 | 
						|
extension jumps to the second instruction of this sandbox sequence,
 | 
						|
nothing bad will happen (because DR0 will already contain the correct
 | 
						|
segment ID).
 | 
						|
 | 
						|
<p>Optimizations include: 
 | 
						|
<ul>
 | 
						|
<li>use guard zones for <i>store value, offset(reg)</i>
 | 
						|
<li>treat SP as dedicated register (sandbox code that initializes it)
 | 
						|
<li>etc.
 | 
						|
</ul>
 | 
						|
 | 
						|
<h2>XFI</h2>
 | 
						|
 | 
						|
<p>XFI extends SFI in several ways:
 | 
						|
<ul>
 | 
						|
<li>Handles fault isolation and protection
 | 
						|
<li>Uses control-folow integrity (CFI) to get good performance
 | 
						|
<li>Doesn't use dedicated registers
 | 
						|
<li>Use two stacks (a scoped stack and an allocation stack) and only
 | 
						|
  allocation stack can be corrupted by buffer-overrun attacks. The
 | 
						|
  scoped stack cannot via computed memory references.
 | 
						|
<li>Uses a binary rewriter.
 | 
						|
<li>Works for the x86
 | 
						|
</ul>
 | 
						|
 | 
						|
<p>x86 is challenging, because limited registers and variable length
 | 
						|
  of instructions. SFI technique won't work with x86 instruction
 | 
						|
  set. For example if the binary contains:
 | 
						|
<pre>
 | 
						|
  25 CD 80 00 00   # AND eax, 0x80CD
 | 
						|
</pre>
 | 
						|
and an adversary can arrange to jump to the second byte, then the
 | 
						|
adversary calls system call on Linux, which has binary the binary
 | 
						|
representation CD 80.  Thus, XFI must control execution flow.
 | 
						|
 | 
						|
<p>XFI policy goals:
 | 
						|
<ul>
 | 
						|
<li>Memory-access constraints (like SFI)
 | 
						|
<li>Interface restrictions  (extension has fixed entry and exit points)
 | 
						|
<li>Scoped-stack integrity (calling stack is well formed)
 | 
						|
<li>Simplified instructions semantics (remove dangerous instructions)
 | 
						|
<li>System-environment integrity (ensure certain machine model
 | 
						|
  invariants, such as x86 flags register cannot be modified)
 | 
						|
<li>Control-flow integrity: execution must follow a static, expected
 | 
						|
  control-flow graph. (enter at beginning of basic blocks)
 | 
						|
<li>Program-data integrity (certain global variables in extension
 | 
						|
  cannot be accessed via computed memory addresses)
 | 
						|
</ul>
 | 
						|
 | 
						|
<p>The binary rewriter inserts guards to ensure these properties.  The
 | 
						|
  verifier check if the appropriate guards in place.  The primary
 | 
						|
  mechanisms used are:
 | 
						|
<ul>
 | 
						|
<li>CFI guards on computed control-flow transfers (see figure 2)
 | 
						|
<li>Two stacks
 | 
						|
<li>Guards on computer memory accesses (see figure 3)
 | 
						|
<li>Module header has a section that contain access permissions for
 | 
						|
  region
 | 
						|
<li>Binary rewriter, which performs intra-procedure analysis, and
 | 
						|
  generates guards, code for stack use, and verification hints
 | 
						|
<li>Verifier checks specific conditions per basic block. hints specify
 | 
						|
  the verification state for the entry to each basic block, and at
 | 
						|
  exit of basic block the verifier checks that the final state implies
 | 
						|
  the verification state at entry to all possible successor basic
 | 
						|
  blocks. (see figure 4)
 | 
						|
</ul>
 | 
						|
 | 
						|
<p>Can XFI protect against the attack discussed in last lecture?
 | 
						|
<pre>
 | 
						|
  unsigned int j;
 | 
						|
  p=(unsigned char *)s->init_buf->data;
 | 
						|
  j= *(p++);
 | 
						|
  s->session->session_id_length=j;
 | 
						|
  memcpy(s->session->session_id,p,j);
 | 
						|
</pre>
 | 
						|
Where will <i>j</i> be located?
 | 
						|
 | 
						|
<p>How about the following one from the paper <a href="http://research.microsoft.com/users/jpincus/beyond-stack-smashing.pdf"><i>Beyond stack smashing:
 | 
						|
  recent advances in exploiting buffer overruns</i></a>?
 | 
						|
<pre>
 | 
						|
void f2b(void * arg, size_t len) {
 | 
						|
  char buf[100];
 | 
						|
  long val = ..;
 | 
						|
  long *ptr = ..;
 | 
						|
  extern void (*f)();
 | 
						|
  
 | 
						|
  memcopy(buff, arg, len);
 | 
						|
  *ptr = val;
 | 
						|
  f();
 | 
						|
  ...
 | 
						|
  return;
 | 
						|
}
 | 
						|
</pre>
 | 
						|
What code can <i>(*f)()</i> call?  Code that the attacker inserted?
 | 
						|
Code in libc?
 | 
						|
 | 
						|
<p>How about an attack that use <i>ptr</i> in the above code to
 | 
						|
  overwrite a method's address in a class's dispatch table with an
 | 
						|
  address of support function?
 | 
						|
 | 
						|
<p>How about <a href="http://research.microsoft.com/~shuochen/papers/usenix05data_attack.pdf">data-only attacks</a>?  For example, attacker
 | 
						|
  overwrites <i>pw_uid</i> in the heap with 0 before the following
 | 
						|
  code executes (when downloading /etc/passwd and then uploading it with a
 | 
						|
  modified entry).
 | 
						|
<pre>
 | 
						|
FILE *getdatasock( ... ) {
 | 
						|
  seteuid(0);
 | 
						|
  setsockeope ( ...);
 | 
						|
  ...
 | 
						|
  seteuid(pw->pw_uid);
 | 
						|
  ...
 | 
						|
}
 | 
						|
</pre>
 | 
						|
 | 
						|
<p>How much does XFI slow down applications? How many more
 | 
						|
  instructions are executed?  (see Tables 1-4)
 | 
						|
 | 
						|
</body>
 |