Export via serialize/base640:00
Okay, so here we are in our demo application, and here we have an export screen. Now this export screen allows us to export all of the books in the database, and what it's doing is it's using serialize to export the books, and then it's wrapping them up in base64_encode to make it a nice, friendly, shareable string. So we can see the Base64 encoding version here. I've included the decoded version at the bottom to make it simpler to see what's going on here, even though we're not going to use that as part of the demo. Now if you haven't seen the serialize function before, this is going to look kind of interesting, but if you have, it's going to look very familiar. So the first thing you'll notice here is a capital O, and that signifies that we have
different class names nested in it, and so on. And I'm not going to go into any more detail in how it works, that's a bigger topic if you're interested, go check it out. But I just want to point out, there's a few things, we've got the class name here for the collection, and then we've got the Book class in here, which is inside it, and there are multiple Book classes in here, because there are multiple books. And so the object value is telling us all the different things that are in there, the different classes that are made up of this string. So to do the export and the import, what we have to do in this application is grab the Base64 representation up here, copy that, and if I go to the import screen, and I paste
Importing serialized data1:32
So to do the export and the import, what we have to do in this application is grab the Base64 representation up here, copy that, and if I go to the import screen, and I paste that in there and go import, it's going to, for the sake of the demo, it's just doing a dined up. And we can see here, we have the Eloquent class that's been recreated from the serialized string, inside the array we have the five books, and we have all the values from the books all here. So everything is here. And this is passed through my clipboard in the Base64 serialized version of it. And so this is a standard way that you could use serializing, is by doing imports and exports.
Deserialization attack overview2:35
on serialized actually brings it back and recreates it, it can actually run custom code that we inject. And that's what we want to do here, and that's called a php serialization attack, where we're injecting our own custom code into the serialization string, which is being recreated and executed. So there's a tool called the PHP Generic Gadget Chains, PHP GGC. And this is a tool that's designed for building payloads that we can use for deserialization attacks. And so we can use the tool, and we tell it what application we're running, and if we scroll down, we can see it supports a whole bunch of different gadget chains and a whole bunch of different frameworks and applications.
Building RCE payload3:38
This allows us to do what's called remote code execution. So again, rather than me talking, I'm just going to show you an action, because it's far easier to understand when you actually see it happening. So what I'm going to do is jump over into a terminal, and then we'll build our payload. Okay, so we're in the terminal, and we're in a simple php shell. And in here, I'm going to run the, I'm going to build the payload using phpggc. Okay, so I've used the command up here, and we're running phpggc, we're going to use the Laravel RCE6, and we want it to die and dump the value of the config. So we can see the output here of the payload, we've got our object up here, we've got message bag, and there's a bunch of nested objects, including the interestingly named eval loader.
So we can see the output here of the payload, we've got our object up here, we've got message bag, and there's a bunch of nested objects, including the interestingly named eval loader over here. And as you can see down over here, we have code, and this is our payload, this is the remote code we want to run. Then we also need to wrap this in base64, so we can inject it into our importer. So base64_encode payload. Okay, so now we have base64 to inject into our importer, just going to grab that, and then jump back over to the browser. Okay, so now we're in the browser, and I'm going to load the base64 in here, and click
then jump back over to the browser. Okay, so now we're in the browser, and I'm going to load the base64 in here, and click import. Okay, cool, so now we have our die and dump, but you'll notice down here we've die and dump the config repository, which is what our injected code was. And if you look over here, this handy little thing from Laravel is telling us the eval loader is what triggered it with eval. So now that we have running our own code, and this is the code that we put in the gadget chain. This isn't code in the application, but we're running our code, our injected code, which
Getting a reverse shell5:34
the API keys for. The point is that we now have access to the config on the site, but it's not just that. We have full access to the code on the site. We can run whatever php code we want, which gives us full access to the site. We can create records, we can change values. We can also get access to the server, and this is a really neat trick. So what I want to do now is run a remote connection, a remote shell. So tell the server to try and connect to my computer so that my computer can reconnect into it, then we'll get access to the shell. It sounds complicated.
into it, then we'll get access to the shell. It sounds complicated. I'll show you what I mean. So first of all, I just want to go back to our import screen, make that all ready to go. Now we'll go to the command line and we'll run our new payload. Okay, so we're back in here and we're going to run this payload now. So we're also using the same RCE6 here again, but rather than passing in a DynDump of config, we're going to run execute. So we're going to get php in the application to execute command, which is going to be a
So what I'm saying here is I want to listen on port 1, 2, 3, 4, and if you notice up here, we have port 1, 2, 3, 4, and this is the IP address of my laptop as seen by this web server. So I'm going to run that, and then I'm going to jump to the web browser, and in here I'm going to put in our payload, import that, it's running. I'll jump back to the command line. And now we have access to the service. This is actual command line access to the application. And we can see the PHP files there in the public directory. If I go back a level and run that, we can see there's a whole bunch of other files in here.
Mitigations and safer JSON8:11
So what do we do? How do we solve this problem? How do we avoid this? Let's jump to the code. Okay, so in the code, so how do we avoid this? And the best thing that I can suggest here is don't use serialize. serialize is incredibly useful for a lot of things, but you don't want serialize being used anywhere where the value can be accessed and modified by the user. It's fine to use for some things like the Laravel's queue, for example, or for caches or things like that, where the user can't access the value.
It's fine to use for some things like the Laravel's queue, for example, or for caches or things like that, where the user can't access the value. And Laravel's cookies use serialize, but they're also encrypted, which means that you have to be able to decrypt the cookie in order to modify the serialized value, which you can't do because you don't have the encryption key. But if you do need to transmit data around and do exports, either encrypt the value so that it cannot be decrypted, and so there's no way that it can be modified, but then you need to worry about the encryption keys. A better way to do it, if you're doing an import like this, would be to use JSON. So then we can do this, json_encode, and that's going to produce a much safer value.
A better way to do it, if you're doing an import like this, would be to use JSON. So then we can do this, json_encode, and that's going to produce a much safer value which, sorry, we have to decode it here as well. So json_decode, there we go. So if we use JSON here, what's going to happen is rather than passing around a serialized value, a value that is potentially vulnerable to be changed and modified and inject stuff into our application, we now have JSON, which provides safe data. Granted, JSON isn't going to know what classes there are, so you do need to manually reconstruct the things that you're passing around, so that is a downside. But from a security point of view, it is significantly better because you don't have all that extra
the things that you're passing around, so that is a downside. But from a security point of view, it is significantly better because you don't have all that extra data. And if we go to the web browser, and in our exporter, and I grab the exported data here and then go to the importer and paste it in, as we can see here, we now have just flat arrays, as we would expect because we're using JSON to pass the data around. So we don't have the metadata from those classes, but we also don't have the security risk. So that would be my recommendation, is unless you specifically need to use serialize, then don't touch it.
