← All talks

Attacking V8

BSides Canberra41:351.0K viewsPublished 2024-10Watch on YouTube ↗
Show transcript [en]

hope you had a good lunch Hopefully the rain wasn't too bad as well outside uh we have a great talk now on attacking V8 by Aon let's welcome a welcome aan to the

stage hello everyone hope you had a good lunch thanks for coming to this talk my name is aan I'm a senior consultant at at Cyber CX I'm based in New South Wales been there for almost the past decade I presented last year in bide Sydney much smaller conference than this one on ransomware development and this is my first time to present here in Cambra I've been attending bides Cambra since 2016 but it's my first time to present I started my infoset career as a pentester but I also enjoy exploit development and reversing and malware and F research I have a couple of cves and a few exploits that I wrote in the past today I'm here to talk about a

research I did last year in my own time this is not what I do during my day job long story short uh CVA came out for a vulnerability in Google Chrome with with reports of it being exploited in the wild so I had some confidence that this is an exploitable vulnerability and decided to study it because why not right this presentation is to document and share with you the Journey of going through this research it's going to be detailed technical presentation there will be JavaScript if you're a front Ed developer you may like it or not because the scripts don't look like they make much sense but they will when we examine how they are processed

internally it's okay if you didn't follow along with every single slide I will be doing Recaps with key takeaways every while first a childhood story when I was a little kid I enjoyed playing video games and I was always fascinated with game trainers or cheats and always wondered how do they work on my old computer I came across a cheat engine that lets you create custom cheats basically what it does is it searches and edits the memory of the game process and it worked like this I run my game and let's say I have three initial lives I switched to the cheat software and searched the gamees memory for the value of three it will find several results I

go back to the game and kill the character so the lives count decrements to two I switch back to the cheat software and search within the previous results for a value that has changed from 3 to two this will narrow down the results within the memory space rinse and repeat until you find the memory location of the desired V desired variable be it lives or AMO or whatever and set it to arbitary Value like 99 or something and then I play the game this could be one of my earliest experiences with computer hacking which I like to Define as manipulating computers or more specifically software to behave in a way that was not intended by its original

developers and exploit that to our advantage So today we're going to talk about a modern software much more complex than those games and that is V8 not the car engine we're talking about Chrome V8 not that either so what is it V8 is a JavaScript engine created by Google and it Powers Google Chrome and not GS what's a JavaScript engine anyways here are two sets of programming languages what do you think each set have in common languages on the left are compiled while the ones on the right are interpreted which are slower but more flexible than compiled languages where would JavaScript f it looks like an interpreted language right we do not compile it into a binary

executable as we do in C instead we execute it from within a browser or another runtime environment like not GS or electron so is va8 The Interpreter that that runs JavaScript well it's a lot more than that long time ago JavaScript was used in small scripts to perform simple tasks like form validation then with the rise with complex applications like Google Maps interpreting JavaScript became too slow slow and there was an essential need to speed up execution the JavaScript engine is the runtime environment that enables browsers to execute JavaScript but also in an optimal and efficient way it employs just in time compilation and various optimization techniques to improve the the performance of JavaScript execution this is a list of different

JavaScript engines V8 is Google's JavaScript engine it's the one used in Chrome nodejs and M moft Edge spider monkey is what Firefox uses chakra was an engine developed by Microsoft and used in Legacy Edge Bowser before it got Rewritten upon chromium jscript was the old engine used in the old Internet Explorer browsers and there are other lightweight engines that can be used in embed systems this is how V8 executes a JavaScript first the script code is parsed by a parser that produces an abstract syntax tree which is a representation of the script in the form of a tree data structure looks like this the abstract syntax 3 is then fed into a component in V8 called Ignition

which produces a bite code that is a machine independent virtual machine code similar to Java bite code or net Intermediate Language ignition executes or interprets the bite code and as the bite code runs V8 monitors the execution collects information and made decide to optimize some code by throwing it to another component called turbofan which is an optimizing compiler that takes the bite code and compiles it into faster native machine code occasionally the optimized code may become invalid and V8 will need to fall back de optimize and then reoptimize again this cycle goes on as the JavaScript executes ignition and turbofan is the modern execution pipeline in V8 previously they had different components named full code gen

