Monday, January 26, 2015

Control-flow processor exceptions (single-stepping on branches) on control-flow branch instructions (jmp/call/ret)

"single-stepping on branches" is processor hardware feature of x86/Intel architecture. When it is enabled, the processor generates a single-step debug exception only after instructions that cause a branch. This mechanism
allows a debugger to single-step on control transfers caused by branches. What does this imply to defense against control-flow hijacking attacks (e.g. ROP or JOP) ? 

Control-flow Transfer Instructions
Control-flow hijacking attacks allow an attacker to overwrite a value that is loaded into the program counter (EIP) of a running program, typically redirecting execution to his own injected code or existing ROP/JOP gadget chains for executing arbitrary malicious code. In general, the value that is subverted could a jump target address, function pointer, or return address in a user-controlled stack. 

Call/Jmp/Ret instructions, called as control-transfer branch instructions, are used by control-flow hijacking attacks to redirect CPU execution. There are many software tools that can perform binary analysis on those instructions, for example, by dynamically instrumenting control-flow graph (CFG) for control-flow integrity (CFI) enforcement. So it would be good if the hardware processor can generate an exception on (or after) those control-transfer instructions. 

Single-stepping on Branches
In x86/Intel processor architecture, there is a bit (Trap Flag, TF) in EFLAGS register as below. It is set to enable single-step mode for debugging, clear to disable single-step mode. 
In single-step mode, the processor generates a debug exception (#DB) after each instruction. This allows the execution state of a program to be inspected after each instruction.

However, things are changed under a special condition as indicated below. When BTF (single-step on branches) flag in IA32_DEBUGCTL MSR is set, the processor treats the TF flag in the EFLAGS register as a “single-step on branches” flag rather than a “single-step on instructions” flag. This mechanism allows single-stepping the processor on taken branches. Note that the exception is a trap-class exception, which means the exception is generated after the branch instruction (call/ret/jmp) is executed.
So now we can make processor generate an exception (#DB) on (^after^, actually) every call/jmp/ret instruction. 

Potential Usages
We might have some usages with this capability, for example:

  1. Build dynamical CFG (Control Flow Graph) without changes to software binary or source code.
  2. Detect unknown control-flow hijacking vulnerabilities by using dynamic taint analysis, e.g. when a tainted value loaded into the program counter (EIP) has been influenced by data from the untrusted inputs.
  3. Perform software-invisible hooks for function calling (target of "call" instruction).

However, there are some limitations:

  1. Performance overhead !!! (unless we use it under some environment where performance is not a big concern).
  2. It cannot control jmp/ret/call individually, for example, trigger exceptions only on CALL instructions, or RET instructions, or even only on "indirect" jmp/call instructions (because normally code with direct-jmp/call is trusted due to W^X on code section). 
  3. It also has no CPL (user or kernel) controls, but we can control it through EFLAGS.TF bit crossing system call/ret. 
  4. Because this #DB is controlled by EFLAGS bit, it can be easy to be disabled by using a "popf" instruction if the stack is controlled by an attacker:( . 
  5. It obviously requires OS kernel changes (Does OS provide legitimate #DB handler registration?) 

Please let me know if you have any comments.

Intel IA32 architecture software development manual: 

No comments:

Post a Comment