Edit report at https://bugs.php.net/bug.php?id=69090&edit=1
ID: 69090
Comment by: php-dev at coydogsoftware dot net
Reported by: simon at ikanobori dot jp
Summary: add prefix/xor to cache keys/check permissions or
separate caches
Status: Analyzed
Type: Feature/Change Request
Package: opcache
Operating System: linux/debian
PHP Version: 5.6.5
Block user comment: N
Private report: N
New Comment:
dol+list, thank you for your comments. Your observations are consistent with my own experiences,
with the exception that I haven't had a chance to test exploitation under CloudLinux/CageFS. I
haven't bothered testing with cPanel's "Jail Apache" virtfs feature either, due
to unrelated reliability problems with that feature. I'm trying to avoid too much discussion of
such platform-specific mitigations because they're out of scope for a PHP bug report, and they
shouldn't even be necessary. I feel strongly that this is a PHP bug which needs to be fixed in
PHP.
I agree that device+inode should ideally be added to the key scheme, but they alone are
insufficient. They are not necessarily private information, and can be read if permissions of the
parent directory allow. This breaks user expectations; if wp-config.php is unreadable for a user,
that user should not be able to execute that script, period. This is why I think EUID should be part
of the key. I don't like the thought of opcache playing permissions referee and I think it
suggests a fundamental design flaw with the way SHM is being used, but working within the existing
SHM design I think EUID is the best way forward. Perhaps a more radical change would be better, but
I'm not familiar enough with prior art in PHP opcode caching to know what it is.
Using device+inode is also less straightforward than using EUID, otherwise I'm sure the
maintainers would have already done it: it seems inappropriate to stat() for this info during key
generation for performance reasons. APC apparently got a stat struct passed from the SAPI so the
stat() was already done outside of APC code (does this imply APC cached scripts per compilation unit
and not per file, since the web server wouldn't know which files are included? I haven't
reviewed APC code in enough detail yet to confirm).
For my patch I use EUID simply because there's no undue performance hit and it fixes the
cross-user permissions bypass in both "out of the box" and common control panel
environments. It's probably not perfect but surely we can agree it's better than the
existing code.
For anyone using FPM who wants to mitigate before PHP fixes the vulnerability, 2 years ago Mattias
Geniar confirmed that separate FPM master daemons is the way to go, and provided example configs.
It's cumbersome and inefficient and shouldn't be necessary: https://ma.ttias.be/a-better-way-to-run-php-fpm/
Previous Comments:
------------------------------------------------------------------------
[2016-11-07 11:46:20] dol+list at cyon dot ch
First off all thank you php-dev to bringing this issue back on the table.
I'd like to bring in my view from a shared hosting provider.
IMHO the two big hosting panel softwares Plesk and cPanel are vulnerable to this issue. We currently
use cPanel. cPanel provides a different set of options to isolate the user from reading other users
files. In addition to the user isolation it's also possible to isolate you processes any
further by running them in a chroot environment. This could be easy leveraged by using tools/distros
like Cloudlinux CageFS (https://www.cloudlinux.com/cagefs), which we use.
All this countermeasures to separate processes users on process and file system level are voided if
the OpCache is accessible without the user boundaries.
Our stack currently is not vulnerable to the mentioned issue due to the combination of additional
tools and server softwares that isolate users also on the OpCache level. The magic is a combination
of Cloudlinix as file system isolation and Litespeed as a PHP SAPI process and OpCache isolation.
Having some insights into the hosting industry this stack is not the majority. IMHO most of the
cPanel users use the built in capabilities of the EasyApache stack, which is vulnerable.
In the past we've seen some attacks targeting multiple users on the same server. The most
effective was: Symlink Bypass http://www.rafayhackingarticles.net/2012/01/hack-website-on-shared-host-symlink.html
A skilled attacker could easily take over dozens of shared hosting with the help of this
vulnerability. Depending on the shared hosting provider I guess an average of users per host is
approx. 200-1000 users. Given the current market share of Wordpress (easy extractable secrets) of
approx. 60% ( https://w3techs.com/technologies/details/cm-wordpress/all/all
) the impact of a hacked website/CMS/Plugin is very high. In automatic manner an attacker could take
over half of shared hostings only by optimizing for Wordpress.
This is IMHO a pressing matter for all the other shared hosting providers and customers support
staffs out there.
I'm in favor to the current solution to fix the issue by appending a contextual part to the
path.
IMHO the best solution from a security point of view would be the inode information.
From a operations point of view inodes also solve the issue of deploying with symlinks. E.g. https://www.scalingphpbook.com/blog/2014/01/30/zend-opcache-and-atomic-deploys.html
------------------------------------------------------------------------
[2016-11-07 09:39:26] sjon at hortensius dot net
jpauli, using separate pools does not fix this issue; the cache is maintained by the master process
that has access to all pools regardless of permissions of those pools.
------------------------------------------------------------------------
[2016-11-06 20:54:49] php-dev at coydogsoftware dot net
bjh438-git, thank you for your comments. I had previously read that article. The setup described
mitigates the security concerns I've outlined for one reason: It recommends disabling OPCache
completely.
Such a multi-user setup is generally recommended, but as I mentioned in my response to jpauli,
it's dangerous when combined with a single SHM cache with a simplistic hash keying scheme which
makes no attempt to segregate users.
My readings of php-internals archives suggest that OPCache was indeed meant to be usable in
multi-user setups so perhaps the maintainers simply haven't thought through the all
implications of the key scheme, though they have discussed its other shortcomimgs. I'm working
on a brief but more comprehensive advisory which I'll post here and on php-internals soon if we
don't get more meaningful engagement on this issue.
------------------------------------------------------------------------
[2016-11-06 17:27:01] bjh438-git at yahoo dot com
php-dev - would it not be best (at least when using fpm) to run each pool under a separate user /
group account as discussed here: https://www.digitalocean.com/community/tutorials/how-to-host-multiple-websites-securely-with-nginx-and-php-fpm-on-ubuntu-14-04
This would seem to satisfy the original suggestions earlier in the thread to leave as much as
possible to the OS level security controls.
I mention because I was wondering if your patches (which are warmly welcomed...I've been
posting on this issue for a while now on SF & SE) presume that these types of controls would
also be implemented or perhaps obviate them.
------------------------------------------------------------------------
[2016-11-04 20:11:41] php-dev at coydogsoftware dot net
I'll address jpauli's points:
Not everyone is using FPM. My patch fixes this problem on both FPM and apache2handler, where we can
separate users with mod_ruid2.
My experience with FPM is limited so I may be speaking out of turn, but when I tried separate FPM
pools with separate users, they were still forked from the same parent FPM master process. Correct
me if I'm wrong but the OPCache SHM segment is opened in this master process and inherited by
the pools as a file descriptor. The multi-user pools still share a single OPCache, and thus they
actually aid in bypassing file permissions, rather than fixing the problem.
I agree 100% with your points about relying on the lower stack to isolate vhosts and enforce
permissions, but the entire point of this bug report is that OPCache breaks this isolation in
real-world configurations since it uses a shared cache passed from Apache parent to children, or
from FPM master to pools.
You stated "Obviously, the OPCache SHM wouldn't be shared here" in a multi-pool
configuration. What would such a configuration look like? My testing shows the opposite to be true.
Perhaps you meant to suggest multiple FPM master daemons instead of multiple pools? If the OPCache
SHM can be initialized at the pool level instead of in the master process (I don't think this
is how it works today; I'd love to be proven wrong on this), this is great for FPM users but
does nothing for users of other SAPI's.
The big caveat in my testing is that most of it was done under cPanel. Both mod_ruid2/mod_php and
FPM have the shared OPCache problem under both EA3 and EA4. This is true for both PHP5 and PHP7. If
you think this is a problem with cPanel's apache2handler and FPM configurations then I can take
the issue up with cPanel, but I'd love to see how they could fix this for all SAPI's where
OPCache is useful. Clearly the problem isn't limited to cPanel though, based on the other users
commenting on this bug.
Hopefully this will clarify users' concerns with this bug; from your response I'm frankly
not sure you fully understand the problem we're reporting. I agree with "jeff at mcneill
dot io" that to isolate vhosts with PHP as it stands today, you'd need entirely separate
Apache parents (for apache2handler) or FPM master processes (not just multiple pools).
------------------------------------------------------------------------
The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at
https://bugs.php.net/bug.php?id=69090
--
Edit this bug report at https://bugs.php.net/bug.php?id=69090&edit=1