NoJITsu: Locking Down JavaScript Engines

Conference:  BlackHat USA 2020



The presentation discusses a bytecode interpreter attack on JavaScript engines and proposes a solution using Intel NDK to enforce fine-grained memory access control.
  • JavaScript engines use bytecode interpreter to execute scripts
  • Bytecode interpreter can be corrupted to run arbitrary system calls
  • Intel NDK can be used to enforce fine-grained memory access control to protect against the attack
  • Intel NDK assigns memory chunks with up to 15 keys with associated permissions
  • Changing the permission bit of a specific key at peek-a-ru register can change the permission of the memory chunk
The presentation gives an example of an attacker script that corrupts the function object with the address of the system function and corrupts the context object with any shared command, successfully running the system code.


Data-only attacks against dynamic scripting environments have become common. Web browsers and other modern applications embed scripting engines to support interactive content. The scripting engines optimize performance via just-in-time compilation. Since applications are increasingly hardened against code-reuse attacks, adversaries are looking to achieve code execution or elevate privileges by corrupting sensitive data like the intermediate representation of optimizing JIT compilers. This has inspired numerous defenses for just-in-time compilers.Our work demonstrates that securing JIT compilation is not sufficient. First, we present a proof-of-concept data-only attack against a recent version of Mozilla's SpiderMonkey JIT in which the attacker only corrupts heap objects to successfully issue a system call from within bytecode execution at run time. Previous work assumed that bytecode execution is safe by construction since interpreters only allow a narrow set of benign instructions and bytecode is always checked for validity before execution. We show that this does not prevent malicious code execution in practice. Second, we design a novel defense, dubbed NOJITSU to protect complex, real-world scripting engines from data-only attacks against interpreted code.The key idea behind our defense is to enable fine-grained memory access control for individual memory regions based on their roles throughout the JavaScript lifecycle. For this we combine automated analysis, instrumentation, compartmentalization, and Intel's Memory-Protection Keys to secure SpiderMonkey against existing and newly synthesized attacks. We implement and thoroughly test our implementation using a number of real-world scenarios as well as standard benchmarks. We show that NOJITSU successfully thwarts code-reuse as well as data-only attacks against any part of the scripting engine while offering a modest run-time overhead of only 5%.



Post a comment

Related work

Conference:  Black Hat Asia 2023
Authors: Simon Scannell, Valentina Palmiotti, Juan José López Jaimez

Conference:  Defcon 31
Authors: Jonathan Birch Principal Security Software Engineer, Microsoft

Conference:  Black Hat Asia 2023
Authors: Gabriel Landau