From: php at ter dot dk
Operating system: Linux (Debian)
PHP version: 4.3.7
PHP Bug Type: Directory function related
Bug description: glob() in safe mode only checks ownership of first file in directory
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 bug report at http://bugs.php.net/?id=28932&edit=1
--
Try a CVS snapshot (php4): http://bugs.php.net/fix.php?id=28932&r=trysnapshot4
Try a CVS snapshot (php5): http://bugs.php.net/fix.php?id=28932&r=trysnapshot5
Fixed in CVS: http://bugs.php.net/fix.php?id=28932&r=fixedcvs
Fixed in release: http://bugs.php.net/fix.php?id=28932&r=alreadyfixed
Need backtrace: http://bugs.php.net/fix.php?id=28932&r=needtrace
Need Reproduce Script: http://bugs.php.net/fix.php?id=28932&r=needscript
Try newer version: http://bugs.php.net/fix.php?id=28932&r=oldversion
Not developer issue: http://bugs.php.net/fix.php?id=28932&r=support
Expected behavior: http://bugs.php.net/fix.php?id=28932&r=notwrong
Not enough info: http://bugs.php.net/fix.php?id=28932&r=notenoughinfo
Submitted twice: http://bugs.php.net/fix.php?id=28932&r=submittedtwice
register_globals: http://bugs.php.net/fix.php?id=28932&r=globals
PHP 3 support discontinued: http://bugs.php.net/fix.php?id=28932&r=php3
Daylight Savings: http://bugs.php.net/fix.php?id=28932&r=dst
IIS Stability: http://bugs.php.net/fix.php?id=28932&r=isapi
Install GNU Sed: http://bugs.php.net/fix.php?id=28932&r=gnused
Floating point limitations: http://bugs.php.net/fix.php?id=28932&r=float