and crankshaft and at some point they had a mixture of both pipelines together and they called it Franken pipeline which was very convoluted full code genen was the Baseline compiler and compiles JavaScript into native unoptimized code and crankshaft was the old optimizing compiler but these are now replaced with ignition and turbofan V8 also introduced a mid tier compiler called spark plug and that is it's another compiler that sits in between ignition and turbofan it produces non-optimized native code this can be useful because optimization itself is an intensive process and we also need to collect enough information before even deciding to optimize the code ultim ultimately the goal is to achieve high performance JavaScript execution ra rather than producing the

most optimal code so spark plug is a middle ground between the slower by code interpreted by ignition and the heavily optimized code produced by turbofan in this talk we are only concerned with a few of these components just know that ignition interprets bite code produced from the JavaScript source code and turbofan is the optimizing compiler that converts bite code into native machine code to demonstrate ignition let's read this simple function it takes three arguments a0 A1 A2 and T some arbitrary processing on them we add the first two in a local variable X decrement the third argument and store the result in a second local variable Y and then return the product of these two

variables now here's the equivalent ignition bite code of this function let's step through its instructions first instruction load accumulator which loads the second argument A1 into the accumulator register we add the first argument a0 to the value stored in the accumulator register store the accumulator register into r0o register load argument 3 into the accumulator subtract a small integer of value one from the value of the accumulator store the accumulator into R1 load register i z in the accumulator register multiply the value stored in R1 times the value stored in the accumulator storing the result in the accumulator and finally return the value in the accumulator register the takeaway here is that this is an internal virtual machine a

register based VM inside ignition inside V8 sorry and ignition is The Interpreter that executes our JavaScript in the form of these bite code instructions if we call a function enough times it becomes hot and to cool it down V8 sends it to turbofan the optimizing compiler which produces a native machine code shown on this slide let's also read the assembly code no I'm just kidding we're not doing that okay with that brief background let's get into the vulnerability we're researching and we'll introduce further Theory as we go from the N website it says there's a type confusion in V8 that allows potential exploitation of Heap corruption brief Interruption about type confusion you may have seen this meme

before an interviewer asks a question what's 2 plus2 the interview responds with 22 surprisingly the interviewer acknowledges the answer not very surprising to us it's obvious from the quotes that we're talking about strings or characters but the point is the type of data is a very important context as it determines how will the computer process them addition of value of two as an integer is going to be processed through an arithmetic operation while adding two as characters will be string concatenation back to our advisory it lists some external reference links one of them is the chromium bu tracker CR bug.com this link lists further details and even provides a proof of concept code that triggers the bug which is very

useful it states something about leaking the whole value at the time I was researching this this page was not public yet which would have been around Q3 last year that's why also on the next page it says permission required for this page resource so let's pretend we haven't seen it for now what we can see in the URL is a bu tracking ID 1450 481 V8 is an open source project we can look up this ID in the source reple and we find some merge requests one of them has been merged into the main branch looking at this merge request we get a nice diff between the old code and the patch the patch added checks for some

conditions if the receiver is JS arguments object and if it has fast packed elements if these conditions are met the store fast element built in is called with the standard store for the store mode argument that's a lot of information slowly but first we need to set up our Dev research environment we need to get our hands on a vulnerable version unpatched version of the software we're researching referring to the advisory it states V8 in Google Chrome prior to some specific version number we want to find an earlier version number unfortunately for us Google does not allow download of alter software there are third party archives but we cannot trust those also remember that we

are only concerned with the V8 engine rather than the entire browser Google Blog shows release history where we can look at previous version numbers let's pick one earlier than the patch update there used to be a tool online tool called Omaha proxy that I used to find which V8 commit is associated with a particular Chrome version this tool is no longer available but there is a current alternative called chromium Dash and it does the same thing we can go to chromium Dash look for our desired Chrome version and find the V8 commit from there now that we know the commit that we want we start setting up our environment to download and build the V8

from Source we use the doot tools published by Google follow this procedure to clone the repo and roll back to the unpatched V8 commit after checking out the old commit we should verify that we actually have the old code by looking at the effect files just a sanity check and we find that our code indeed lacks those newly added checks we continue to build the repo it's going to take a while to compile maybe an hour or two if I remember correctly after compiling V8 how are we going to use it I initially thought that I would need to link V8 against a test program or something to start using it but luckily V8 comes with a great tool

