#37273 [Opn]: Symlinks and session handler allow open_basedir bypass

2007-08-20 Thread vrana
 ID:   37273
 Updated by:   [EMAIL PROTECTED]
 Reported By:  c dot i dot morris at durham dot ac dot uk
 Status:   Open
-Bug Type: Documentation problem
+Bug Type: Session related
 Operating System: Linux
 PHP Version:  5.1.3
 New Comment:

Security vulnerability should be better fixed in source than
documented.


Previous Comments:


[2006-07-27 11:41:42] a dot d dot stribblehill at durham dot ac dot uk

This is *not* a documentation bug: as the original report says, it is a
security vulnerability -- one that can and should be fixed in the code.



[2006-07-27 01:34:11] [EMAIL PROTECTED]

Reclassified. Ilia will give more info for whomever is going to
document this.



[2006-06-16 14:32:37] c dot i dot morris at durham dot ac dot uk

For a possible solution to this, in ext/session/mod_files.c, the
ps_files_open function has:
data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY, 
data->filemode);

On systems that support O_NOFOLLOW (FreeBSD, Linux>=2.2, maybe others)
you can probably do
data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY |
O_NOFOLLOW, 
data->filemode);
which will cause this open to fail (with error ELOOP) if the session
file is a symlink rather than a regular file.

On systems that don't support O_NOFOLLOW, stat()ing the file and making
sure the file mode isn't S_IFLNK should do it.

Would you like me to try to put together a patch for this?



[2006-05-03 16:19:05] c dot i dot morris at durham dot ac dot uk

As above - I managed to lose the bug password and it took a while to
come through to my email.



[2006-05-03 13:30:53] cim at compsoc dot dur dot ac dot uk

Ah, there appears to be some confusion over what I mean. I don't mean
ini_set() the session directory to a symlink, I mean set the session
directory to a real directory (which, yes, must be within open_basedir
confines) that contains a symlink outside open_basedir.
(So, for example, open_basedir = /users/www1/, create a symlink from
/users/www1/bob/sess_abc to /users/www2/fred/target, ini_set() the
session storage directory to /users/www1/bob/, and then create a session
with ID 'abc' using ?PHPSESSID=abc)

Does that make more sense?



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
http://bugs.php.net/37273

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


#37273 [Opn]: Symlinks and session handler allow open_basedir bypass

2006-06-16 Thread c dot i dot morris at durham dot ac dot uk
 ID:   37273
 User updated by:  c dot i dot morris at durham dot ac dot uk
 Reported By:  c dot i dot morris at durham dot ac dot uk
 Status:   Open
 Bug Type: Session related
 Operating System: Linux
 PHP Version:  5.1.3
 New Comment:

For a possible solution to this, in ext/session/mod_files.c, the
ps_files_open function has:
data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY, 
data->filemode);

On systems that support O_NOFOLLOW (FreeBSD, Linux>=2.2, maybe others)
you can probably do
data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY |
O_NOFOLLOW, 
data->filemode);
which will cause this open to fail (with error ELOOP) if the session
file is a symlink rather than a regular file.

On systems that don't support O_NOFOLLOW, stat()ing the file and making
sure the file mode isn't S_IFLNK should do it.

Would you like me to try to put together a patch for this?


Previous Comments:


[2006-05-03 16:19:05] c dot i dot morris at durham dot ac dot uk

As above - I managed to lose the bug password and it took a while to
come through to my email.



[2006-05-03 13:30:53] cim at compsoc dot dur dot ac dot uk

Ah, there appears to be some confusion over what I mean. I don't mean
ini_set() the session directory to a symlink, I mean set the session
directory to a real directory (which, yes, must be within open_basedir
confines) that contains a symlink outside open_basedir.
(So, for example, open_basedir = /users/www1/, create a symlink from
/users/www1/bob/sess_abc to /users/www2/fred/target, ini_set() the
session storage directory to /users/www1/bob/, and then create a
session with ID 'abc' using ?PHPSESSID=abc)

Does that make more sense?



[2006-05-03 13:18:04] [EMAIL PROTECTED]

The change of the INI setting for save_path is already being 
validated against both safe_mode and open_basedir. If you try 
to set them to a symlink pointing to an external file you will 
get an error message like this:
Warning: ini_set(): open_basedir restriction in effect. File
(...) is not within the allowed path(s): (...)



[2006-05-02 09:40:47] c dot i dot morris at durham dot ac dot uk

Description:

[Filed in bug tracking system as stated in message to [EMAIL PROTECTED]
on 25 April following no response to that message or the similar
message on 31 March]

Creation of symlinks in a user-specified directory allows creation and
editing of any web-server writable file bypassing open_basedir and
other safe-mode checks.

PHP session creation allows any alphanumeric session id to be specified
by the client by setting $_GET['PHPSESSID']. PHP then creates (or
overwrites if it exists) a file called "sess_".session_id() in the
directory specified in the session.save_path configuration option.
When doing this, it does not check whether the session file is a real
file or a symlink.



Reproduce code:
---
1) Create a symlink named "sess_foo" in a directory owned by the script
owner and within the open_basedir confines, to the file that will be
overwritten with junk session data.
The file to be overwritten must be webserver-writable but can be
outside the open_basedir confines. Alternatively you can use a broken
symlink to a web-server writable directory.
2) Make the following script:

3) Call this script with ?PHPSESSID=foo 

Obviously for this to work, sessions must be enabled, setting session
save paths must be allowed, the filesystem must support symbolic links
(and the exploiting user must be able to create them, which will
generally require shell access), and PHP must be running as an Apache
module rather than as suexeced CGI.

Expected result:

Some sort of error about invalid session data (or possibly just a
silent refusal to use that session and the creation of a new session).
PHP should check that the session file "sess_".session_id() either does
not exist, or exists and is a real file rather than a symlink, before
attempting to read from or write to it.

Actual result:
--
The file that is the target of the symlink will then be overwritten by
the session data (assuming it is webserver-writable). This allows
overwriting of any uploaded file, including those uploaded by other
users.
(If the symlink does not point to a real file, then the file will be
created)

Since the session data may be a valid file for certain formats (PHP
scripts, for example), this has potential uses for cross-site scripting
due to the bypassing of open_basedir.
For example, storing "" as session data to a file
exploit.php in a