When the hash_update_file() function is called it first retrieves the resource data for further processing. It then reads data from the stream for hashing purposes. A malicious userspace stream hanfler can destroy the hash resource and replace it with a specially prepared fake resource that contains a modified hash function pointer table. When the function continues hashing it will call the overwritten function pointer and therefore execute the shellcode.
Affected is PHP 5 <= 5.2.1
When PHP functions need to keep track of data structures they register resources with the Zend Engine. The resource system has reference counters but those only keep track of the PHP variables that point to the actual resource. There is however no usage counter that counts how many functions currently use the resource internally.
Because of this a special bug class exists in the PHP code. Whenever it is possible for usercode to interrupt a PHP function after it has acquired the resource data through the resource identifier, the usercode can destroy the resource and for example allocate a PHP string of the same size that will take the same place in memory as the freed resource. This PHP string can be used to create a special crafted resource that allows exploiting the internals of the PHP functions. When the malicious interruption ends the function will continue and use the replaced resource data.
This bug demonstrates that to achieve the necessary function interruption not only a userspace errorhandler but also a userspace stream handler can be used.
Proof of concept, exploit or instructions to reproduce
The attached exploit uses the substr_compare() information leak vulnerability to determine the offset of the shellcode in memory. It then triggers the vulnerability and destroys the hash resource, replacing its function pointer table with the address of the shellcode.
The exploit will spawn a shell on port 4444 as usual.
This vulnerability is of the same class as MOPB-27-2007, however this time a userspace stream handler is used to interrupt the function and not a userspace error handler.
The only correct fix for this would be to rework the PHP resource system to support usage counters and to use them. Or to use the resource reference counter for that purpose.
Because this is a lot of work we doubt that it will be fixed soon.