called d8 which is immensely useful it's a shell where you can execute JavaScript as you do in the web browser developer tools you can also pass it as script to execute or ex execute a script then drop in a shell d8 also supports powerful commands known as the native syntax that we can use inside JavaScript source to greatly assist with debugging for example we can debug print a variable or an object or a function to print debugging information and this is how I got all these screenshots on the slides we can also break execution while running d8 under debugger and many other commands you can run d8 minus minus help to get a list of

different switches with V8 compiled we now can start our analysis first we want to understand what the vulnerable code does and how do we reach it we see that the patch is in a function called store element Handler in the key store IC class and this code is in a source file called ic. CC IC is short for inline cache quick recap we have V8 an interpreter ignition whose job is to quickly turn the source code into unoptimized bite code AS V8 excuses the bite code it keeps collecting profiling data when a function gets called many times it becomes hot to cool it down V8 sends it along with the profiling data to turbofan the optimizing compiler

which produces optimized native machine code for faster future execution inline cache is one of these optimization mechanisms our bug has something to do with it with the with the inline cach so we would like to activate it but first let's quickly glance over some more theory in a programming language like C or C++ type checking occurs at compile time when we declare a variable we must also Define its data type for example the C code on the left of the screen deare declares a variable named X and also specifies its data type of integer this is called strong typing whereas a dynamic language like JavaScript allows the program to start using variables and assign them arbitrary data without

having to worry or specify their type this is much easier and flexible for the developer but it offloads additional work to the JavaScript on time it needs to make assumptions about the data type and needs to get it right and should also be able to change those assumptions when needed if it makes the wrong assumption security implications could ensue which what happens in type confusion vulnerabilities C++ also requires the programmer to Define classes before they can instantiate objects from them but JavaScript allows the programmer to dynamically create objects and add members to them on the Fly JavaScript abstracts class definitions away from the developer and to be able to do that V8 maintains a hidden class for objects that the

developer creates these hidden classes are also called shapes and they Define the shape of the object that is its members and their orders or offsets multiple objects can reference the same hidden class or shape if they have the same shape this shape can transition dynamically as we add more members also known as properties to the objects unlike C++ where all the members have to be predefined in the class when we access an object property V8 we need to perform extensive lookup operations as it walks the shape transition chain and finds the property location or offset in order to reduce similar future lookup operations and improve performance V8 will keep a record of for how it previously accessed that property

so that when it encounters another lookup of a similar shape it can take a shortcut from the cached information and Skip another entire lookup this process is the inline Clash we mentioned earlier in the patched file ic. CC let's take an example to see inline clash in action here we have a function IC it takes an object and returns 10 plus X property of that object we call this function numerous times with an object argument here's the bite code of the function first get named property of argument zero then add a small integer 10 and return the value notice these numbers in Brackets these are slots in the feedback Vector which contain the cached information we just talked

about slot zero is for the small integer slot one informs V8 how to load the property X of our object and note that it says monomorphic this is the initial state of a of a feedback vector and it means that V8 has only seen one object shap shape passed as an argument for this function so far if we call the function again but this time pass it another object with a different shape the new shape has an additional property y this time this time V8 finds that the object shape does not match what it has seen before in validating the cached information it performs another slow lookup for the new shape and updates the feedback

Vector now we can we see that the feedback Vector slot has changed to polymorphic Which is less ideal in terms of performance than the previous monomorphic state now if we keep changing the shape of the object we pass to the function for example here we change the members of the object argument a b c and so on eventually the feedback Vector transitions to a Megam morphic state which is the least optimal basically V8 say says I've seen too many shapes now maybe there's no point optimizing this anymore so I'm just going to give up on tracking this any further okay so our source code has something to do with the inline cach what else do we need to know to be able

to reach the vulnerable function the function name is store element Handler and there's something about key store what is that here we have an array we store some values at numeric based indices these are called elements of the array we can also store values at non-numeric indices as in dictionaries or associative arrays in V8 these are called named properties here is a the back print of our array showing all the elements and the properties elements 1 to 3 at index locations 0 1 and 2 we also see properties named a b and c with the values 61 62 and 63 and while we're talking about arrays let's also discuss their types remember we said that V8 maintains a hidden class

or shape for objects these are also called Maps looking at the map of this array it says packed SMI SMI stands for small integers meaning that the contents of this array are only of type small integers if we add a double decimal value to the array we find that the shape has transitioned from SMI to double elements and if we add another object to the array it transitions to a more generic type elements array okay what does packed mean packed means that the array contains contigous or adjacent elements stored next to each other comparing languag again in a language like C we can only Access Data within the defined bounds of the array in the C code on the left we have an

