ID:               28932
 User updated by:  php at ter dot dk
-Summary:          A site's session data is available for all virtual
                   hosts on same server
 Reported By:      php at ter dot dk
 Status:           Open
 Bug Type:         Directory function related
 Operating System: Linux
 PHP Version:      4.3.7
 New Comment:

(re-changed summary)


Previous Comments:
------------------------------------------------------------------------

[2004-06-28 05:36:39] php at ter dot dk

As a sidenote related to session security this bug could in a default
setup with multiple virtual hosts in safe mode (as in a typical
webhosting-setup) be exploited pretty bad by a single customer,
retrieving ALL current sessionids:

1. Create a folder which is world-writable
2. Create a PHP-script that writes to a new .php-file in that folder
(making that file having the same user as the Apache user), using
something like glob(ini_get("session.save_path")."/sess_*")
3. Access that PHP-script via a browser.

Since the script is owned by the same UID as the Apache-user and the
session-files, and glob() checks the UID for the first file (where it
instead only should check the UID for the directory), a full list of
*all* session files is available - even sessions for sites under other
virtual hosts.

Combined with the possible exploit mentioned in bug #28242 (online test
at http://stock.ter.dk/session.php - the bug was dismissed as "not our
problem; every single administrator in the world would just have to
create a custom save_path for each and every virtual host"), the user
could read and write sesssion data to every single session on the
server.

The Filesystem and Security chapter still doesn't mention anything
about the problem, and even the Safe Mode chapter states: " The PHP
safe mode is an attempt to solve the shared-server security problem. It
is architecturally incorrect to try to solve this problem at the PHP
level, but since the alternatives at the web server and OS levels
aren't very realistic, many people, especially ISP's, use safe mode for
now." 

I agree that it would be nice if every single administrator would have
separate session.save_path for each virtual host or even jailed every
user, but as mentioned above, it isn't that realistic.

I really hope that one would consider reworking the session storage
process as mentioned in bug #28242, and somewhat creating a harder job
to find or accessing session files

Some approaches to solve parts of the problem could be adding the UID
to the session file in safe mode (as HTTP authentication currently is
doing), the servername, a hash of both, and/or by other means not
having users to be able to access the session files directly (which
even could mean not allowing scripts - still in safe mode - with the
same UID as the Apache user, although some applications might depend on
this functionality with serverside-generated php-code) or otherwise
disallow file related functions from accessing sess_*-files at all -
once again, still only in safe mode.


.. and even if my session-related concern is disregarded, I still hope
the glob()-bug is fixed :)

- Peter Brodersen

------------------------------------------------------------------------

[2004-06-26 07:36:33] php at ter dot dk

Description:
------------
The SAFE_MODE handling of glob() needs a checkup for security reasons.

In short - always with SAFE_MODE on:

1) glob() can still fetch all filenames in a directory not owned by the
same UID as the user, if just the first file in the directory (or more
specific, the glob-pattern) happens to be owned by the same user as the
PHP-script.

2a) No warning is raised if glob is used on another owner's directory
and there is no match.

2b) In those cases where SAFE_MODE correctly prohibits glob() from
fetching a list of files, the warning still discloses the first
filename.

Solution: glob() in SAFE_MODE should be restricted in the same way as
opendir() is.


Longer version + rationale:

1) 
I assume the premise for glob() should be the same as opendir(). But
glob() isn't handled in the same way.

It appears that glob() only checks *the first file* (assuming the
directory isn't owned by the same user) in the glob()-result for
ordinary SAFE_MODE UID-comparsion.

If we have three files in a directory (owned by xyzzy, UID 999):

a.txt (owned by user foo, UID 1000)
b.txt (owned by user bar, UID 1001)
c.txt (owned by user baz, UID 1002)

.. a PHP-script owned by foo executing glob("/path/to/directory/*")
will return all file names, regardless of the ownership (of course, the
Apache User would have to have read/execute-access to the directory)

The exact same PHP-script owned by bar will give an error, just because
the first file in the directory doesn't happen to be owned by bar.


2a)
glob() is allowed to check whether no files match a specific pattern.

2b)
Furthermore, the glob() SAFE_MODE warning (when present) disclose the
first file name (where the UID check is performed):

opendir("/path/to/directory/") raises a warning like "The script whose
uid is 1001 is not allowed to access /path/to/directory/ owned by uid
999". This is fine.

glob("/path/to/directory/*") raises a warning like "The script whose
uid is 1001 is not allowed to access /path/to/directory/a.txt owned by
uid 1000". Notice that "a.txt" is mentioned in the error - a file name
we might not have known in advance.


Combining the problems mentioned in 2a and 2b gives us a possibility to
get an almost complete list of files in a directory by walking through
the letters, as in this example:

("Does a* exists? No. Does b* exist? No. Does c* exist? Yes, cyber.txt
exists. Does cz* exist? No. Does d* exist? Yes, door.php exists. Does
dp* exist? No. Does dq* exist? No.")

Of course, instead of "Yes, cyber.txt exists", one would recieve "..
not allowed to access cyber.txt", still disclosing the name.


Reproduce code:
---------------
Preparation:

1. touch /tmp/a as the same user as the owner of the following PHP-code
(create the file out of PHP - yes, this might require shell access in
this setup, but this is just a controlled preparation to highlight the
problem)

2. make sure PHP is running in safe_mode

3. Test code:

// Example 1
$a = glob("/tmp/*");

// Example 2a
$b = glob("/tmp/doesnotexist*");

// Example 2b
$c = glob("/tmp/sess_*");


Expected result:
----------------
In all cases:

Warning: glob(): SAFE MODE Restriction in effect. The script whose uid
is 1000 is not allowed to access /tmp/ owned by uid 0


Actual result:
--------------
For $a:
glob() works unrestricted and a list of all file names is retrieved.

For $b:
glob() works unrestricted and returns false (giving us the knowledge
that no files begin with "doesnotexist").

For $c:
"Warning: glob(): SAFE MODE Restriction in effect. The script whose uid
is 1000 is not allowed to access
/tmp/sess_14758f1afd44c09b7992073ccf00b43d owned by uid 33" (disclosing
the session file name [33 is the Apache User])




------------------------------------------------------------------------


-- 
Edit this bug report at http://bugs.php.net/?id=28932&edit=1

Reply via email to