Since PHP 5.2.0 the PECL zip extension is bundled and for example activated by default in the popular dotdeb PHP distribution. This extension provides access to zip files and also introduces the zip:// URL wrapper.
A stack based bufferoverflow in the URL parsing of the zip:// wrapper can be easily exploited to execute arbitrary code. Because the zip:// wrapper is not affected by allow_url_include or allow_url_fopen the bufferoverflow can be triggered no matter how PHP is configured. This can allow arbitrary remote code execution.
Affected are PHP 5.2.0 and PHP with PECL ZIP <= 1.8.3
Insufficient boundary checks are still common and often the result of hasty coding. However copying data into a stack variable without any kind of boundary check is quite keen.
The zip:// URL wrapper does exactly this. It first truncates the fragment part of the URL (which contains the name to a file within the .ZIP archive) and then copies everything after the scheme into a stack buffer with the size MAX_PATH_LENGTH (around 4096 bytes on linux and 1024 on BSD).
The copy is performed binary safe, so that it is even possible to copy 0 bytes depending on the source of the URL.
The following gdb debugging session show the environment after the saved instruction pointer is overwritten.
$ gdb ./php-5.2.1 GNU gdb 6.4-debian ... (gdb) run MOPB-16-2007.php ... Program received signal SIGSEGV, Segmentation fault. [Switching to Thread -1214888256 (LWP 7794)] 0x55555555 in ?? () (gdb) i r eip eip 0x55555555 0x55555555 (gdb) x/2x $esp 0xbffb5110: 0x08418e00 0xb792f04c (gdb) x/1s 0xb792f04c 0xb792f04c: "zip://", 'A'
... (gdb) x/5i 0xb792f04c 0xb792f04c: jp 0xb792f0b7 0xb792f04e: jo 0xb792f08a 0xb792f050: das 0xb792f051: das 0xb792f052: inc %ecx
The debugging session shows that an attempted code execution occurs at the supplied offset of 0x55555555. Additionaly the stack layout shows that as esp+4 there is a pointer to the complete zip:// URL and a disassembly shows that zip:// actually turns nicely into x86 code. Therefore all one needs to exploit this successfully is an offset to a POP/RET sequence. As demonstrated here:
$ su - mopb $ ./php-5.2.1 MOPB-16-2007.php ... and now on another shell $ id uid=100(user) gid=100(user) groups=100(user) $ nc 127.0.0.1 4444 id uid=1107(mopb) gid=1107(mopb) groups=1107(mopb)
Proof of concept, exploit or instructions to reproduce
Attached is an exploit that creates an overlong zip:// URL and then triggers it by loading it. In a real world scenario an external attacker would supply the URL to a remote PHP application that tries to open it. Wordpress 2.0 does for example open URLs supplied by an attacker when the pingback(?) functionality is used.
The exploit needs one offset that depends on the main binary and points to a POP/RET sequence. To test the exploit one can put in an offset like 0x55667788 which will always crash.
Furthermore the stack layout might be a little bit differen when PHP is compiled with other compile options. Therefore the lenght of the URL might need adjustment.
In the circles of PHP developers this vulnerability is called a local one, although a lot of applications allow supplying URLs either through URL include vulnerabilities or by design. Examples are avatar upload functions or the Wordpress Pingback code (not in the latest versions).