ambionics / phpggc

PHPGGC is a library of PHP unserialize() payloads along with a tool to generate them, from command line or programmatically.
https://ambionics.io/blog
Apache License 2.0
3.2k stars 492 forks source link

PublicProperties enhancement #196

Open mcdruid opened 1 week ago

mcdruid commented 1 week ago

https://github.com/ambionics/phpggc/issues/195

cfreal commented 1 week ago

Hi,

You're right, it should clearly be PublicProperties, not Attributes.

I was wondering if we couldn't do a cleaner implementation where we intercept the object and convert each of its properties to public using the reflection API, but it does not look like it, sadly.

I am always skeptical about editing the serialized data directly, especially when we change the size of a serialized value. Look at this piece of code, for instance:

<?php

class Inner {
    protected $property = "abc";
}

class Outter implements Serializable {
    public $inner;

    function __construct($inner) {
        $this->inner = $inner;   
    }

    function serialize() {
        return serialize($this->inner);
    }
    function unserialize($data) {
        $this->inner = $data;
    }
}

$object = new Outter(new Inner());
print serialize($object);

Here, Outter has a custom serialisation, so its serialized representation is:

C:6:"Outter":45:{...}

See the 45 here? That's the total size of the internal payload. In our example, the internal payload is the serialized version of Inner. If we were to apply your Enhancement, we'd have the internal size change, and the payload would not work.

Any ideas?

mcdruid commented 1 week ago

I was wondering about changing the objects themselves.. guessing we'd need to use reflection.

I'll make the change from attributes to properties first, and then have a look at switching the properties on the objects rather than fiddling with the serialized payload as a string.

mcdruid commented 3 days ago

I'm not seeing an obvious way that we could convert protected/private properties on the objects in the chain to public before they are serialized.

PHP's Reflection tools can do similarish things (e.g. ReflectionProperty::setAccessible) but you can't then serialize an object you've created and manipulated via Reflection.

In researching that I've also come across some neat tricks you can do using a closure, but again I don't think it's going to help us achieve exactly what we're trying to do here.

So I think we're left with variations on a couple of options:

The initial implementation of editing the serialized string is pretty crude, and as pointed out by @cfreal there may be payloads where it's going to be hard to change all of the character counts appropriately.

I have looked at doing some proper parsing of the serialized data so that you can make changes and then reassemble the serialized payload correctly (see - for example - https://deliciousbrains.com/announcing-serializededitor-com-visual-editor-php-serialized-data/ ) but I'm not sure that approach is going to solve the highlighted problem of a class which does custom serialization.

Manipulating the source classes could mean a couple of different things - e.g.:

Editing each gadget to provide "public property" variants of the relevant classes is probably the least hacky approach, but it's also likely the most onerous in terms of implementation and maintenance.

Any thoughts on preferred approaches? There may well be a better one than any I've outlined here.