In a typical PHP-5 deserialization of use, we will use a dispenser to cover a pointer to the string contents of the pointer, thus reading the next stack slot. However, in PHP-7,The internal string representation is different.
In PHP-7, The basic structure of the struct zval internally a pointer to a structure zend_string, so that the reference string. zend_string instead use the member array, the string is embedded into the structure at the end. Therefore,point directly to the string content of the pointer cannot be overwritten.
However, PHP-5 technology might make the pointer leak. If a structure of the first field point to some we can read the content, we can for the structure is allocated and released,then distributor will make it points to has previously been released slot, this will allow us to read some memory.
Fortunately, the internal representation of php_interval_obj structure of the DateInterval object is very useful. This is the definition:
timelib_rel_time is a kind of simple structure, no pointers or other complex data type,only the integer type. This is the definition:
Let payload memory leak occurs:
The result is:
If we do some formatting work,you can see we have been reading up some memory. After obtaining a struct timelib_rel_time field of offset after,we read the following values:
So,we can infer that memory is like this(note,in the serialization process, timelib_slll field is int rounding):
In 6 4-bit version,we leak the initial information of the method and in 3 2-bit version is the same. But in the 6 4, This method is more useful,because we will be the entire memory are converted into int type, and there is no truncation.
Therefore,we need to create two DateInterval objects, and then use the first object to read the second object's memory(rather than reading useless string).
In the previous content,we leaked the stack and code address. We need to do now is to read the memory address, thereby acquiring enough data to build a
Now, read arbitrary memory becomes a little tricky,because we both can't fake an array, can't control it in the field(we can't control arData)。 Fortunately,we can use another object:DatePeriod in.
DatePeriod is internally represented as php_period_obj structure. The following is defined:
Note that the first field,this is a point timelib_time structure pointer. When this object is released, the structure of the first field will be the dispenser cover, which becomes the same size as the point has been the release of the structure pointer. Therefore,after the assignment,the engine will read timelib_time it. The following is timelib_time definition:
We see that tz_abbr field A is a pointer to a char, a string pointer. In the DatePeriod object is serialized when,if zone_type is TIMELIB_ZONETYPE_ABBR(2),so tz_abbr point of the string is strdup to copy, and then be serialized. This to the Read primitive is applied to a number of restrictions,each time we only read one NULL byte.
Now, we need to find which object is in the DatePeriod before being released.
If we want to read 0 x7f711384a000,you need to send this:
We can see,timelib_rel_time within days of the field offset and timelib_time within tz_abbr is the same.
DatePeriod filling is the last step, at the same time it is also the most complex. When the DatePeriod object is serialized when,date_object_get_properties_period function will be called, and returns a properties HashTable serialized. This HashTable is zend_object the properties field(embedded in the php_period_obj within the structure),it will be in the DatePeriod object is created when it is assigned. In this HashTable is returned to the caller,the function will use php_period_obj within each field to update the value of this hash table. This sounds simple,but imagine, in the release DatePeriod object, the HashTable has been released, which means it's the first byte is a pointer to the free list pointer. In order to understand the damage caused by the impact,we need to understand what PHP is how to apply the hash table.