 Okay, good morning. Good morning, welcome again to our second talk of the day. Our next speaker is a security researcher at Checkpoint and he will be talking to us today about exploiting PHP 7 Unserialized Mechanisms. So please give a big warm round of applause to Yanai Lignac. Okay. Hi. Welcome to my talk teaching the new dog old tricks. It's about PHP 7 memory internals for security researchers. And I must give a disclaimer first, this is not a talk about PHP code, it's about PHP 7 interpreter. So I hope you enjoy it. About me, my name is Yanai. As already said, I work at Checkpoint. My team and I work about many stuff and networks, embedded devices, exploit sites, mega-client sites, exploits, and also memory corruptions of all sorts. And today's agenda, first we'll have a brief introduction about PHP and then we'll talk about PHP Unserialized Mechanism. After that, we'll talk about the implementation of the Zivo system, the value system of in PHP 7. Now, gaining this knowledge of Unserialized and the Zivo system, we'll see some bugs and vulnerabilities. After that, we'll discuss the internals of the allocator in PHP 7. It's a new allocator. And using our knowledge of the allocator, and we can see how we can exploit the bugs and write remote code execution exploits. Then we'll have conclusions, questions, et cetera. So let's begin. PHP is the most used language in web applications today, for writing web applications. And basically, it's server-side language and servers rule the world. All our information, all the data we have in web applications is in the server-side. So it's very interesting as malicious attackers want to get some information to attack the servers, usually. And when I said it's the most used language today, today, the most popular version of this language is version five. But in the last year, it came up a new version was released, PHP 7. And this is what we're gonna talk about today. So it's one of the most important languages today and what about the security of PHP and PHP applications? Well, like every popular, yeah, every popular language has vulnerabilities written into it or written by it. So we know many vulnerabilities of SQL injection in recent years and XSSs. These are high-level vulnerabilities and it depends on the code you're writing to your web application. But there are also memory corruptions. How can you have memory corruptions in a high-level language, you ask? Well, some of the functionality of PHP is implemented in the interpreter itself, which is written in C. And when you pass user input to this kind of functionality and you have bugs there, you can get some memory corruptions. The most notable example for this is the un-serialized function, which has been exploited many times over the recent years. And this is exactly the function we're gonna talk about today, but in the new domain of PHP 7. So what about the history of un-serialized, well, too many CVs, actually. And most of these vulnerabilities were exploited in object injection. So property-oriented programming, if you heard of it. And this is a logical exploitation. It depends on the application that is used, how this application, the PHP application was written. But there also have been memory corruptions. And memory corruptions does not depend on the application. Memory corruption exploitation depends only on the PHP version. As I said, version five or version seven. And for memory corruptions in PHP 5, there was a generic exploit developed by Ionic, Stefan Esser. It was described in blackhead 2010. And this exploit could basically work on every PHP 5 memory corruption, un-serialized memory corruption you had. So as I said, it's a very old problem, but here is an example from this year on July. This small website, I don't know if you're familiar with it, opened the bug bounty. And this bug bounty attracted attention of a company called Evil Knight. They looked at the API and found this API call, a post request, and you can see a cookie here. And this cookie has a very interesting format. This is the format of un-serialized. And of course, when you send the request, you see that the server will actually process this cookie. So we have a vulnerable un-serialized right there. And what they did, they fuzzed un-serialized in version five in this instance and found a new vulnerability, exploited it, got the bounty. So what about PHP 7 I'm talking about all the time? Well, it's a new release of the language. It was released just a year ago. And they kind of re-implemented everything for performance. So they re-implemented the value system. They've re-implemented the memory allocation mechanisms. And for this reason, the exploitation technique of the memory corruption does not work anymore. Exploitation of memory corruptions depend heavily on the memory layout. And when you change all of that, nothing works again. But still we have the un-serialized functionality in PHP 7 because you have some sort of backward compatibility. And there have been some CVEs, object injection still works being purely logical. But the memory corruptions, while still there, you can still find memory corruption vulnerabilities in un-serialized, you don't have any remote exploits until this talk, more or less. Yeah, thanks for me. So let's talk about un-serialized. And un-serialized, first, we have to, I have to make something clear. You should say dis-serialized. Un-serialized is grammatically incorrect. And if it bugs you, well, that's how it's gonna be for the rest of this talk. So I'm sorry, but PHP being PHP, we can go on. And this is the documentation of the serialized and un-serialized functionality in PHP. Basically, what serialized does, it takes values and converts them to a string that you can store and retrieve later. And un-serialized is the reverse operation. So you get a string in a specific format of un-serialized, you convert it back into a value. This is very simple, you would say, but there have been too many vulnerabilities there and you'll see exactly why. So let's have an example of serialization, okay? Assume we want to serialize this nice array that has another array inside of it and so on and we'll go step by step and serialize it to un-serialized format. So first we're serializing an array with four elements. So as you can see down below, we have the string and it's a colon for a for array colon for values. And in PHP, every array is actually a hash table or a dictionary, so it's a key value store. But when you have an array, the keys are implicit. The keys are just a running index and it's implicit. So we're serializing the first entry. It's integer zero as key, so you have icon zero and null for the value. Next, we're serializing integer one for key and integer one, three, three, seven as the value. And then we're serializing a string. So we have integer two as the key and string. So we have S for string, colon five for five characters, then we have Apple. This is the value of the string. Next, we're serializing another array. So we have the implicit key, integer three and the value A colon three for three values in this nested array. Next, we have an explicit key, string A, so we have S for string, colon one for one character, A for the value of the string and this is the key and the value is integer one. Next, we're serializing a class and object actually. So we're going back to the implicit keys, so it's integer zero as an implicit key. Then we have object that the class name has three eight characters, so it's colon eight, colon the class name, STD class. Then we have colon zero. Zero is the number of properties in this object. Zero properties, nothing in there, curly brackets with empty properties. Then we're serializing integer one as key and integer seven, three, three, one as value and this was the process of serialization. Quite straightforward and we have this nice format that we know about. Next, I wanna show you an example of un-serialization and this would be the exact reverse, so it's gonna be a bit boring, but I want to show something here. PHP supports a feature called references and references means that during un-serialization, in the format, we can specify, instead of specifying a value such as integer or string, we can specify a reference and this tells the PHP mechanism, go back, see what was the nth value we just parsed, we just un-serialized and I want a copy of this value or a reference to this value. And in order to support this feature, during un-serialization, every value that is parsed by the un-serialized mechanism is kept a reference to. So we have this array called the var hash array and in this array, we have pointers to every parsed value and let's see how it's done. So first, we're un-serializing an array with four elements. So you can see in the var hash array and it's a one-based array, you have pointer to this array we just instantiated. Next, we have a key integer zero and value null and we can see that the second pointer points to the null we just parsed. Next, we have integer one, a key integer one, three, three, seven as a value and the third pointer points to one, three, three, seven. Then we have the string apple as the value and the fourth pointer points to this value. Next, we have this array. Now this is an eternal array, but the var hash array is a flat array. So even though we have some nesting here, we have array within an array in the value. If the values are nested, the var hash array is flat. And the fifth pointer points to the array. Next, we're unserializing the key a and the value one and we have another pointer to the value. Then we're instantiating the STD object. So we have another object there and the seventh pointer points to this instantiated object. And now something interesting is going to happen. We're unserializing a reference to the third value. So what would happen here? PHP, the unserialized functionality will go and check the var hash array. Go to the third value. The third value here is the pointer to the integer 1337. And now we need to create a reference to this value. So this is what we do. We unserialize this value and we create a reference to a previously parsed value. So this is how PHP supports this feature of references, of backward references. And this mechanism, this feature caused many bugs in the past because as you can see, we keep pointer to stuff we maybe need. Maybe we don't need it again. And there has been some confusions over the years. If you got lost in this complicated format, there are some points I want you to remember. So first, it's a complicated format, okay? Yes, obviously. And complicated formats leads to bugs. Lead to bugs and we're gonna see it soon. We have many, many ways to control allocations, right? We're instantiating objects, we're instantiating, we're unserializing strings of various lengths. We're unserializing hash tables of various lengths so we have lots of control as users. And we keep these references things so we keep references backwards to things we unserialized in the past and we can reuse them. So in this case, we have some use after free. We manage to free some object in some magical way. We still have pointer and we can go forward as exploiters. So now we talk about the Zivo system in PHP. Now Zivo are basically values, okay? This is how the PHP interpreter calls values or holds variables. This is a PHP variable. I'm sorry that we have PHP in this talk but there won't be much of it any soon, so anytime soon. And as high level language, we have a couple of features for our variables. One, we have automatic memory management or garbage collection and we have these references. Reference when Y is a reference to X, it means that the value of variable Y changes when the value of variable X changes and vice versa. So they both basically are variables holding the same value. And this is how the Zivals were implemented in PHP 5. We had this struct called Zivo struct. It was on the hip, it was reference counted and every Zivo was basically a pointer to this struct and every time we created a new Zivo, we had to allocate this struct on the hip and initialize it. The memory management was quite obvious. We have reference count and we have memory cycle detection. So it was a very simple algorithm which most people know about. And to make references, to make two variables point to the same value was just two pointers pointing to the same struct on the hip. Everything was very simple, very nice. But it was slow. So when implementing PHP 7 and there have been some motivations to change it and the implementers wanted less references. Now, if Zivo is a pointer, every time you need to reference a value, you have to de-reference the pointer which can be expensive. Also, every time we create a new value, even if it's for a short time, even if you know exactly where it is, we still have to make allocation on the hip which can be expensive. So they designed the system for embedding. They said, okay, I don't want Zivals to be just on the hip, I want to be able to embed them everywhere I want, on the stack, in other structs and so on. And this is how the new Zivo system is implemented. We have this Zivo struct, but now it's a bit different. It's simpler. Zivals are just this struct and this struct contains only value and type. So there's no reference counting and we can put this struct wherever we want. And the value thing here can be one of two things. It can be a primitive type. For example, a primitive C type like integers or floats and so on. But PHP supports also strings. So if you want to support complex types like strings or objects, this value would be a pointer to a complex struct and this struct will be on the hip. So this struct will not be on the stack. It will be somewhere on the hip and it will also be reference counted. So here is an example. This is an integer and in memory we'll just have the value one, three, three, seven and the type long. That's it. We don't know where it is. And it's quite simple. Now, to support reference counting, now it's the responsibility of whoever initializes the Ziva, right? If the Ziva is in a struct, whoever initializes the struct needs to clear it up. But there is one complication here. If the value is a primitive value, everything is good. But if it's a pointer to another struct, this struct should be reference counted. And when it's referenced, and this is how the garbage collection works, it checks the value pointed by the Ziva and it has also reference count and garbage collection. Now, let's see an example. Okay, so this is the string struct. As I said, it's a complex type. So this struct is on the hip and is pointed by a Ziva. As you can see, the first field in this struct is the reference count. And the last field is a flexible array member. Array member. So when allocating a string, we allocate more data, enough memory to store the string content itself and we access it via this field. So this is what it looks like in an array. When we're initializing an variable of type string, then we have the Ziva struct. It's of type string. We're allocating the Zend string on the hip. We initialize it with the values. You can see we put the value Apple here and we just point the Ziva to this string and the reference count is one. And this struct is on the hip. So how can we manage the references? How did the references feature was implemented? This time we cannot just make one Ziva point to another because they may be in different scopes. One can be in a struct, the other can be on the stack and they must be managed, right? So there is a solution here. In PHP they implemented another type called references and it's easier to show than explain so I'll show how it works. Soon you have this variable X, which has 1, 3, 3, 7 and now we have the variable Y which is a reference to variable X. Okay, so we have it in memory and I want to make a reference to it. So we initialize a new struct on the hip called the reference and this struct has two fields. The first field is a reference count and the second field is another Zival and we initialize this Zival value to be the same as X. So you can see it contains a copy of the value. Then we change the type of X to be a reference and now it points to the Zend reference and now we can initialize Y and Y will be the same so it also be a reference to something on the hip. So we moved the contents of X to the hip and now we can have the two Zivals X and Y be just pointers to the hip like previously. The key things to remember about Zivals is that they're designed for embedding so they can be in many places which can cause some bugs. The motivations or goals were achieved. There are less references and less hip usage but making a reference is a bit complicated. Reference is not a feature that it used too much except in exploits. So this, yeah, so there is trade off here between Zivals are now more simple but reference are more complicated and it's a worth trade off, in my opinion at least. So now that we know about Unserialize and we know about Zival, let's see some bugs and vulnerabilities. So first, the use of an initialize value. Now, since Zivals used to be pointers, initializing pointers was very easy, right? Everybody knows how to initialize a pointer. You just point it to null and you're done. Initializing structs is a different story, right? We need to initialize them with some macros or maybe some constants. It may take some time and programmers tend to skip this phase if it's possible. You don't initialize your structs, which is a problem and this can lead to some vulnerabilities and this is the exact example here. We have this function, the SPL object storage Unserialize. And this function is the custom implementation of Unserialization of the struct SPL object storage. This class is implemented in C code. Some of the classes in the SPL, the PHP standard library are implemented in C and this is the case here. And as you can see in the first line, two variables are declared, entry and inf. These are Zivals, so they are type Zival struct and at no point in this code, they are initialized. And after a few lines of code, we reach this statement calling the function PHP var Unserialize. This is the C implementation of the Unserialize function and the first value is a pointer to this inf that was never initialized and this is supposed to be an out variable, but it's not, okay? So, but this is where we're gonna store the string we're Unserializing. The next variable is P. P points to the string we're Unserializing. So this function, what it does, it's Unserializes the string pointed by P and stores the value in inf. However, internally, we reach this line of code, Zival pointer destroy, which means please destroy the Zival, which is pointed by Rval, right? And what is Rval in our case? It's inf. It's exactly the thing we did not initialize at no point. And the reason the code does this is to make sure there is no value in this Zival before writing new values. But since this value was never initialized, what's in this value is garbage from the stack and if you shape your stack correctly, you can cause some bad things to happen. So this was CV 2016, seven, four, eight, zero. Okay, so the next example is type confusion and we are going to rely on the fact that making references is very complicated process, as you see, and it changes the type of the Zival, okay? And we can suspect somebody forgot about this possibility and this is exactly what happened. So we have the same function again and you have more lines of code from the same function, but it's the Unserialize of the SPL object storage. And as you can see in the second line here, we are calling the internal Unserialize and we're again converting the string pointed by P and store the result in the Zival entry, okay? Two lines later, we're checking the type of what we just unserialized, making sure it's of type object. So you can see that if the type of entry is not object, the function bails, bad things happen. But if it's, nothing should happen, but if it's of type object, we can go on and in the last line we're unserializing the next value, P is advanced here and now it points onwards in the string and we're converting the string pointed by P and store it into inf. But what happens if inf is actually a reference to entry? Well, the type of entry is going to change and let's see how what happens. So first we're unserializing entry and it's an object and we're checking and this is actually an object and everything is good. But next we're unserializing inf and inf is a reference to entry. So according to the process, first we're allocating a Zend reference on the hip and we're initializing to be the copy of entry. Then we're changing the type of entry to be of type reference and point to this reference. And of course, lastly, we're initializing inf. But in this process, we actually changed the type of entry after we checked it. So this is a bug and actually bad things happened. It's not exploitable, but it's still a bug and it was fixed. So this is about seven, three, two, five, eight in PHP system. Okay, so this is the last bug for today and it's a use after three. And the problem stems from the fact that now Zivals are embedded in structs and we keep pointers to these Zivals but structs have their own rules of how they change and some structs tend to change the place in memory without telling anything else. And if we keep using these pointers, we're actually pointing to a memory which is not valid anymore. So such a dynamic struct can be a hash table, for example. And this code is a processness data function. This function is a function that processes every hash table during unsterilization. And hash tables can be also properties of an object. Okay, and here in the first line, we're declaring the variables and the next line we're adding a new entry to a hash table. So what we do here is zand hash add new, we're adding a new entry. The first parameter is the hash table and the second parameter is the key. And the result of this function is a pointer to the Zval that is the value. So we get data points to this Zval we need to fill. And then we're calling the last line, last line calls the internal implementation of unsterilize. And again, it converts the string pointed by P and stores its value in data where data is pointing to. But look at the last parameter here, it's the var hash, this array we talked about in the previous part. And var hash keeps pointers to every piece of info, of every value we parsed, including data. So data points internally to the hash table and it is stored in the var hash array. But what happens if we manage to insert another value to the hash table? Then the hash table might resize, right? Hash tables grow sometimes in memory. And when they grow internally, they reallocate their memory to store additional values. So this is how it looks like in memory. First you're unsterilizing an object and this object has some properties. For example, it has two properties, zero and one. And then magically we manage to insert another value to the properties hash table and it reallocates. So now the var hash points to the new value that was unsterilized, but the two previous values are not valid anymore. And this is not very common scenario. It's quite hard to exploit this or to trigger this vulnerability actually. Because if you remember the format before unsterilizing an object, there is the number of properties in this object. And PHP makes sure the hash table has enough capacity to hold all these properties. Yeah, there are two ways to do trigger this vulnerability. The first one is the wake up function. And this is a function that objects can define. And this function, this method is called after the object has been unsterilized. And if for some reason, this function defines a new property on the fly, then the interpreter is not aware of this beforehand. So inserting a new property on the fly can trigger this vulnerability. And also we have my all times favorite object in PHP or class, the date interval class. And this class has a very interesting behavior. It does not initialize its properties hash table beforehand. Or it actually only when accessing the properties of this object, then it defines and adds its properties to the hash table. So if we manage to come to some functionality, such as to string or wake up that touches this object or some object's properties, such as date interval, we can trigger this vulnerability. And this was CVE 2016-7479. So these bugs are complicated. There are still unsterilized vulnerabilities. Even between the time I submitted this talk and today, there has been at least one more that was reported and published. The vulnerabilities are a bit different than what we used to see because this evil system is a bit different, but still memory corruptions. And these vulnerabilities let us use freed values. As we've seen, the two vulnerabilities let us reuse the freed values. And this is a property that we're gonna need in order to exploit these vulnerabilities. So after we know about how the system works internally and how the evil system works and we found some bugs, we gonna talk a bit about the allocator. Because when exploiting this kind of our corruption vulnerabilities, we need to know how the allocator works, how memory looks like. So we had the previous allocator of PHP 5 and it's not too important, but it was a heap-based allocator. And every slot, slot is the allocation unit in PHP. Okay, so it's called slot and it has a size and flags to each allocation and also a fill list for caching. And it was very fun. And it was nice for exploiting. But now we have a different allocator. The allocator here is, it's a complete rewrite. There is nothing similar. And it features beans or memory pools, as you know them. And fill list. This is basically how the allocator works. These are the basic building blocks. And I'm gonna throw some details at you and you probably see them or not. The allocator works in chunks of two megabytes from the operating system. And these chunks are divided into pages. The first page is pagescript array. It describes the chunk. It has a couple of structs. The two important ones is the one describing the pages which pages are used or free to use and pointers to the beans or to the free lists that were initialized. And every bean is a fill list of specific size. We have the 32 bytes bean. We have the 16 bytes bean. And it can spend over multiple pages. Now, let's say it in a way you can actually remember. So, here is a chunk from the operating system. It's of two megabytes. And it is divided into pages. First page is the chunk descriptor. You can see the three slots. It is just array of pointers to free lists and page info for each page. And this is what the bin looks like. So, bean, it can spend over multiple pages. This is the 16 bytes bean. And we initialize it as a fill list. So, every 16 bytes points to the next bytes. And we register it in the chunk descriptor. So, we have the page information, 16 bytes, spends over two pages and pointer to the free list. Now, the allocation algorithm is quite simple. First, we assume we want to allocate a slot of size 30. Then you check to which bin it belongs to. I think it belongs to bin number five. Then we check if there is a free list available or is a bin initialized. If there is no initialized bin, we initialize a new bin. And then we just pop from the free list. So, assume you want to allocate 30 bytes. There is no 30 bytes bin available right now. So, we're initializing a new bin and then we just pop from the free list. So, we want to make another allocation of the same size. It's even simpler. We just pop from the free list. Frame memory is the inverse operation. So, we get some pointer. We want to free it back to the pool. So, we check to which chunk it belongs and which page within the chunk it belongs to. We read the chunk descriptor to know to which free list to push it back and we push it back to the free list. So, assume we want to free the first slot of this bin. Then we just see it belongs to the third page. We see the third page is of bin 32. We go to the free list and we just push back. What I want to remember about the allocator is that the allocator is very, very predictable, which is important for us as exploiters. And it's impossible to free just random pointers. This was used in the previous exploitation technique, but it's not possible anymore because we have some bit operations and lookups and it's hard to forge memory to look the correct way for this to work. And we can abuse free list pointers to get arbitrary right. So, we can change them if we can change the free list and then allocate a few slots we might, we can allocate to the place we want to and I'm gonna explain it much more later. So, now that we have vulnerabilities and we know about the allocator and how it works, it's time to write an exploit, right? And every exploit has a few stages, the most common on our leaks. So, we want to leak some memory information to bypass the SLR. Then we would like to read some memory so we can read the data or gadgets, function pointers, so on. We would like to write, maybe write our shell code, maybe write some other stuff and we would like to execute code. This is what we're here for. And first thing, we're gonna talk about the leak. I think it's the most complicated part and we're going to abuse the allocator. It's our best friend here. It's roughly based on the previous exploitation technique but it's a generalization of it and we're gonna use our ability to serialize freed objects and we believe we can serialize free objects, not only use them because this is how we initially started, right? We have un-serialized. If we're un-serializing something, we probably, it was serialized before. So, we have some kind of mechanism here that we serialize something and un-serialize and serialize it back and so on. The allocator, if you remember, is a frill list. So, it overrides everything that is passed to it. So, if we free pointers, the allocator is going, free slots that the allocator is going to override them. And after we freed and we used them, we can read via these pointers and read some data that was also freed. So, this is how it's gonna work. The allocator is a frill list and the first pointers in these slots, in the free slots is the next slot. It's just a pointer, right? So, here is the free slot struct and you can see the first field is a pointer. And when we read freed objects, we can sometimes read via this pointer. I mean, we freed the slot and we're still using it so we can read via this pointer and read to the next slot, next free slot. So, here is my all-time favorite class in PHP, the date interval class. And as you can see, this is how the struct is implemented in memory and the first field is a pointer. How lucky we are, right? And it points to this struct. And this struct is a very simple struct. It has many fields, but all of them are integers. They're not only integers. Integers, SLL is signed long, long. So, they're also un-serialized and written back to the user. So, when un-serializing this class, we're actually getting these numbers. The year, the month, the day, and so on. So, how are we gonna forge a leak in practice? First, we're going to allocate this date interval and we will allocate an object to leak. For example, a string. And then we're gonna free both objects. First, the string, then the date interval. And the allocator is gonna override the date interval with a pointer to the string, to the freed string, because this is how it works. And the allocator is also gonna override this string with some pointers. So, when we serialize this date interval, we're gonna read these objects. And here's some hex dump for you, because you really wanted it. And here is a, you can see the free list before we start our exploit. And now we allocate a date interval. And as you can see, it points to some struct that I initialized with minus ones because it doesn't matter so much. And we're also allocating or un-serializing a string, one after the other. And now we're freeing the two of them. And as you can see, the date interval pointer, the first pointer now points to the freed string. And when I'm serializing this date interval, actually get the values in the string. We'll get these pointers as integers, which is very nice. How can we read memory? Well, we have this very nice struct already. So, if we can control this struct fully, if we can control as evil fully, we can just change this pointer to whatever we want. So, pointing the first field in the date interval will let us read any kind of memory we want. If we don't control as evil fully, which can be the case, in 64-bit it was the case, there is a more complicated way, and I'm not going to discuss it here, but basically we can use a different object, the date period object, which has a first field as a pointer, which points to another struct and in this struct, we control another pointer that is passed to SDR copy on un-serialization. So, we can read memory with SDR copy, and you can see all the details in the paper I published earlier this year. I'll give you links in the end of this talk. How can we possibly write memory? Writing memory is much more complicated here, and this is what we're gonna do. We're gonna free some strings back to the allocator, and in the strings, we're going to write pointers to wherever we want, whichever memory we want to write. Then we're gonna abuse the free list, and we're gonna change the pointers in the free list, and going to increase them or decrease them, depending on our vulnerability, onwards in memory, and at some point, if we increase them enough, they will actually point to this memory we want to write to. So, after a few allocations, we'll actually have the memory allocator give us the address we want to write to. And this is exactly what we want to happen, so how can we free these strings? As we said, every value we're parsing in un-serialized gets a reference in the var hash array. So, we have reference to almost everything, and nothing is freed during un-serialization. Next up, there is one exception, and it's in the hash table, we can use the same key twice. Keys are not kept references to. And if we're using the same key twice, and the keys can be strings, the second time we're using this key, it is freed. The hash table looks at the key and says, hmm, I've already got this key, I don't need it anymore, and freeze it back to the allocator. No references to it are made, and it's back in the allocator for us. And the next thing we need to know is how to abuse pointers in the free list. This is the most interesting part here. And the reason it's possible, and it's very common, is because we have type confusion here, right? We have the free slots, which the first field is a pointer, but we also have the something that was freed, the value that was freed, and we still have a reference to it. And on the heap, every struct on the heap has a reference count, and the reference count is always the first field. So if you take the zend object, for example, a destruct first field is a reference count. So if we have a type confusion between an object, so not a type confusion, but if an object was freed and we can still use it, increasing the references, or decreasing the references to this object actually increases or decreases a pointer. So this is how we can control this pointer. And some more hexes, so this is part of the exploit I wrote to bug 71311. And here you can see that we already freed the strings back to the allocator, so you can see the full ones in the hex dump. And now we're gonna trigger the vulnerability. The vulnerability here lies in the array object class, and parsing it increases the pointer in the free list by two. And now we have use after free of this address, the first that is pointed by the free list, it's still in the allocator, but we have a reference to it. And it was the 11th value that we parsed. So adding references to this address, so r11 parsing r11 and so on, we're increasing this pointer by two every time. And at some point, reach the value we just freed back to the heap, or back to the allocator. So now if we allocate twice from the spin, the first time we'll allocate the object that was corrupted, and the second time we'll actually can write to 414141 and we will seg fault, but there's exactly what we wanted, right? So code execution, after all this, code execution is quite simple. If we're controlling your Zivl, as I said, the easiest way is to override a callback, right? You control some object, you can decide what callback it has. But if you don't control Zivl fully, you can just, we have a right primitive, so we can write the function pointer, and that's about it. So takeaways from this exploit. The allocator is your friend. Please use it, use it wisely. And these primitives are reusable. They can be used in many different vulnerabilities within the unserialized mechanism. And combining all these primitives, you get the remote code execution. Conclusion from the talk. Even though you're writing your code in high level language, such as PHP, you're still vulnerable to memory corruptions or other low level problems. The new design of PHP is for efficiency. You still have vulnerabilities. I don't think it's more secure or less secure in a way. And the allocator is exploiter friendly, please. When you're writing an exploit, use it. And never, never, never use unserialized. This function is a remote code execution, basically. Every time it was somewhere in a reachable code, it was exploited. So more information you can find in checkpoint blog and also in the description of this talk, I have a link to the paper I just published. And there are gonna be another publication soon about the vulnerabilities I talk about, more information about them, and also a third vulnerability, which I did not discuss here. PHP bug system is a second link and there is every bug recorded and every vulnerabilities. Some of the bugs doesn't have CVs, unfortunately. And the third link is Nikita Popova. I think he pronounces them correctly. Blog, he's a main contributor to PHP and he has many explanations about internal mechanisms within PHP. If you wanna contact me, so you have my corporate mail, my Twitter, and I'm in most other platforms, such as Gmail and so on. Couchsurfing, I don't know. So that's it. Any questions? Thank you, and I, if you have questions, please line up next to the microphones and ask your question. If you're leaving, please do so quietly. Any questions from here or from the internet? People are overwhelmed. So I'll start with a question, actually, because I have one. I didn't understand the internals too much. I'm interested in applications of exploiting things that you talk about. Have any of this in the wild? Are we expected to see this in the wild? Actually, PHP 7 is not very widely used by now. I think it's 2%, it's just a year old. But as soon as the big platforms, the big CMSs will move to this new language, I predict we'll see some of them, other vulnerabilities, but I assume we'll see more exploits coming. Questions from the audience or from the internet? Internet has a question, yes. Okay. Yes, are there any popular projects that are affected by these vulnerabilities? Not that I know of, not yet, because PHP 7 is not widely used by now. There are no big CMSs using PHP 7. Sorry. Internet, if you have more questions, then go ahead. Anyone from the audience? Last chance? Okay, so again, please give a big round of applause to you and I for a good talk.