array of three elements therefore we cannot access or assign value at the fifth index but in JavaScript we can what V8 will do is we'll Mark the fourth index of the array as a whole between the data now we have enough idea about the inline cache and other V8 internals going back to the vulnerable function let's start writing a proof of concept that triggers the bug we first want to want to reach the vulnerable code here's a JavaScript function f stores an element in an array we keep calling this function repeatedly up to 100 times let's run it and watch if inline cache kicks in and reach the desired code we run the8 under the GTB debugger and set a break point

in our Target file and indeed it hits our break point after 10 iterations of this Loop great we are able to reach the interesting function next we want to write a test case that calls the function with the specific conditions that were patched back to the diff the patch checks if the object is of type GS arguments and that it has fast packed elements in which case it sets sets the store mode to standard store in our test case we want to satisfy these conditions to trigger the bug we now know that packed elements means there are no holes in the array object what about the other conditions JS arguments is simply a special object available in every

function that contains the arguments passed to this function in this example argument zero is the first argument to the function argument one is the second Etc what about store mode the optimized code that inline cach will produce is actually one of C C++ functions that are baked in V8 and known as builtins the specified store mode decides which builtin is going to be used to optimize storage operation of that object the patch forces the use of standard store so we can guess that the bug must be present in one of the other cases let's explore the different store modes here we have a function that stores the value of the second argument in index one of an array passed in the

first argument we call this function 10 times to Prime the inline cache and then inspect the generated built-in using the debug print command we find that the standard store mode is used and here is it under the debugger now let's tweak the function to trigger the other store modes in this version We increment the index of where to store the value effectively expanding the array with each iteration this time we get another builtin store and grow no transition copy on right so with each storage operation the built-in function makes a copy of the object in a larger buffer because we grow the array note that it also says no transition meaning that it is not going to transition or

change the shape of the object into a different one as according to the feedback Vector we always pass an array to this function we haven't called it with any other object type yet another store mode similar to the previous one except that it doesn't grow the array in order to reach it I had to perform three consecutive store operations first one passing an array of integers packed SMI we find that the standard store is used next I call the function again passing doubles decimal values the previous array of small integers cannot hold doubles so now the array type has transitioned into a more generic type packed double Elements which can handle both smis and double we

still get the standard store built in again we keep calling this function changing its content further more to characters or strings these data cannot be represented by neither SMI nor doubles so the array transitions into packed Elements which is the most generic elements kind that can handle other values than SMI or doubles throughout this script the array has transitioned from the specific integer SMI to the most generic elements kind such transitions can only go in one direction from a specific element kind to a more generic one it does not transition back to the specific kind like integers again so this time we get this store mode that says no transition indicating that the array object we're storing into

is not going to transition to a different shape the last store mode is ignore out of bound which initially sound like the most interesting one to me I was able to trigger it with something called typed arrays which are basically row binary memory buffers they have fixed length and specific data types as in this example we Define a typed array of length one for an unsigned short integers when we attempt to write beyond the this arrays length nothing happens and the length Remains the Same hence when inline cache optimizes this function it uses the ignored outof bound built in not that interesting after all so it took me a while to write all these test cases and study V8 and figure

out what's happening in the background while I was doing that an exploit got published I got very excited I ran it and it crashed but it gave me a good nudge into the right direction consider this array with three elements 0 1 and two it's packed because the array the elements in the array are contous contigous if we add an element to the array after a gap here we store a value at the fifth location skipping the fourth the array transitions from packed to Holy type and we see that V8 stores a special value called the hole to indicate holes within the array now here's the problem we're finally going to write the test case

that triggers the bug starting with a similar function that takes an array or object and stores some value at some index all specified in the function argument we add a second function doesn't do anything yet but it gives us access to the JS arguments object which is available in every function the arguments object is empty because there are no arguments that we passed to the main function just note that this is a JS arguments object its map is packed elements and its length is zero we pass the argument object to our function call it 10 10 times to trigger the inline cache to optimize the function in our function we store a dummy value in a property of the array

