We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

From Zero to Zero Day

00:00

Formal Metadata

Title
From Zero to Zero Day
Title of Series
Number of Parts
165
Author
License
CC Attribution 4.0 International:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
In this talk I will share my story of how in a little over a year, a high school student with almost zero knowledge in security research found his first RCE in Edge.
Keywords
Semiconductor memoryGoodness of fitDivision (mathematics)Level (video gaming)Musical ensembleComputer animation
MathematicsVulnerability (computing)Information securityPerfect graphDemo (music)Magnetic stripe cardMultiplication signInformation securityPerfect graphMereologyComputer programmingType theoryJust-in-Time-CompilerLecture/ConferenceMeeting/InterviewXMLComputer animation
Personal digital assistantVulnerability (computing)Continuous functionSelf-organizationOperations researchInformation securitySystem programmingInformationLocal GroupMilitary operationData managementImplementationDirected graphPhysical systemVulnerability (computing)Demo (music)CodeSoftware developerQuicksortProcess (computing)Lecture/ConferenceComputer animation
Operations researchContinuous functionSelf-organizationVulnerability (computing)System programmingInformation securityInformationLocal GroupMilitary operationImplementationData managementDirected graphPhysical systemGUI widgetOpen setDivisorMathematical analysisGroup actionComputer programmingFormal languageSolid geometrySoftware developerExecution unitGroup actionQuicksortComputer programmingVulnerability (computing)DataflowSoftware developerProgramming languageCodeGraph (mathematics)Goodness of fitLevel (video gaming)XMLLecture/ConferenceComputer animation
MereologyData bufferBuffer overflowLocal GroupMemory managementWritingVulnerability (computing)Classical physicsGame theoryInformation securityMultiplication signBuffer overflowIntegerStack (abstract data type)Buffer solutionMeeting/InterviewComputer animation
FlagWater vaporMobile appInformation securityMeeting/Interview
MereologyMultiplication signTwitterVideo gameBuffer overflowComputer animationLecture/ConferenceMeeting/Interview
MereologyDifferent (Kate Ryan album)Video gameInformation securityYouTubeVulnerability (computing)Water vaporVideoconferencingGame theoryGraph (mathematics)Pattern languageComputer animationXML
CodeVulnerability (computing)Military baseVideo gameConnectivity (graph theory)Exploit (computer security)Software bugProjective planeOptical disc driveReal numberWebsiteLecture/ConferenceMeeting/Interview
Vulnerability (computing)Vulnerability (computing)Process (computing)CodeSoftware bugMeeting/InterviewComputer animationLecture/Conference
MereologyVulnerability (computing)Type theoryCodeError messageoutputVulnerability (computing)Pattern languageCovering spaceSoftware bugMultiplication signBuffer overflowIntegerLine (geometry)CodeSoftware developerMeeting/InterviewComputer animation
MereologyGraph (mathematics)Vulnerability (computing)State of matterCodeForceVulnerability (computing)Software developerExistenceMilitary base1 (number)Software bugDifferent (Kate Ryan album)Buffer overflowState of matterSet (mathematics)Address spacePrimitive (album)Pointer (computer programming)Functional (mathematics)Buffer solutionJSONXMLComputer animation
Scripting languageJava appletCodeParsingInterpreter (computing)Run time (program lifecycle phase)CompilerJust-in-Time-CompilerVulnerability (computing)Primitive (album)Information securityJust-in-Time-CompilerCodeFunctional (mathematics)Machine codeProcess (computing)Formal languageSoftware developerProgramming languageCompilation albumMultiplication signMereologyCompilerComputer programmingReading (process)Meeting/InterviewComputer animation
Formal languageLetterpress printingVideo game consoleScripting languageJava appletProxy serverObject (grammar)PrototypeInheritance (object-oriented programming)LogarithmOperations researchSystem callFunction (mathematics)IntegerElement (mathematics)Data conversionVulnerability (computing)Just-in-Time-CompilerMathematical optimizationJava appletCodeArray data structureCategory of beingObject (grammar)Formal languageInheritance (object-oriented programming)Functional (mathematics)Data conversionOperator (mathematics)System callIntegerElement (mathematics)PrototypeProxy serverType theoryCASE <Informatik>Mixed realityDampingLoginDataflowQuicksortDifferent (Kate Ryan album)Data storage devicePointer (computer programming)Interior (topology)Semiconductor memoryLecture/ConferenceComputer animation
DiagramScripting languageGraph (mathematics)Element (mathematics)Semiconductor memoryCASE <Informatik>Pointer (computer programming)FlagLengthElement (mathematics)Electronic mailing listDisk read-and-write headAnalytic continuationPoint (geometry)Data storage deviceComputer animation
CodeField (computer science)Sample (statistics)Venn diagramElement (mathematics)Address spaceFlagRead-only memoryMiniDiscSemiconductor memoryCASE <Informatik>IntegerFlagCategory of beingVolume (thermodynamics)Greatest elementElement (mathematics)LengthGraph (mathematics)Computer animation
Vulnerability (computing)Telephone number mappingFlagField (computer science)FlagCASE <Informatik>Telephone number mappingObject (grammar)Focus (optics)Arithmetic meanComputer animation
CodeSample (statistics)Category of beingData typeFlagLogical constantElement (mathematics)FlagCASE <Informatik>Semiconductor memoryComputer animation
Read-only memoryElement (mathematics)Core dumpSemiconductor memoryFlagLogical constantArray data structureComputer animation
FlagElement (mathematics)Read-only memoryCore dumpFlagState of matterContent (media)Logical constantMetadataQuicksortMixed realityComputer animation
VarianceGlass floatNormal (geometry)Representation (politics)Exclusive orControl flowGame controllerVulnerability (computing)Exclusive orDampingCuboidVotingState of matterData storage deviceValidity (statistics)Array data structureElement (mathematics)DataflowCategory of beingNumberLogical constantLecture/ConferenceComputer animation
Data typeJust-in-Time-CompilerFunction (mathematics)Data conversionCodeVulnerability (computing)Just-in-Time-CompilerCodeFunctional (mathematics)Machine codeObject (grammar)Sound effectType theoryData conversionCASE <Informatik>Software bugComputer programming1 (number)Computer animation
Just-in-Time-CompilerData typeFunction (mathematics)ForceJust-in-Time-CompilerVulnerability (computing)MathematicsFunctional (mathematics)State of matterVirtual machinePerturbation theoryMultiplication signMachine codeLoop (music)Computer programmingType theoryInstance (computer science)XML
Just-in-Time-CompilerData typeCASE <Informatik>Type theoryMultiplication signMachine codeFunctional (mathematics)Compilation albumJust-in-Time-CompilerData conversionCompilerControl flowMathematicsDigitizingSoftware bugLecture/ConferenceXMLComputer animation
Vulnerability (computing)CodePrototypeElement (mathematics)Functional (mathematics)Vulnerability (computing)Just-in-Time-CompilerSound effectMachine codeContent (media)CASE <Informatik>FlagState of matterLecture/ConferenceComputer animation
Vulnerability (computing)CodeStatement (computer science)OvalPrice indexCASE <Informatik>FlagContent (media)MereologyCodeLecture/ConferenceMeeting/InterviewComputer animation
Vulnerability (computing)Statement (computer science)Slide ruleOvalLengthFunctional (mathematics)Digital photographyGraph (mathematics)CodeStatement (computer science)CASE <Informatik>PrototypeType theoryEmailDisk read-and-write headLengthFlagHoaxComputer animation
Vulnerability (computing)Statement (computer science)OvalElement (mathematics)Array data structurePrototypeIterationCountingLengthDirected graphElectric currentElement (mathematics)Parameter (computer programming)Statement (computer science)Array data structureFunctional (mathematics)MereologyEnumerated typeCodeMultiplication signComputer animation
Directed graphElement (mathematics)Vulnerability (computing)Electric currentOvalIterationPrototypeArray data structureCountingLengthMultiplication signCuboidLoop (music)CountingElement (mathematics)PrototypeStatement (computer science)System callFunctional (mathematics)Computer animation
Vulnerability (computing)PrototypePrice indexOvalChainEDIFObject (grammar)PrototypeChainFunctional (mathematics)Loop (music)Multiplication signMereologyType theorySlide ruleParameter (computer programming)Digital photographyProgrammschleifeXMLComputer animation
Vulnerability (computing)Loop (music)PrototypeArray data structureChainFlagJava appletCodeJust-in-Time-CompilerProxy serverFunction (mathematics)Sound effectObject (grammar)PrototypeChainType theoryData conversionProxy serverFunctional (mathematics)CASE <Informatik>Computer animation
Vulnerability (computing)PrototypeJust-in-Time-CompilerProxy serverFunction (mathematics)Sound effectObject (grammar)Just-in-Time-CompilerPrototypeChainSound effectFunctional (mathematics)Type theoryObject (grammar)Loop (music)Axiom of choiceCodeProof theoryComputer animation
Array data structureGlass floatVulnerability (computing)Just-in-Time-CompilerFunction (mathematics)Object (grammar)VarianceData typePrototypeLoop (music)Error messageMereologyProof theoryCodeMachine codePerturbation theorySoftware bugComputer animation
Object (grammar)Proxy serverPrototypeConstraint (mathematics)Vulnerability (computing)Function (mathematics)LogarithmPrototypeFunctional (mathematics)Loop (music)Type theoryProxy serverDampingError messageLine (geometry)ChainXMLComputer animation
Vulnerability (computing)Function (mathematics)PrototypeObject (grammar)LogarithmChainType theoryDampingError messagePrototypeMereologyFlagKey (cryptography)Functional (mathematics)CodeEvent horizonXMLComputer animation
Vulnerability (computing)Function (mathematics)Object (grammar)PrototypePointer (computer programming)Proxy serverLogarithmChainPrototypeDampingFunctional (mathematics)Just-in-Time-CompilerProxy serverElement (mathematics)CodePointer (computer programming)Parameter (computer programming)Multiplication signCASE <Informatik>Line (geometry)Loop (music)Data conversionXMLComputer animation
Vulnerability (computing)Function (mathematics)PrototypeObject (grammar)Proxy serverCrash (computing)LogarithmMachine codeAddress spaceCodeProof theoryLoginCrash (computing)Object (grammar)Video game consolePrototypeFunctional (mathematics)DampingImplementationArray data structureChainTerm (mathematics)DigitizingJust-in-Time-CompilerXMLComputer animationLecture/ConferenceMeeting/Interview
Crash (computing)Vulnerability (computing)Just-in-Time-CompilerFunction (mathematics)Object (grammar)PrototypePointer (computer programming)LogarithmThread (computing)Context awarenessView (database)Library (computing)Address spacePrimitive (album)InformationLeakAnalytic continuationProcess (computing)DampingLine (geometry)Type theoryNumberPointer (computer programming)Point (geometry)Data conversionSoftware bugPrimitive (album)Object (grammar)Address spaceHoaxCASE <Informatik>Computer animation
View (database)Library (computing)Address spaceAnalytic continuationProcess (computing)Primitive (album)Pointer (computer programming)LeakInformationContext awarenessThread (computing)Object (grammar)Primitive (album)CodeView (database)Field (computer science)Pointer (computer programming)Library (computing)Address spaceHoaxComputer programmingGraph (mathematics)Term (mathematics)Level (video gaming)Computer animation
Demo (music)CodeCrash (computing)Exploit (computer security)Proof theoryCalculationGraph (mathematics)Context awarenessCore dumpLecture/Conference
Demo (music)Exploit (computer security)Duality (mathematics)Maxima and minimaDemo (music)2 (number)TouchscreenMathematical optimizationSource codeObject-oriented programmingGastropod shellCodeCalculationRow (database)Exploit (computer security)CASE <Informatik>Lecture/ConferenceXMLUMLComputer animationSource code
Hill differential equationDemo (music)FeedbackCodeExpected valueObject-oriented programmingProof theoryDemo (music)Data conversionSound effectJust-in-Time-CompilerObject (grammar)Pointer (computer programming)CASE <Informatik>Source codeComputer animation
FeedbackInformation securitySlide ruleMeeting/InterviewLecture/ConferenceComputer animation
Multiplication signMusical ensembleInternetworkingNumberLine (geometry)Open sourceAutomatic differentiationLecture/Conference
Open sourceJust-in-Time-CompilerComputer fileLatent heatVulnerability (computing)Dynamical systemTraffic reportingFormal languageNumberRun time (program lifecycle phase)Domain nameSoftware bugMultiplication signSystem callReading (process)Connectivity (graph theory)Compilation albumMeeting/Interview
Process (computing)WindowEscape characterNumberGraph (mathematics)CodeLecture/Conference
Multiplication signMereologyRight angleLecture/Conference
Semiconductor memoryInflection pointCartesian closed categoryMusical ensembleLecture/ConferenceDiagram
Transcript: English(auto-generated)
Johnathan, wonderful good morning. You're going to tell us about the last 12 months
and what you have done. So your talk is called From Zero to Zero Day. This is Johnathan Jay from the MSRC in Israel, a division of Microsoft, and the stage is yours and that is your applause. Thank you. So, hi everyone. My name is Johnathan and I'm
a security researcher in MSRC in Israel. On my free time, I play CTFs with Team Perfect Blue and I also do research on my own. I'm just an 18-year-old and I started doing security-related
stuff last year, so the first part of this talk is going to be about what I learned in the past year and how I learned it. So if you're just starting off in security and you want to learn what to pick up what I did and learn from what I did, this could be helpful for you. And for the more experienced security researchers in the audience, the
second part of this talk is going to be about a zero-day I found in Chakra, a JIT type confusion. So we're going to dive deep into that. And even if you're just starting in security, a basic understanding in programming is probably going to be enough to follow up. There's going to be a lot of code, but it's fine. It's not too complex.
And last but not least, we're going to finish up with a demo, hopefully working one. So yeah, let's start off. So why vulnerability research? Vulnerabilities are for me some sort of riddles. There is some sort of very challenging riddles that we have to find some flaws that the developers did not consider. This is very challenging and very interesting for me at least,
and I found it awesome to do. So what is a vulnerability? There are a lot of definitions to it and when you want to understand something, you start a job on Wikipedia. There are a lot of definitions, as you can see. It's somewhat weird, like the probability that an asset will be unable
to resist the actions of a threat agent. I don't know what that means. Yeah, for me a vulnerability is some sort of flaw in a program that allows you to change the intended flaw of the program. That's what I consider as a vulnerability. And the definitions we've just seen do not really tell me how I find them. So how do we find vulnerabilities? So when
I started, I had some knowledge in programming. I wasn't the best developer out there. I was just a decent one, and on a good level in C and assembling and OS internals to understand how things actually work in Python to actually write some code. So I wasn't the best developer,
but I had some knowledge. For example, I read the C programming language, which is a great book, and will give you all the C knowledge you need to know in order to actually get into vulnerability research. The next thing I did was to expose myself to vulnerability research basics. So I read online about some basic vulnerabilities, like classic
stack buffer overflows, like integer overflows, and so on. And then I tried to practice my knowledge, and that was through war games. War games are some challenges that are offline, and you can try to solve them. They're security related challenges, like you have to find some vulnerability and exploit it. So that's what I did next, and at first
I failed badly, but it's okay, because I read the solutions, I read the write-ups, and I learned how to actually approach the solutions and actually how to solve them. So it comes with time, and it's okay to fail, because we all do. The next thing I did was to expose myself to CTFs. Now, CTFs are capture the flag contests, and
they're basically competitions where you have to solve some challenges and compete against other teams. This is a team effort, and you play with other players, your friends, and this is how I met my teammates. So we met through CTFs, through IRC, and we played together, and sometimes we fail miserably, but sometimes we actually do well, and we got
to qualify to some finals, and we actually got to travel the world, because when you qualify to finals, they sometimes pay for the trip. So we actually got to see some cool places, and yeah, that was awesome, and I actually think CTFs are a great way to get into security.
So the next thing I did was I dove into the deep waters. So once you know the basics, it's important to not stay there for too long. You have to expose yourself to harder things. I was afraid to see and try to solve the harder challenges at first, but with time, I managed to understand that nothing too bad would happen even if I failed.
So I tried to solve challenges, and I failed, but with time, I managed to pick up the tricks and the ways to solve the harder challenges, and I think this is very important to not be afraid to solve things, because even if you fail, you learn from other people's solutions, and it's fine. It's totally okay. A great tweet by LiveOverflow, which also
should be in the audience or in the conference, and basically says what I really believe in, move away from the basics as soon as possible and expose yourself to harder things which you don't understand. This will make you understand things that you thought you understood but you actually didn't, and also, it's important to learn from different resources. This way,
you learn different tricks and different ways to approach problem solving, and it's very important in my opinion. So yeah, LiveOverflow also has a great YouTube channel, which I also watch, and he talks about vulnerability and security research, and I really recommend watching his
videos as well. So after I had some knowledge in CTFs and wargames, I practiced, and I practiced a lot, so it's really important to practice and solve challenges on your own, because this way, you actually get to pick up those tricks that you have to understand in order to solve
some challenges. So some vulnerabilities have patterns in them, and the way you catch on those patterns is by seeing them quite a few times, and a great way to do that and to pick that up is by solving a lot of challenges. This is where I also expose myself to actual real-world vulnerabilities. I did that through a lot of websites. There's the Project Zero bug tracker,
which you can read the vulnerabilities about exploits and so on. So this is where I also expose myself to real-world vulnerabilities, and I also realized that there is a great connection between vulnerabilities in CTFs and in real world. So vulnerabilities are essentially bugs,
and they exist both in CTFs and in real life, and the main problem that you think you have when you actually try to do real-world research is that you think that the code base is huge and stuff like that, but even if the code base is huge, bugs are still out there, so don't be afraid
to look at it, because the odds of vulnerabilities running out just when you're starting to look at something are very low. So you can start to try and solve things, and by solving things, I mean looking at actual code bases. After doing some practice, I realized that how do we find vulnerabilities, and once we actually start and repeat the process of solving challenges and
practicing, we noticed that vulnerability research is about identifying bugs, and we do that by looking at code. So looking at code is essentially understanding the code, and we have to find vulnerabilities in it, right, because we want to find them, and that's through actually auditing
the code, and that comes with practice. So practice is really important in vulnerability research, and once we master that, even though I'm not a master and I'm not near that, I still think that practice is important. So how do we actually find vulnerabilities? So after, as I said before,
vulnerabilities have patterns in them, and patterns are something that you catch up with time, and I wasn't doing that for so long, I'm doing it just for a year, so how did I catch it up? That was basically through a practice. As I said before, practice can cover the time that you're not doing that for so long, so I managed to notice that vulnerabilities have
patterns in them. For example, programming errors like integer overflows and sadness issues, these are bugs that actually exist because people make mistakes and we're all humans, and humans make mistakes, we're not perfect. A great example for such a mistake is the following code. So on the third line, we have an integer overflow, which a lot of developers
know about the existence of this vulnerability, yet they still make this mistake. As I said before, it's because we are all humans and humans make mistakes, so don't be afraid to look at actual code bases, because these kind of bugs actually exist in actual code bases,
not only in CTFs, so don't be afraid to look at actual vulnerabilities. You can find simple ones as well. So there's a great difference between CTFs and real-world vulnerabilities. In CTFs, usually when you find the vulnerability, you know what you need to do with it. You know what you have to progress with it, you know what you need to exploit, how you need to exploit
it. So sometimes you have a stock buffer overflow, so you need to override some variable, the return address, or you can override a function pointer. Mostly when you see a vulnerability in CTF, you know what you need to do with it. In real world, you usually have a set of weird
states, a set of primitives that you have. Primitives are essentially capabilities we have as attackers. So you usually have some primitives that we can chain together to do something greater, and that can later lead to vulnerability. And a such example of that will be the vulnerability I found in chakra, which is basically a bunch of primitives together, chained together to
make an actual vulnerability. So this is basically everything I knew about vulnerability research and security research before I started looking in chakra. So yeah, let's dive into some JavaScript. So JavaScript engines. I didn't say I learned JavaScript because I didn't,
and JavaScript is a very readable language. Once you have learned a few programming languages, reading JavaScript would probably be a more smooth process. So doing that would not be too hard. Now JS engines are basically what's capable of running the code that you write as a
JavaScript developer. They have a lot of parts to them, and the most important one for us is the JIT compiler. Now, JIT stands for just in time compiler. What it essentially does is when some function gets hot and it's being called a lot of times, it compiles this function to machine code to improve performance. Now, this compiler is also responsible of
doing optimizations for the code. It has a lot of assumptions about the code and doesn't want assumptions to be broken. So we'll see just how that works later about JIT compiler
vulnerabilities. So about JavaScript basics. So JavaScript is a dynamically typed language, and it's fairly readable. You can make arrays in different ways. You can have arrays with different types of elements, and you can have console log to actually print stuff.
So JavaScript has prototypes. Prototypes are essentially something that you can in some way you can inherit features from other objects. So for example, in our case, we have a parent object and a child object. So the child object, if the prototype of it is the parent object, then it will inherit some features. In this case, the properties X and Y. So this actually
is fairly important to us in the exploit and the vulnerability I found. And you can modify the prototype by accessing the proto property. JavaScript also has something called proxy. Now, a proxy is an object that can wrap another object and redefine basic operations,
like getters and setters. So in our case, we redefined the getter. So we redefined the getter in which we're supposed to get X to be 1337. But due to the fact that the proxy redefines the behavior, what happens is the getter handler is actually being called.
So we redefined the basic operation of the getter. And in this way, we actually tracked the function call to the getter of this object. So JavaScript has arrays, as we've seen. But Chakra has arrays with types. So the first type we're going to talk about is called
the JavaScript native int array. And it's the way Chakra calls integer array. Basically, it's an array that stores integers, which are 4 bytes per element, and they're basically integers. JavaScript also has a native float array, which is essentially floats. And unlike the C definition for float, this float is actually 8 bytes. And they store floats, and they're
8 byte per element. And we have JavaScript arrays, which are objects, they store objects, essentially pointers, and they're also 8 bytes per element. So let's see about how we can convert between the types. So if we start off with an array of
integers and we add a float to it, what happens is we convert the array to a float array. And if we add an object to it, the conversion that takes place is a conversion to a JavaScript array. If we have both floats and integers, the outcome is a float array.
So if we have both floats and integers and objects and mixed array, what happens is this array is considered to be a JavaScript array. And the most weird conversion that takes place is this one. And you usually don't see this when you see about conversions in JavaScript engines, but this is very crucial for our talk. So when you have two arrays
and you assign one of them to be the prototype of another one, the prototype one is directly converted to a JavaScript array. Now, this is going to be very important to us later, so keep that in mind. And this basically, the conversion takes place right when the assignment takes place. So let's see about how array layout actually looks like in memory.
So when we have an array, for example, in this case, a JavaScript array, it has some flags which indicates some things about the array, and it's called array flags. We have the length of the array and we have the head. Now, a head is a pointer essentially to a segment, and a segment is a continuous memory region which has elements in this. So it has some of
the elements in the array and it stores another pointer to the next segment. So what comes out is essentially a linked list of segments that start off with the head pointer in the JavaScript array. So let's see how that actually looks in memory. We have this simple example where
we have two values, in this case integers, and we have an array with two values. So let's see how it actually looks like in memory. So in red, we have the JavaScript array properties. So we can see the initial array value, which is the array flags value, and we
have in green, we have the actual segment. So we can see the segment has the length and the size and in blue, we can actually see the memory layout of the segment, and if you notice on the right bottom, we have the two elements we defined. So we actually can see this in memory and this is how it looks like. So what is the array flags that we're just talking about?
So array flags is basically some flags that indicate some things about the array. In our case, it's defined as an enum, and the interesting field for us is there has no missing values flag. And so if we, for those of you who notice, the initial array value that was defined as our
array flags is actually composed of two different flags. It's composed of the array object array flags tag and there has no missing values tag. So the first one is not really important for our talk, so we'll focus on the second one, which basically means that the
whole mean. So we can create an array and have some values between the, like we have, in this case we have three elements, but the middle one is missing. So it's important to say that the values I put there are the way they're represented in memory is by those
constants, and I chose them because it will be easier for us to see them, to actually notice them when we see the memory layout, as we've seen before. So as you can see, this one has a hole and the array flags doesn't have the has no missing values on, which means we have a hole in the array, and things actually make sense. But when we look at the memory layout, we
see some weird thing. So as you can see, we have the dead beef and the 4-1, but between them where the missing value where the hole was, we have some magic constant. Now it makes sense that this constant would represent a missing value or a hole in the array, but as we've seen before, there's
already something that tells us when we have holes in the array, it's that has no missing values flag. So we know that there is one way to indicate holes in the array, and it's the flag we've seen before, but it seems like there is another way to tell if there's a hole in there's holes in arrays. So that's kind of weird, that raises a lot of questions.
We have basically have here, we potentially can have here two things that indicate, should indicate the same state. We have the flag of the array, which is the has no missing values flag, and we have the content of the array, which we've seen before, and is the FFF constant.
So if we can, what happens if they're like mismatched? What happens if we can do that? It has no missing values to be set to true, and therefore says that we don't have any holes, but we actually have a missing value in the array.
Also, we mix some sort of mix data and metadata together, because if the constant is being used in a control flow, that could be interesting for us if we can fake it. And it actually is very interesting, and it turns out that we can actually fake a missing value,
and this was a vulnerability find by Lokihart and Sorry My Bad, and got CVE for it. So basically what they did is they faked a missing value by putting the constant we've just seen before into a float array. So beforehand, the value was not the same one that we've seen, and it was something that you could actually represent as a valid float number. So
this could be turned to a vulnerability. Now, this was mitigated in a few ways, and the constant changed, so you couldn't represent it as is, and what also happened is they added a few more checks to harden things. So we'll talk about how this weird state can be
turned into a vulnerability and how we can actually exploit it, but before that, we actually wanted to talk about something interesting. So as I just said, the exploit, the vulnerability Sorry My Bad and Lokihart found was in native float array. So apparently JavaScript arrays
don't store floats as actual floats. They first do something called boxing. So they first XOR these values with this constant you can see here. So before you put the float into the array, this value is being XORed with the floats you're trying to put in. So the question that
comes up next is whether we can actually have missing values in JavaScript array, and if so, does the constant change? Because as we see here, we change the way we represent values, and therefore we can represent new values. So theoretically, we should be able, theoretically the engine
should change the constant. Otherwise, we could potentially get there and actually represent it. And it turns out that the constant does not change. Therefore, we can actually represent it by first boxing it. So what we did is we first boxed it and XORed the constant with
the FFF we've just seen here. So that in turn gives us the ability to have the constant as the original constant value. Why are you wondering? Because this property of XOR. So when you have XOR of three elements and two of them are the same, then they cancel out and they give you the other one. So if we have two of them to be this value,
and the one of them to be the magic value, what happens is we actually get in turn the magic value, and therefore we actually can represent it. And this is exactly the vulnerability I found, which is truly, truly heavily relies on the fact that we have basic understanding
of JavaScript engines. This boxing is one of the first things we learn when we learn about JavaScript engines. So we used this idea of boxing things and turned in supposedly unexploitable state to be a vulnerability. So basically what we did, as you can see here, we first boxed it
and then we put it into an array that is a JavaScript array and not a float array. So how do we turn this weird state into a vulnerability? So what happens first thing we need to understand is how vulnerabilities look like. So JIT stands for just in time, as I
already explained, JIT bugs are usually what we see today are usually type confusions. So type confusions are essentially confusion between two types that the program thinks something is of one type but actually of the other type. So JIT type confusions, the most
common ones are due to side effects. Now side effects are some things that take place in the JIT code. Now JIT code, I refer to the machine code that is emitted when the JIT compiler compiles the hot function. So in our case, in the usual case I mean, what happens is you have
some function, let's call it foo, which changes a type of some object. Let's think of it as an array and then the function that got JITed doesn't know about this conversion. So it thinks that everything's still the same and nothing has changed so it's going to work with it as it did before and this leads to a type confusion. So let's look at an example.
So first thing we do when we have such vulnerability is we make the function hot. As I explained before, to make the function be compiled into machine code, we first have to make it hot. We do that by calling the function a lot of times and this can be done by a simple loop. And the next thing, okay, so also it's important to say that the JIT compiler has a lot of
assumptions. These assumptions are on some types, on some things that take place during the execution of the function. So for instance, when we call foo and it thinks that foo doesn't do anything, it knows that it doesn't need to check things again because if foo doesn't change
any state of the program, why would we check something again? Because that would make the function way more slower and JIT compilers are all about making things faster. So it has
something that breaks the assumption, we can eventually get to a weird state and from there we can turn this into a vulnerability. So in this case, once we call the function and we make it be compiled into machine code and we actually call the function again, but this time we want to trigger the type confusion. So what happens here is foo will change the type of the
array and therefore the assumption that foo doesn't change anything breaks. This gives us the ability to have the JIT compiler think that nothing happened, but we actually change something. This is very important because JIT compiler bugs are very complex sometimes and this one is not
too complex. What happens here? We essentially made the JIT compiler forget to not know about the conversion that happened. So if foo converts array to a JavaScript array, something happened but the JIT compiler doesn't know about it. So it's very crucial to understand that
and the assumption essentially breaks because we changed something in the function that we called. So this is basically about JIT type confusions. We essentially have some function to make it hot and then we have another function which gets emitted to machine code and in that function we
call some other function which will have side effects. Side effects are things that the JIT compiler is not aware of. So let's actually move on to my vulnerability. So to turn the mismatch between the flags, if you remember we had a flag that indicates it has no missing values and we also
had the array content. So once we have the mismatch and we actually managed to fake a missing value, to turn that into a vulnerability is quite interesting. So the way Luckyheart and Concat which relied on has no missing values and also relied on the content of the array.
This allowed the mismatch to get us into a weird state which shouldn't happen because in normal case why would we have the missing values and the content of the array to have not a mismatch between them. So in our case, in Luckyheart's case, they got the array flags
to be mismatched with the content of the array and they call Concat. Now the way Concat was called was once we make a fake array, let's call it buggy, and the buggy has a fake missing value in it but the flag that indicates if there are any missing values is not set to true.
So we set this fake array and we call Concat with it. Now the first thing that happens when we call Concat is we get to this code. Now this is the part where we have a lot of code, now bear with me, it's not too hard, it's just a lot of code. So first thing we get is we get to this function. This function essentially is called ConcatArgs and AItem in this case is
the fake array, the buggy we have. It's called, we invoke IsFillFromPrototypes and we would essentially want FillFromPrototypes to return false in order to get into that if statement. Now IsFillFromPrototypes essentially checks the following things. First thing it checks is
that there's only one segment in the array and that check is through the fact that it checks that the next segment of the header head segment is null and another thing that it checks is that the length of the array is equal to the length of the segment. Therefore, the segment is the only segment in the array. So this is the first thing that it checks and
the second thing that it checks is that the flag has no missing values is set to true. Now this can be easily bypassed because we've already seen that we can fake a missing value and set the flag to true. By doing that we can actually get IsFillFromPrototypes to return false and we can actually successfully get into the if statement. Next thing we reach is this
else statement which calls copy array elements with our array as an argument. As you can see, it's the p-item array. Once we reach copy array elements, it's essentially copying an internal function and which does the following. Now this part is very interesting for us.
So first thing that happens is they make an enumerator out of the array. An enumerator will enumerate every single element in the array and will increase the counter every time it engages a new element. Now let's see how this enumerator is actually implemented. So this is
a code for the moveNext function and as we can see on the red box, when we encounter a value that is actually a missing value, we actually skip it. So what happened here is that every time we got into the while loop and there was a missing value, it skipped it.
Therefore, the count that we had there is not equal to the amount of elements in the array. This is because every time we find a missing value, it doesn't increase the counter. And therefore, we can actually get into this if statement and call internal FillFromPrototypes.
Now this also is a very interesting function for us because it basically first thing it does is loop through the prototype chain. Now if you remember, the prototype is basically some object that we inherit features from. So we can have our prototype to have another
prototype and therefore we forge a prototype chain. So this thing, this function first loops through the prototype chain and then what it does, it calls this long function name with our prototype as an argument. So if you remember, a prototype is actually directly
converted into a JavaScript array when assigning a prototype. Remember from the conversion slide. So essentially what happens here, it knows that the prototypes should know that a prototype is a JavaScript array and we loop through it. So every time we get in here, what happens is the prototype is being passed through the object argument and is then converted to a JavaScript
array through the ensure non-native array. So as you can guess, ensure non-native array basically ensures the array is not of a native type. Therefore, it's a JavaScript array. And this is the interesting part for us. So what we've seen so far is, so to quick recap,
so what happened so far is we managed to get concat to convert some array into a JavaScript array and there is nothing that indicates this conversion. It's important to know that once we fake a missing value and we call the prototype, we call the concat, all of the prototype chain
is being converted to JavaScript array. Now as we've seen before, prototypes in the prototype chain are not native types. So we shouldn't be able to put there any type because it should be JavaScript array. So it turns out that we actually can do that.
So remember proxy. Proxy can wrap an existing functionality and redefine its functionality. In our case, we can, and it's pretty fun that we can do that, is we can redefine the functionality of get prototype. So what this allows us is whenever we call get prototype, we redefine a behavior. So remember that prototype chain? So what we do here is
basically redefine this prototype chain loop and by doing so, we allow us to return to call an arbitrary function. Now if we call an arbitrary function of our choice, for example, in any function, what happens is the JIT compiler checks if this function is marked as having
side effects or not. If the function is marked as having side effects, the whole function that got JITed is marked as having side effects and it's not good for us because we don't want the JIT compiler to know that some side effect took place because we want to get attack confusion. So what we do is we use an existing function which is called value off and is marked
as not having side effects. This basically allows us to return any object in the prototype chain by calling get prototype. So let's see an example for that. This is the proof of concept code and we'll go through every single part of it and I'll explain everything. So first thing we do as I explained before, we loop through this first loop to make the function hot
and therefore it gets compiled to machine code. Next thing we do is redefine two arrays. Bug array which is going to be the array we fake a missing value inside of it and an array which is called arr and is going to be our target for attack confusion. Now it's important
to notice that the target which will be converted to a JavaScript array is not buggy, it's array. So notice that. Next thing we do is we redefine the get prototype off. So get prototype off is basically the get prototype function and by redefining it to be value off, whenever
the function get prototype is called on arr, we redefine the implementation and get it to return the arr itself. This allows us to return arr whenever get prototype is called. This is huge because if we define a proxy, what happens is we essentially made the get prototype loop to
return us a JavaScript native float array. So if you notice on the line before, whenever get prototype is called, we call value off and if we loop through the prototype chain and value off is the get prototype, the return value is going to be arr which is a JavaScript native float array as you can see here. This allows us to essentially have a native float array even though there
shouldn't be any of those in the prototype chain. Next thing we do is we redefine the missing value constant and by doing so, we still have the has no missing values flag for buggy set to true. This is the interesting part because this is where the mismatch takes place.
So we have a missing value but we don't have the flag set to true. So next thing we do is we call the jitted function. So this is the the key part here. So when we call this function, what happens is this function is this code is being ran. So first thing
we do is we define the temporary and we define the first element of the array object, the array array to be a float. This makes the JIT know that whenever it uses array and no conversions happen, it still thinks it's a JavaScript float array. And then we call the temp, we call the
concat function and we give it the buggy as an argument. This will essentially loop through all the prototype chain and one of the prototype is the proxy as we can see here. And when we call the get prototype for the proxy, we get the ARR and when we call the get prototype for the ARR, we get sorry, when we get we call the prototype for the buggy, we get proxy.
When we call the get prototype for the proxy, we invoke the get prototype for ARR, which will return us the ARR. And therefore, what happens is ARR is converted to a JavaScript array whenever the prototype chain loop takes place. Afterwards, in the last line of the JIT function,
we basically write some value into the array and therefore write a float into some array that was converted to JavaScript, a float JavaScript array. So array is the same array that is in a prototype chain and therefore we still overwrite, we overwrite some pointers with a float,
which in our case is one, two, three, four. So last thing we do is call console log and we crash because we have a object which is defined for this address and it's not mapped. So this is essentially the proof of concept code and what we do here is fairly simple.
First thing we do is we make a function hot and by doing so, we make it be compiled into machine code. Next thing we do is we redefine the behavior of the JIT prototype function and we allow it to return an arbitrary object whenever we call get prototype. Then we invoke the JIT, then we fake a missing value
and we call the JIT digit function, which in turn allows us to convert in all of the prototype chain to be some JavaScript arrays. Now even though most of them are JavaScript arrays, one of them, the array one, is not because we redefined the implementation for get prototype.
This in turn allows us to have a JavaScript float array converted to a JavaScript array and we can still act with it, still operate with it as if it were a JavaScript float array and therefore the last line here allows us to write a number as a pointer
and therefore we have what we called in the beginning a type confusion. So we essentially had a type confusion between a float and a pointer because the JIT did not understand that some conversion took place. Now this is essentially the bug and to exploit it we actually had to do a few things. So first thing we did was we targeted
a fake object primitive. Now what is a fake object primitive? The thing we've just seen was that we faked an object at an arbitrary address. In our case it was faked in data one, two, three, four as seen here, but the thing is we actually can fake an object at an arbitrary address.
The object we decided to fake was a data view object which has some fields of it which are a pointer you can read from and write to. This in turn gives us a read write primitive and from there what we did was to use a known trick which is to find a stack address and overwrite it to execute our code in ROP.
So to do all that we used a great library which is called PwnJS and we had to fix a few things but it did most of the work. What it did was basically do what we explained. It requires us to have a read write primitive and then what it does is to scan the program
to find a stack pointer. From there it overwrites the stack with whatever we want because we got an arbitrary read write primitive and then what happens is we can execute code in ROP. Now we first exploited it on edge but to actually execute our own code,
when we actually executed our own code we executed it in the sandbox context. So it wasn't any nice for a demo because it would not allow us to pop a calculator or something, so what we did was we actually compiled ChakraCore for Linux and we exploited it for Linux.
The exploit for Linux was similar because we still overwrote the stack with some things we controlled as we had a read write primitive and from there we basically executed code in ROP. So that's basically what we did to get a POC, a Crashing Proof of Concept to actually execute our own code.
So let's see a demo for that. Let's hope it actually works. One second, you can see my screen. Sorry.
Second. So let's hope everything works. So basically we have ChakraCore compiled for Linux and this is how it looks like.
So to execute it we actually have to give it a source file. So in our case we give it an exploit, our exploit. So notice this is going to be a very fast exploit because Chakra is super fast and it has super optimizations because why not? So it's going to be super fast, so beware. And therefore we have, okay, we have a shell.
So to prove them we actually got code execution. Let's run a calculator. Oops. And we have a calculator as well.
So we actually can actually execute code and everything works as expected. So a few things about, let's get back to the talk. Oops. Wait. Sorry.
So what we've seen here, sorry. What we've seen here was basically we had some code. Okay, so the demo shows us a very simple thing. We first did what we did in the proof of concept. We got the JIT compiler to not know
that some side effect took place. This side effect in our case was some conversion that allowed us to convert some array into a JavaScript array and then in turn allows us to write floats as pointers. And this basically gave us a top confusion primitive which we exploited by faking an object
which gives us a read write primitive and therefore we continue to write our code in ROP. So I really hope that my talk was helpful for those of you who want to get into security research and I hope it was entertaining for those of you who just wanted to hear about the technical stuff. And I want to thank everyone who helped me out
with the talk and with the slides and for you for coming here and seeing my talk. I know there's a great competition and there's way cool talks probably but you still came to mind so thank you for that. And yeah, thanks for coming.
Jonathan, do we have some time for questions? Yeah, sure. Okay, was it the first talk you held for such a big audience? Yeah, I never spoke more for more than 10 people. For Christ's sake, that was good.
We have a couple of microphones over here so if you have questions for Jonathan, please line up over there and shoot the questions. We could start with one from the internet. Do we have one? Oh, okay, don't we have any questions over here?
Where's here? Number five, here we go. So thank you for the excellent talk. I was surprised to see that the JavaScript engine for Edge was open source. So is this something that they're moving towards,
more open source components? I think that open sourcing is cool but I don't know how actually out of the future we'll be and if we'll open source more things. I actually don't know. Then we continue with microphone number two.
Hey, thanks for your very interesting talk. So obviously JIT compilers are a pretty in-depth subject so my question is, when you got started with all this, how much prior domain knowledge of JIT compilers
and dynamic language runtimes did you have and when you then got to ChakraCore, how much did you have to just read through the whole thing before you could actually start looking for a vulnerability in there? So what I did was when I started to get into Chakra,
I read a lot of file reports and I've seen that there's plenty of vulnerabilities there. So I read most, not every one of them but I read a lot of them and I tried to understand what they do and why they exist and this is basically what got me into looking for my own bug as well. So basically I had understanding
of JavaScript engines in general and then I read about how Chakra works and read about specific vulnerabilities in Chakra. Yeah. And over to microphone one please. You mentioned the process in Windows runs in a sandbox so you weren't able to up later.
Would this stop the exploit in the wild or is there a way this could be dangerous even in the sandbox? So to actually execute your arbitrary code, you have to escape the sandbox. There's things you can do with the sandbox but they're pretty limited. I didn't exploit, I didn't find a sandbox escape yet
I hope I will find one day but to actually execute interesting stuff you need to do a sandbox escape because there's plenty of mitigations nowadays and specifically for edge does a lot and I don't know specifically if you could do anything interesting without a sandbox escape but you could actually run code.
So you could do stuff, I'm not sure if it's interesting enough to actually do something in the wild. And over to microphone number two. Hey, great research and work. How much time did you work on it? So I worked on Chakra for about,
I think from July I think. When I started I started reading about Chakra in July but I had some knowledge about JavaScript engines before for about like two months I think. So not for, I mean the more important part for me
was to actually read about examples and see examples more than spending a lot of time on it. I think it comes with practice as I explained before what I do believe in is practice and my practice was through reading a lot of research and a lot of internal stuff about Chakra. So it's about I think from July
and maybe two months before. All right. Nobody's queuing up on the microphones or have I overseen someone? Jonathan. Excellent. Thanks. That's it. That's your applause.
That was Jonathan Jay from Zero to Zero Day. Thank you very much.