Loop-Oriented Programming: A New Code Reuse Attack to Bypass Modern
Defenses
Bingchen Lan, Yan Li, Hao Sun, Chao Su, Yao Liu, Qingkai Zeng
State Key Laboratory for Novel Software Technology
Department of Computer Science and Technology
Nanjing University, Nanjing 210023, China
Email: zongdugexia@163.com
Abstract—Code reuse attacks have become one of the most
popular exploitation techniques, and coarse-grained control
flow integrity (CFI) is a practical approach used to prevent
such attacks. Recently, some new approaches have been
proposed to construct call-preceded-ROP attacks to bypass
coarse-grained CFI, however, we find that they still fail to
bypass shadow stack, which enforces caller-callee semantics to
strengthen CFI that constrains the control flow in a much
stricter way. Therefore, in this paper, we propose a new code
reuse attack, named loop-oriented programming (LOP), aiming
to bypass both coarse-grained CFI and shadow stack. Quite
different from previous code reuse attacks, LOP collects entire
functions as basic building blocks (i.e., gadgets), and chains
these gadgets in a way that the control flows strictly follow the
process of call-ret-pairing. Specifically, LOP selects a
particular function with a loop statement, called loop gadget, to
chain all the available gadgets. To demonstrate the
effectiveness of LOP, we construct a proof-of-concept exploit
against Internet Explorer 8 on 32-bit x86 platform.
Keywords- Code Reuse Attack; Control Flow Integrity; Call-
Ret-Pairing; Loop-Oriented Programming
I. INTRODUCTION
In order to bypass traditional data execution protection
(DEP), code reuse attacks chain together a number of
instruction sequences, which are originally present in the
program’s address space, to conduct arbitrary operations.
The instruction sequence is commonly called gadget, and it
usually ends with an indirect branch. Generally speaking,
there are two steps to launch the attack: 1) searching for
gadgets, and 2) chaining gadgets via indirect branches. For
example, ROP [2] and SROP [28] choose short code
fragments inside functions as gadgets and chain them by ret
instructions, whereas the gadgets in JOP [4, 5] that consist of
short code fragments are chained by indirect jumps.
One of the most promising defense mechanisms against
code reuse attack is control-flow integrity (CFI) [23]. The
main idea of CFI is to derive an application’s control-flow
graph (CFG) prior to execution, and then monitor indirect
branches to ensure that the control flows follow a legitimate
path of the CFG. However, due to some limitations,
particularly the difficulty of obtaining a precise CFG without
source code and debugging symbols, most CFI solutions [16,
17, 18, 20, 21] have attempted to relax CFI policies by
applying it directly to binaries. Often, these loose policies
simply require that indirect call points to the beginning of a
function, and function should returns to any valid call site
(but not necessarily to the particular call site of the caller).
Recently, a number of proof-of-concept exploits have
been proposed to construct call-preceded-ROP attacks [7, 8,
10, 26] to bypass coarse-grained CFI. We refer to such
attacks as CPROP for short. Gadgets in CPROP begin right
after call instructions, and thus the control flow of ret
instruction can bypass the loose policies. As demonstrated in
CPROP, coarse-grained CFI solutions are ineffective and
weaker than generally thought.
In order to strengthen the security of coarse-grained CFI
solutions, defenders may further restrict the indirect branches
(indirect call, indirect jump, ret) in stricter way. First,
shadow stack [12] is a run-time defense to complement CFI
toward stricter protection for ret instruction. Second, there is,
however, no effective method to enforce strict policies for
indirect call and indirect jump since the difficulty to exactly
extract the target addresses in binaries. Although some works
[29] can guarantee the integrity of function pointers in a
program and thereby further restrict indirect call and indirect
jump, they are impractical as they need source code, which is
usually unavailable, particularly for commercial-off-the-shelf
(COTS). Therefore, strengthening coarse-grained CFI is
currently realized through shadow stack, which can defend
against CPROP and is usually considered as the most
restrictive protection for ret instruction.
Shadow stack [12] is a run-time mechanism for checking
that functions return to their caller. In the original CFI work,
Abadi et al. [23] utilize the shadow stack that is securely
maintained at runtime to harden CFI, and thus shadow stack
can complement coarse-grained CFI toward stricter
protection from code reuse attacks. The shadow stack usually
works that it keeps one copy for each return address in stack,
such that the ret instruction is forced to point to the
originally caller. Hence, shadow stack constrains the control
flows in a much stricter way by verifying the caller-callee
semantics, whereas CPROP violates this. Besides, some
works [19] have shown that the performance overhead of
shadow stack can be low. Hence, shadow stack is currently
an effective and practical defense that strengthens CFI
against code reuse attacks.
In this paper, our goal is to bypass the combination of
coarse-grained CFI and shadow stack, which we believe is
2015 IEEE Trustcom/BigDataSE/ISPA
978-1-4673-7952-6/15 $31.00 © 2015 IEEE
DOI 10.1109/Trustcom-BigDataSe-ISPA.2015.374
190