called B sides remember that unlike numeric based elements properties are named indices as in dictionaries or associative arrays we do that because this storage doesn't grow the arguments length it still has it still has no elements and it its length remains zero now the function has a monomorphic feedback vector it has been optimized for a single object shape of packed Elements which is our GS arguments object next we declare an array and pass it to our function but we write Beyond its length V8 notices that this array has a different shape than the JS arguments object previously cached in the feedback Vector it updates the feedback Vector to become polymorphic and it changes the built in

to grow no transition this is the store mode we saw earlier when we extended an array by writing Beyond its length and it uses this builtin for both object shapes the packed SMI Elements which is the secondary we are growing and also the packed Elements which is the JS arguments object we first used now here's where the bug happens in this state if we pass the arguments object to the store function again and dra it Beyond its length as we did in the other SMI array the grow no transition built-in is going to be called it will grow the arguments object without changing its map or shape because no transition and this is a problem because

now we have a packed elements object that contains holes at the bottom of bottom of the screenshot you can see element zero which is the value that we wrote and V8 grew or extended the object by placing whole values at the end but it did not change the object shape from packed to Holy as it should have so now our script can read the value of the hole by accessing it from the arguments object V8 will return it to us as we are reading from a packed element object effectively leaking the whole value to the end user now what's the implication of this the hole is an internal value it is not meant to be

known or accessible to the end user leakage of the hole has been exploited multiple times in the past to achieve code execution in V8 getting fixed every time there's now a proposal to not make such a leak a security risk in the future if you search up V8 whole exploit for example you will find multiple writeups and techniques that explain them including this bug all of which have been fixed by now okay now let's see how can we turn this info leak of the hole into code execution when turbofan optimizes the function it makes some assumptions and speculations we can trace them using d8 let's quickly inspect the optimization of our script before looking at how the

whole leak the whole leak was exploited we run our script through d8 with a couple of flags the first flag will produce a log of turbofan optimization steps that we can read or grab through and the second one will produce a Json output file that we can use to visualize using a tool called turer that comes with V8 and it looks like this after loading the files we get this view where we have the JavaScript source code on the left assembly on the right and in the middle we get a c of nodes graph which is a representation of the JavaScript that can show both the control and the data flow of the program zooming into the graph we find the

beginning of our Loop watch the node highlighted in yellow the loop variable is checked if it is Le less than a constant of 30,000 which is the number of iterations if not we go through the false branch and exit if true we load the key property of our object and store the value of one also notice that turbofan has inlined our function instead of calling it as part of its optimization finally we increment the loop variable and continue to iterate as you can see talizer is a powerful tool to visualize the control flow of the script and also inspect the data and their types now consider this function f takes a Boolean argument if the argument is true we

return not a number n if false we return minus one very simple we'll add a slight change bitwise or with zero n or 0 equal Z so if we call it with true we get back zero one more minus change we add one to the result so if we call this function with true it returns one if we call it false it returns minus one let's send send this function to turbofan and analyze it we can do that by calling it many times or we can use the native syntax using d8 okay here's our graph we first have a node of potential values for n either not a number n or minus one the node

highlighted in yellow top one we see that in the data types of the nodes we perform bitwise or and increment operations ultimately resulting in either the value of zero or one depending on whether we call the function with true or false so far so good now here comes the trick if we replace n in this function with the whole value side note there's also a native syntax to retrieve the whole so if you want to test it out you don't actually require uh INF Le vulnerability you can just use d8 but of course you cannot do that in in a real web browser okay replace Nan with the whole similarly in the graph we have potential

values of either the whole or minus one but this time turbofan thinks that the output of the number function is always going to be minus one there was no case to handle the the whole value because this was not an ever expected scenario here's the previous graph from the N function where V8 correctly identified both cases of not a number or minus one for the variable now the turbofan in incorrectly predicts or assumes that this function is always going to return zero whether we call it with true or false which is wrong because if we call it with true we actually the return value is going to be one not zero let let's try to use this

incorrectly optimized value as an index to access an an array here we declare an array of four doubles the last element should be at index 3 but we attempt to read at index 4 while turbofan thinks we are reading at zero because it's speculated that n is going to be always zero however it is one it's going to be one if we call the function with true we run this doesn't work we Trace turban we find that it has added some bounce checking when accessing the array the bounce checking ensures that we only read from index zero so we need to work around that the at method is an alternate way to access array elements

