php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #37273 Symlinks and session handler allow open_basedir bypass
Submitted: 2006-05-02 09:40 UTC Modified: 2007-08-23 02:04 UTC
Votes:2
Avg. Score:4.0 ± 1.0
Reproduced:0 of 0 (0.0%)
From: c dot i dot morris at durham dot ac dot uk Assigned: iliaa (profile)
Status: Closed Package: Session related
PHP Version: 5.1.3 OS: Linux
Private report: No CVE-ID: None
 [2006-05-02 09:40 UTC] c dot i dot morris at durham dot ac dot uk
Description:
------------
[Filed in bug tracking system as stated in message to security@php.net 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:
<?php                                                          
ini_set("session.save_path","/path/where/sess_foo/symlink/is/");
session_start();                                            
$_SESSION['bar'] = "bar";                         
session_write_close();
?>                                                        
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 "<?php print("foo"); ?>" as session data to a file exploit.php in another user's upload directory will cause that PHP code to be executed if it can be read via HTTP. This could be used for cookie stealing, etc. (Obviously some garbage due to the session storage format will also be printed, but this may not be a major problem)



Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2006-05-03 13:18 UTC] iliaa@php.net
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-03 13:30 UTC] 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 16:19 UTC] 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-06-16 14:32 UTC] 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-07-27 01:34 UTC] sniper@php.net
Reclassified. Ilia will give more info for whomever is going to document this.
 [2006-07-27 11:41 UTC] 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.
 [2007-08-20 10:19 UTC] vrana@php.net
Security vulnerability should be better fixed in source than documented.
 [2007-08-20 10:37 UTC] jani@php.net
Ilia, is this fixed or not? :)
 [2007-08-23 02:04 UTC] iliaa@php.net
This bug has been fixed in CVS.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
 
Thank you for the report, and for helping us make PHP better.


 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Mar 19 05:01:29 2024 UTC