<story><title>Exploiting the Math.expm1 typing bug in V8</title><url>https://abiondo.me/2019/01/02/exploiting-math-expm1-v8/</url></story><parent_chain><item><author>titzer</author><text>Hi, TLM of the WebAssembly runtime in V8 here.&lt;p&gt;TLDR: it&amp;#x27;s asm.js&amp;#x27;s fault. And yes, complexity.&lt;p&gt;The reason that WebAssembly JIT code memory is still RMW (for now) is actually really unfortunate. As you might know, V8&amp;#x27;s JIT code memory for JS is only writable when the application is quiesced (i.e. JS is not running) and the JIT is either finishing a function or the garbage collector is moving JITted code. It&amp;#x27;s read-execute otherwise. It&amp;#x27;s never both writable and executable at the same time. We generally refer to this as WX protection (i.e. writeable&amp;#x2F;executable exclusive).&lt;p&gt;In the case of WebAssembly, it&amp;#x27;s asm.js that&amp;#x27;s the real culprit here. Internally in V8, asm.js code is translated to WebAssembly by a very quick custom-built parser that validates asm.js&amp;#x27;s type system while parsing and emits WebAssembly bytecode along the way. The WebAssembly bytecode is then fed to the WebAssembly engine for compilation.&lt;p&gt;Well...not so fast. In order to meet our performance goals for fast asm.js validation and startup, the WebAssembly engine does not do up-front compilation of Wasm code coming from asm.js. Instead, it does on-demand compilation of asm.js code, compiling the Wasm bytecode corresponding to each asm.js function upon first execution. We call this &amp;quot;lazy compilation&amp;quot;.&lt;p&gt;We originally shipped lazy compilation cooperating with the WX mechanism executable for JS code. That is, upon every (lazy) compilation of asm.js code, we&amp;#x27;d flip the permission bits on the code space in order to write in the little bit of machine code generated for each function. Problem is, that permission flip is expensive--like really expensive. So expensive that we had to unship WX protection because it made asm.js code unusably slow.&lt;p&gt;We&amp;#x27;re working on fixing this, as we are keenly aware of the risk exposure here.</text></item><item><author>wahern</author><text>Interesting how at the end, after acquiring out-of-bounds write access, that it was easiest to leverage the WebAssembly infrastructure to execute code than to build a ROP chain.&lt;p&gt;Apparently WebAssembly heap memory storing generated code is not write protected &lt;i&gt;at&lt;/i&gt; &lt;i&gt;all&lt;/i&gt;. I guess whatever architecture they have for managing typed memory chunks doesn&amp;#x27;t make it sufficiently easy to manipulate protection bits dynamically, and the WebAssembly folks were content to leave compiled WebAssembly chunks are RWX.&lt;p&gt;It&amp;#x27;s unfortunate that this thread hasn&amp;#x27;t been upvoted. It really drives home the futility of expecting something as complex as V8 to ever be safe enough to sandbox arbitrary code. And it has little to do with the language of the implementation--the same engine re-written in Rust would have been just as susceptible to the two exploits.&lt;p&gt;I guess it&amp;#x27;s just too inconvenient to accept reality. I&amp;#x27;m still surprised that Cloudflare and AWS have actually convinced themselves they can make such architectures secure[1]. Less shocked that everybody is else is so credulous.&lt;p&gt;Though, I &lt;i&gt;am&lt;/i&gt; shocked that Netflix is so credulous. A recent blog post described how they use AWS Lambda functions to manage their CA private key. But, again, because of how limited KMS is I guess it&amp;#x27;s too inconvenient to not trust Lambda. KMS really should support asymmetric key operations; it&amp;#x27;s ridiculous it&amp;#x27;s still not supported. I guess AWS&amp;#x27;s KMS &amp;quot;cloud HSM&amp;quot; solution just wouldn&amp;#x27;t scale (in terms of CPU) if people could do that.&lt;p&gt;[1] Absolute security is impossible, but as complex as this exploit was it&amp;#x27;s obviously still far too trivial. It&amp;#x27;s a totally unwarranted and unreasonable expectation that bad actors are incapable of reading (if not manipulate) co-hosted Lambda or Cloudflare Worker projects. Writing a non-JIT&amp;#x27;d engine with a simplified memory architecture is not only feasible, it would be an obvious candidate for formal verification. But the demands for performance are just too strong and so, as always, security takes a back-seat to performance and speed-to-market.</text></item></parent_chain><comment><author>abiondo</author><text>Author here - thanks for this! I was wondering why Wasm was RWX.&lt;p&gt;My 2c: I think that there will be risk of code injection as long as write_protect_code_memory in Heap is writable. Changing that flag will usually mess things up and crash writing to RX memory (CodeSpaceMemoryModificationScope won&amp;#x27;t switch to RW), but a well-crafted exploit might be able to get a fresh executable MemoryChunk (which will now be RWX). It&amp;#x27;s likely complex to exploit, but the incentive is that code injection is more reliable than ROP when targeting multiple builds (unless you build the chain dynamically, which is slow and often painful).</text></comment>
<story><title>Exploiting the Math.expm1 typing bug in V8</title><url>https://abiondo.me/2019/01/02/exploiting-math-expm1-v8/</url></story><parent_chain><item><author>titzer</author><text>Hi, TLM of the WebAssembly runtime in V8 here.&lt;p&gt;TLDR: it&amp;#x27;s asm.js&amp;#x27;s fault. And yes, complexity.&lt;p&gt;The reason that WebAssembly JIT code memory is still RMW (for now) is actually really unfortunate. As you might know, V8&amp;#x27;s JIT code memory for JS is only writable when the application is quiesced (i.e. JS is not running) and the JIT is either finishing a function or the garbage collector is moving JITted code. It&amp;#x27;s read-execute otherwise. It&amp;#x27;s never both writable and executable at the same time. We generally refer to this as WX protection (i.e. writeable&amp;#x2F;executable exclusive).&lt;p&gt;In the case of WebAssembly, it&amp;#x27;s asm.js that&amp;#x27;s the real culprit here. Internally in V8, asm.js code is translated to WebAssembly by a very quick custom-built parser that validates asm.js&amp;#x27;s type system while parsing and emits WebAssembly bytecode along the way. The WebAssembly bytecode is then fed to the WebAssembly engine for compilation.&lt;p&gt;Well...not so fast. In order to meet our performance goals for fast asm.js validation and startup, the WebAssembly engine does not do up-front compilation of Wasm code coming from asm.js. Instead, it does on-demand compilation of asm.js code, compiling the Wasm bytecode corresponding to each asm.js function upon first execution. We call this &amp;quot;lazy compilation&amp;quot;.&lt;p&gt;We originally shipped lazy compilation cooperating with the WX mechanism executable for JS code. That is, upon every (lazy) compilation of asm.js code, we&amp;#x27;d flip the permission bits on the code space in order to write in the little bit of machine code generated for each function. Problem is, that permission flip is expensive--like really expensive. So expensive that we had to unship WX protection because it made asm.js code unusably slow.&lt;p&gt;We&amp;#x27;re working on fixing this, as we are keenly aware of the risk exposure here.</text></item><item><author>wahern</author><text>Interesting how at the end, after acquiring out-of-bounds write access, that it was easiest to leverage the WebAssembly infrastructure to execute code than to build a ROP chain.&lt;p&gt;Apparently WebAssembly heap memory storing generated code is not write protected &lt;i&gt;at&lt;/i&gt; &lt;i&gt;all&lt;/i&gt;. I guess whatever architecture they have for managing typed memory chunks doesn&amp;#x27;t make it sufficiently easy to manipulate protection bits dynamically, and the WebAssembly folks were content to leave compiled WebAssembly chunks are RWX.&lt;p&gt;It&amp;#x27;s unfortunate that this thread hasn&amp;#x27;t been upvoted. It really drives home the futility of expecting something as complex as V8 to ever be safe enough to sandbox arbitrary code. And it has little to do with the language of the implementation--the same engine re-written in Rust would have been just as susceptible to the two exploits.&lt;p&gt;I guess it&amp;#x27;s just too inconvenient to accept reality. I&amp;#x27;m still surprised that Cloudflare and AWS have actually convinced themselves they can make such architectures secure[1]. Less shocked that everybody is else is so credulous.&lt;p&gt;Though, I &lt;i&gt;am&lt;/i&gt; shocked that Netflix is so credulous. A recent blog post described how they use AWS Lambda functions to manage their CA private key. But, again, because of how limited KMS is I guess it&amp;#x27;s too inconvenient to not trust Lambda. KMS really should support asymmetric key operations; it&amp;#x27;s ridiculous it&amp;#x27;s still not supported. I guess AWS&amp;#x27;s KMS &amp;quot;cloud HSM&amp;quot; solution just wouldn&amp;#x27;t scale (in terms of CPU) if people could do that.&lt;p&gt;[1] Absolute security is impossible, but as complex as this exploit was it&amp;#x27;s obviously still far too trivial. It&amp;#x27;s a totally unwarranted and unreasonable expectation that bad actors are incapable of reading (if not manipulate) co-hosted Lambda or Cloudflare Worker projects. Writing a non-JIT&amp;#x27;d engine with a simplified memory architecture is not only feasible, it would be an obvious candidate for formal verification. But the demands for performance are just too strong and so, as always, security takes a back-seat to performance and speed-to-market.</text></item></parent_chain><comment><author>tptacek</author><text>This is super-interesting back story, thanks for taking the time to share it.</text></comment>