it was created to allow accessing the last element using minus one index as in other languages like python using the at method makes turbofan skips the balance checking step this was actually another bug reported a while back and only got fixed last year so we use the at method to read a value just after our array like this we get an arbitrary value from memory converted to a double because our array is of type double next we're going to develop this outof bound read primitive into further ones that allows to perform arbit read write within the memory space if you recall today from the morning keynote exploit Primitives building this is what we're doing next to do that we need to

format a certain layout in memory if we Define three arrays like that the first one contains some values and a second similar one and the third array contains both arrays each array will be placed in memory in this order elements of the array at the top followed by the arrays metadata that it's its map and length Etc the consecutive arrays will look like that elements of the array followed by metadata of the next array and so on we can inspect the memory under the debugger and compare it to the debug print output this is our first array here we can see the metad data the first array starting at the map address and then the

properties and the elements and the length the reason length reads eight in memory instead of four is that the least significant bit is not used for data in V8 V8 uses it to distinguish between pointers and small integers so small integers are left shifted by one so four left shift one is eight this is also the reason why you see I subtract minus one from the pointer when examining memory this is is a feature in V8 called pointer compression you can read about it further if you're interested and here are the elements of the array also all left shifted by one now we're going to use our out of bound read to leak the metadata of the first

array and copy it into the elements of the second array to create a structure that resembles an array header using this forged array we'll be able to access the data below like elements of array 3 but the difference between accessing the elements through that fake array or normally through array 3 is that the shape between both arrays is different array three is an array of elements when we access them V8 will dreference the pointers and retrieve the destination element object while the fake array that we control we can set its shape to be an array of doubles this way V8 will interpret these pointers as double values and return them without D referencing this is type confusion again

the type of or the shape of the object determines how it is processed the fake array is going to be useful in two ways first we can leak addresses of arbitary objects by placing them in Array three and reading the pointer through the fake array conversely we can craft fake objects by storing a pointer in the fake array then attempting to dreference it through array three these are two common Primitives in JavaScript exploits to achieve arbitary read write which what we're going to do next if we increase the the fake arrays length by editing our forged header we can extend the array and continue reading further objects Beyond array three this is nice but we would like to

have more powerful arbit arbitary redri Primitives instead of being confined to adjacent objects remember typed arrays that we mentioned earlier they allow us to access binary memory buffers through array like objects let's create a typed array and fill it with some values 41s and inspect it under the debugger we see the debu print data pointer which is self-explanatory where we expect to see the typed array contents and indeed this is where our 41s are stored so if we have a nearby typed array we can use our fake array to overwrite the data pointer to an address of our choosing and then perform arbitary read write next we we use that to achieve code execution when

turbofan optimizes a function it compiles it into an executable memory region take a look at this function doesn't do anything it only returns an array of doubles let's call it many times to trigger turbofan optimization and inspect the compiled function here's the Deb print it shows the turbofan generated code and also the source code of the function looking at the turbo fan code we find the address of the actual assembly like this if we disassemble at this address we eventually find our double values so what we're going to do is encode our shell code in similar double values and place little jumps in between to execute the Shell Code there are scrip scripts that can do

that for you a shell code to execute benus H will look like this here are our double values after being compiled with turbofan if we start executing from the right offset within this function our Shell Code manifests like this execute a little bit followed by a jump execute the next bit and so on all what we need to do now is get turbofan to compile our our F our function then we use our arbitrary right primitive to overwrite the instruction start address of the optimized function to the start of our Shell Code and then call the function finally here's the recap of our exploit we first leaked the hole we used the hole to make an outof

bounds read we crafted a fake array object that allows us to modify adjacent locations we used a nearby typed array to achieve a more arbitary rri primitive by modifying the data pointer we encoded our Shell Code in an array of double values we use turbofan to optimize the Shi code and write it in an executable location overr the instruction start address of turbofan code to point to the beginning of our encoded Shell Code which is going to be a known offset within within the function and finally we execute the code now I had a live demo but but unfortunately due to AV issues I cannot show it but I do have a recording which is going to be very brief hopefully you

can see this oops sorry there we go around the8 this is its version it's an old older version then I run the exploit boom drops into a shell [Applause] if you're interested in similar work you can set up a similar playground environment get an exploit try to fix it look at previous V8 exploits and study them you can also search the chromium Bo tracker for pox and play with them here's a list of uh links that helped me during this research thank you all for sitting through and I would also like to specifically thank Kylie and Sylvio for organizing bsides thank you