 So, up to you, Adi. Oh, hey. Welcome to everybody. This presentation is going to be about prototype pollution attack. So first of all, who am I? I do these days mostly PEN tests and a bit of security research. Before starting in Infosec, I used to do lots of web-level PEN mostly front-end. So this is where I learned most of the JavaScript that's going to be presented here. So the plan for this presentation is I'm going to first do a brief introduction to JavaScript, but I'm going to focus very much on the part that are going to be abused through this presentation. We're going to go after that what can allow prototype pollution to happen, how can it be exploited, and I'm going to leave a few notes on how these types of attack can be mitigated. So the first thing that's really interesting in JavaScript is that is the way class or declared and work. Let's say you want to, first of all, declare a class that's called DAG. It first starts as a function. And the way you add a method to classes is you add them on what's called the prototype. So if you want to have a method that's going to be there on all instances of DAG, we're going to do DAG, which is the function. Prototype.talk equals the function. And now after that, all instances of DAG have the function named talk. What's also good to note in JavaScript is that all the objects in arrays from a base object that's named object. And the base object comes with a few interesting properties. One of the interesting properties is called constructor. And constructor points back to the constructor for which the object was created. So here, if we have an instance of DAG, the instance.constructor is going to point to the function of DAG. And if we're able to point to the function of DAG, this also means that we can reach the prototype of it by doing constructor.prototype. And what's really also interesting is that since the ECMAScript 6 standard was introduced, there was a magic property that's named proto that does essentially the constructor the prototype thing all at once. What's also good to note is that there's two notations to access property on an object. And they both allow access to the same property. And there's no differentiation between whether the property is of type function or is simply an object. So this is all mixed up together. So what is prototype pollution? Prototype pollution is a term that was coined a long while ago. People used to do lots of experimentation to add extension method to base types like object. So let's say if you wanted to have a function that's called contains the answer on all objects that are instantiated, you could add it on the prototype of object. And it would add extension method to it. One of the first library that leveraged this a lot was called prototype.js. However, what was found to the usage of this library and other experimentation is that it is actually a really bad idea to do this kind of thing because it causes a lot of incompatibilities between all the libraries that start adding all their things on the prototype of object. So this is now considered a very bad practice. And you're not going to actually see this a lot. So this presentation is not going to be about developers shooting themselves in the foot by actually trying to add extension method on object. This is mostly going to be about what if an attacker can actually start doing those things, can actually start adding property on the prototype of object. So the first thing that we're going to need to ask ourselves is what can even allow an attacker to add properties on the prototype of object. And there's a few operations in which I found there were vulnerable implementation that could allow prototype pollution to happen if we are able to control some parameters. One of the first class of operation that I found to be susceptible to these type of things was merge operation. Merge operation can be seen at a very, very high level as you have two objects with each a set of different properties. And you want to merge them together. And there's also some priority that's given to the last arguments. So basically, we have two objects. It becomes one with all the properties merged together. And the way merge operation that were implemented worked in this way. Basically, they start by iterating all the properties that exist on the second object. And if the property exists on the first and the second object and they are both of type object, it's recursively merge those things. But where it gets really, really interesting is if the attribute's value over here is named underscore underscore proto underscore underscore. Well, this value always exists on object and is also of type object, which means that if we have this value that is defined over here, it's always going to be true that it's going to be defined and existing. And what's going to happen is if we can control on the second side what's defined inside the proto property, this value over here is going to point to the prototype of object. So when it's going to recurse into this, the A value is going to be the prototype of object. And the keys that are going to be set here are going to be the keys that we define in the second part. So basically, if we have this is the base object that we're going to merge properties into, and this is the user input that we control. So here we have a proto property where we added a property named polluted. And when we merge those together with an implementation that's affected, now all instances of object have a property that's named polluted with the value of 1. And this is true not just for objects that are created after the pollution happened, but it also pollutes all the existing object that were previously created. So for those, there was lots of library which had vulnerable implementation. The two most popular libraries that I found to be affected were Lowdash and Oak. For both of these, there were CVE that were released. So if you have no JS project, you probably have been notified by this, but didn't knew too much what this was all about. Also, all the details as to all the libraries that were fixed and which versions they were fixed is all going to be in the paper that I'm going to link at the end. So for this kind of detail, it's all going to be listed there. So the second class of operation that I found to be susceptible to this type of problem are clone operation. Clone operation are basically you have an object and you want to create a full copy of it. And the way I found and the way some implementation were vulnerable to prototype pollution through this operation is if the implemented clone has emerged on an empty object. Basically, this does a clone. However, if the merge operation that's being used to do this is affected by the bug that's previously mentioned, when you're going to be using this clone operation, it's also going to cause prototype pollution to happen with the same effect. For these types, I only found one library which was affected. So this is probably, in practice, a rather niche edge case that's less likely to be seen. The third one that's really interesting is that there's a bunch of library which allows values to be defined by a path. Basically, we start by having an object which has a set of property. And if we want to set object.b.test equals 123, some API allows and some libraries allows us to do this by defining a path. And here, if the attacker is able to control this part of the path, it can start defining the path to be on proto on a score that polluted. And if you control also the value, what's going to happen is that the prototype of object is also going to be polluted. For those libraries, this is essentially something that's working by design. So this is highly unlikely that there's ever going to be a patch. I spoke to a few maintainers which had affected library. And the general consensus is that it's somewhat by design. So for those libraries, you have to be super careful never to allow the path to be fully controlled by user input. So exploitation time. When I started to do this research, this was already some interesting cases. But I wanted to see up to which point it was exploitable in practice. So I looked through a bunch of open source projects that were using Node.js. And the one that I found to be a good case study for this was GhostCMS. At the time I did this research, the latest version was 192. So if you want to reproduce everything that's being discussed here, this is the version that you have to install. It was a fairly large application. It's fairly used. And most importantly, it used one of the affected library with user input. So we actually add something that we could pollute the prototype with. And I wanted to see up to what point this was exploitable. So the first thing we need to do is to identify where the prototype pollution can occur. In the case of GhostCMS, one of the unanticated end points which was used for password reset used the vulnerable merge of load-ash, which is over here. And the portion of the data that we control is options of data. Options of data is essentially, in this context, the body of the post request that we send to that password reset endpoint. So even though we don't fully control options, as long as we control part of the structure, the prototype pollution is going to occur. So it basically gives us a base request like this. So basically we have the password reset endpoint. There's a few values to make sure that we reach the vulnerable merge. And we have here the underscore proto. And over here, this is going to be where all the properties that we want to add on the prototype object where they're going to be. So one of the first things that makes it really hard to exploit this kind of vulnerability is whenever you add a single property, it messes up a lot of the execution of the application. So the biggest problem that you have is whenever you pollute one single property, everything starts crashing. So this is not something that's interesting. If you want to do more than just a denial of service. So the general approach that I took that I think is kind of the good one is to first identify which endpoints as some behavior that you want to exploit. And you're going to try to, first of all, fix it in a way that it's going to finally reach the point that you wanted to reach. And in this case, the target endpoint that I choose was the main page. And the reason I wanted to reach the main page is that the main page actually rendered some templates. And the reason templates are a really good target for prototype pollution is that very often, the way templates are compiled is that they use intermediate structure that are then converted to JavaScript code, then executed. So if we can pollute the properties during the compilation of those templates, we're going to have the potential to have JavaScript code that's going to be executed on the server. So there's a few strategies that we can use to fix all the things that are going to be messed up. Basically, the way we're going to fix thing is we're going to first start by polluting a random property. It's going to crash when we're going to try to render the main page. We're going to figure out why it crashes. We're going to fix that local crash. We're going to retry the payload. It's going to crash again elsewhere. And we're going to continue this iterative process until it finally renders the whole thing. So the first strategy that we can use to fix things is to figure out if there are simply some properties that are missing. In this case of this payload, one of the issues that I add is that during the execution when properties were polluted, the object over here results didn't have any of those meta pagination pages property. So what happened is that it triggered an error which caused the whole page to crash, and it wouldn't reach the endpoints that I wanted. So here, what we're going to do is since meta doesn't exist, we're going to pollute the meta property with this value. And the next time we're going to try this payload, the meta property is now going to exist, and it's going to have those property. So it's now going to work, and it's going to continue further in the execution. The second strategy is sometimes the crashes are going to occur in places where there's not much you can do to avoid the crashes by simply adding properties. So if you're stuck in a situation like this, you have to look at all the conditions that made it reach that point where the crashes is kind of inevitable, and look if there are ways to add properties so that it takes a completely different code path and it no longer takes the path that's going to always crash. The third strategy is something that's really, really interesting about prototype pollution. Is if you pollute a property that's of type object, now all objects are going to have sort of an infinite depth. And the reason is that if, for example, we pollute foo with an empty object, the thing that's going to happen is that foo is going to be of type object, which means if it's of type object, it's also going to have the property foo. So now foo.foo is also pointing back to the empty object, which has a property foo. And the reason this can be really problematic is if there are algorithms in the programs that do sort of recursive operation on object where it iterates the property. If it's of type object, it recurs into it. The problem is that since the objects are all going to have infinite depth, it's going to crash due to Stack Overflow exception. So the program is going to crash, which is not desirable. And the way you can fix those kind of problem is by adding what I would call kind of a stop property. So here, instead of just polluting an empty object, we're also going to pollute the object in a way that it's going to contain the attributes, the property foo. And what's going to happen is now when we do foo.foo, since this value overrides the value that's on the prototype, it's simply going to be of type string. And in most cases, this is going to prevent all the infinite recursions issue. So now that we have done this iterative process until the main page finally renders, it probably renders with garbage data, but that's not much of an issue. We're going to look at what we can pollute to have interesting behavior. And one of the first thing that I notice about the way GoCMS works is that the template to be rendered once the page is finished executing was lazy-loaded. And what's really interesting about lazy-loading is that the value first starts as undefined. And then whenever it sets or it checks whether it's defined, it checks if the value is already exist. And if it already exists, it says, well, it's already loaded. The value is good. I'm going to continue. So here, if we pollute the value that's over here on the score templates, the program is simply going to think that the value was already loaded. And every time a page is going to be rendered, we're going to be in control of which templates gets rendered, which is something that's really interesting. So here, we can control which template is going to be rendered. But this is not the end of the export, mostly because the templates has to have the HBS extension, which means we're not going to be able to do local fire read and other things like that. So we have to pick a template that's interesting. So I first started by pointing to a template that was like TMP. And I did a bit of fuzzing to figure out what was interesting in combination of what the template had to contain and which properties could be added so that some JavaScript code ended up being in the final JavaScript code that was generated for the templates. So what I identified was that partial invocation was the best thing to be corrupted. And there was a value that's called blockparams that ended up being directly in the JavaScript code that was included. I'm just going to take a bit of water. OK, so now once this was identified, I simply had to find a template which contained a partial invocation that could be used. However, when I looked at all the templates that were used by Ghost CMS, when I tried to use these, even if some contains partial invocation, the problem that I had is most of the templates were quite complex. And the compilation, due to the corruptions, ended up completely crashing if it contains too many things outside of partial invocation. But what I found to be rather interesting is that there's a lot of package that are shipped with all their test cases. And one of the package that ships with all its test cases is called Express HBS. And what's really convenient is that all those test cases are a really, really minimal HBS file that can all be pointed to and used. And here, one of those test cases simply contains a partial invocation that I needed and about nothing else. So this was really something that was really useful, because otherwise, I would have to figure out why the compilation for more complex template crashed and tried to fix it, which would have been taken probably a lot more time to finish the export. But here, we have test data that's shipped in production. So thank you, Express HBS. So after this, the properties that we have polluted is essentially the templates that I've linked here. And here, this is like the structure that's used. It's one of the intermediate structure that's used by the HBS compiler. And here, the value over here, this is the value that's going to end up directly being in the final template that's going to be compiled. OK, so demo. Yeah, I don't have my laptop here, but thankfully, I recorded everything. So I'm just going to do Buzz. OK, so wait a minute. This is like not my laptop, so OK. So we first of all start, we have like a blank. This is like a stock install of Ghost CMS. We can see that it works properly. And now what we're going to do is we have our full request with all the payloads. And we have over here what we're going to execute. And what's good to mention is that since the JavaScript code ends up in a context where it's evolved, the require function is not directly accessible. So you have to use this sort of global process, main module, constructor, underscore load. This is roughly the equivalent of require. So here, the payload that we're going to do is we're going to require a child process, and we're going to execute K-Cal, which is like going to make a calculator appear. So the first request is simply going to corrupt the state of the application. So the calculator is not going to pop right now. So what's going to happen is now the state of the application is completely corrupted. And the endpoint crashes. But what's interesting is whenever we're going to refresh the main page, we're going to have the calculator that's going to now be executed. Yeah. And now I still have a bit of time. So how can this issue be mitigated? One of the interesting things that we're going to introduce in ECMAScript 5 is there's actually kind of a way of freezing of making object completely immutable. And this is probably the least used API that was released in ECMAScript 5. But this is actually really useful now because we can actually fix the fact that the prototype of object is modifiable at runtime. So we can actually make the prototype of object immutable. And what's going to happen every time there's going to be a property that's going to be tried to be assigned on the prototype of object, it's simply going to silently fail, which means the assignment is going to be done, but it's going to have no effect. So this can completely prevent a prototype pollution for the object type, which is, I think, something that should be probably the default behavior in JavaScript. Also, one of the interesting things that I've seen some projects do that really prevents a lot of the issue with prototype pollution is to do what's called a JSON schema validation. Essentially, you have endpoints which accept JSON data. You can define sort of a structure that defines which fields are expected, what are their types, and you can also make sure that there are no extra attributes that are allowed. So not only this is going to prevent prototype pollution from being possible, as the attacker is no longer going to be able to control the structure, but it's also going to prevent issues like mass assignment or any other issues where the type expected by the server is not exactly the one. Also in ECMAScript 6, they also introduce MAP, which are essentially a way to finally do MAPs properly because before the way people created MAP was simply to use empty object. However, when you're using empty object, it comes with all the base property of the type object, which has a lot of undesirable effect as we've seen in this presentation. So it's now kind of recommended to start switching object to MAP whenever it's being used for the purpose of being used as a MAP. And yeah, and this is the link to this presentation. It's going to contain all the video, the slides, and also have a white paper which describes everything that I've talked about today and a bit more too.