PHP 反序列化漏洞學習及CVE-2016-7124漏洞利用

前言:

什麼是php序列化和反序列化?
簡單的理解:序列化就是使用serialize()將對象用字符串的方式進行表示並賦值給變量,反序列化是使用unserialize()將序列化的後字符串(這裏的的字符串就是對象序列化之後的產物)構成相應的對象,反序列化是序列化的逆過程。

  • 序列化serialize()操作:
    serialize() 返回字符串,此字符串包含了表示 value 的字節流,可以存儲於任何地方。
    簡單來講,就是將對象轉化爲可以傳輸的字符串,字符串中存儲着對象的變量、類型等。
    舉個例子:
    test.php
<?php 
class people{
  public $name = "hello";
  public $age = "20";
}

$fairy = new people();  //實例化people對象
echo serialize($fairy); // 通過echo的方式輸出people序列化後的字符串
 ?>

最終瀏覽器輸出:

在這裏插入圖片描述
這裏來解釋下是什麼意思:首先O表示的是一個對象,如果傳給serialize()的值是一個數組,那麼這裏的O就會是A,後面的6表示當前對象名是6個字符 people表示對象名,後面的2表示當前對象中存在2個屬性值, {}裏面第一個s表示的屬性名類型是String類型,第二個4表示屬性名name的字符是4個,以此類推第二個s表示name的值是String類型,後面的5表示name值hello的字符是5個,後面的s:3:“age”;s:2:“20”;的意思也是和上面一樣。

  • 反序列化unserialize ()操作:
    將序列化後的字符串轉化爲PHP的值。
    舉個例子:
    test.php
<?php 
class people{
  public $name = "hello";
  public $age = "20";
}

$fairy = new people();
$s_fairy = serialize($fairy);   //序列化爲字符串
$uns_fairy = unserialize($s_fairy);  //將字符串$s_fairy進行反序列化爲對象
var_dump($uns_fairy);      # 打印對象

最終瀏覽器輸出:在這裏插入圖片描述

魔術方法:

PHP 將所有以 __(兩個下劃線)開頭的類方法保留爲魔術方法。所以在定義類方法時,除了上述魔術方法,建議不要以 __ 爲前綴。
__construct()__destruct()__call()__callStatic()__get()__set()__isset()__unset()__sleep()__wakeup()__toString()__invoke()__set_state(),__clone()__debugInfo() 等方法在 PHP 中被稱爲"魔術方法"(Magic methods)。
這裏舉例幾個常用的魔術方法:

  • __construct()
    PHP 5 允行開發者在一個類中定義一個方法作爲構造函數。具有構造函數的類會在每次創建新對象時先調用此方法,所以非常適合在使用對象之前做一些初始化工作。

  • __destruct()
    析構函數會在到某個對象的所有引用都被刪除或者當對象被顯式銷燬時執行。

  • __sleep()
    serialize() 函數會檢查類中是否存在一個魔術方法 __sleep()。如果存在,該方法會先被調用,然後才執行序列化操作。

  • __wakeup()
    unserialize() 會檢查是否存在一個 __wakeup() 方法。如果存在,則會先調用 __wakeup 方法,預先準備對象需要的資源。

  • __toString()
    __toString() 方法用於一個類被當成字符串時應怎樣迴應。例如 echo $obj; 應該顯示些什麼。
    以上幾個常用的魔術方法使用順序和使用方法還請一定要牢記主!!!

反序列化漏洞:

__wakeup()的利用場景:
test.php

<?php 
class Test{
    var $num= "123";
    function __wakeup(){
        $fp = fopen("test.php", 'w');
        fwrite($fp, $this -> num);
        fclose($fp);
    }
}

$test1 = $_GET['test'];
print_r($test1);
echo "<br />";
$seri = unserialize($test1);

require "test.php";

?>

以上代碼的意思就是,首先定義一個Test類,類成員有$num=123,並且存在__wakeup()魔術方法,該方法的功能是打開一個test.php文件,並且將Test類中的變量$num寫入到test.php中,然後通過GET的方式傳入參數test賦值給$test1,最終將$test1傳入unserialize()函數來進行反序列化操作。那麼這裏如果沒有對傳入unserialize()函數的值$test1進行過濾的話,就會導致反序列化漏洞。

exp如下:

O:4:"Test":1:{s:3:"num";s:18:"<?php%20phpinfo();?>";}

在這裏插入圖片描述此時就會生成test.php文件,並且內容爲<?php phpinfo();?>
當然了這裏只是演示了當存在__wakeup()魔術方法的利用方式,除了該魔術方法的利用,php反序列化中其他魔術方法的利用也是非常多的,這裏就不一一舉例了。

__wakeup()魔術方法繞過(CVE-2016-7124)

  • 漏洞影響版本:
    PHP5 < 5.6.25
    PHP7 < 7.0.10

  • 漏洞產生原因:
    如果存在__wakeup方法,調用 unserilize() 方法前則先調用__wakeup方法,但是序列化字符串中表示對象屬性個數的值大於 真實的屬性個數時會跳過__wakeup的執行

  • 漏洞復現
    test.php

<?php
	class car{  //定義car類
	  	public $name ="benchi";  //$name屬性
 	  	function __wakeup(){    
	  		echo "this is __wakeup"."<br/>";
	  	}
	  	function __destruct(){
	  		echo "this is __destruct"."<br/>";
	  	}
	  }
	  $str=$_GET['s'];   //GET方式接收參數賦值到$str
	  $un_str=unserialize($str);    //將字符串進行反序列化,那麼我們傳入的字符串$str就要是序列化之後的值
	  echo $un_str->name."<br/>";
?>

腳本通過GET方式傳入參數s賦值給$str,對其反序列化後輸出name屬性的值
爲了方便觀察,我將傳入的s參數的name屬性值更改爲xss代碼
頁面顯示語句代表反序列化之前先調用了__wakeup 方法
在這裏插入圖片描述點擊確定後,頁面完成後自動執行__destruct方法
在這裏插入圖片描述將傳入的序列化數據的對象變量個數由1更改爲2,頁面只執行了__destruct方法,而且輸出name屬性時報錯,是由於反序列化數據時失敗無法創建對象。在這裏插入圖片描述所以序列化字符串中表示對象屬性個數的值大於 真實的屬性個數時就會跳過__wakeup的執行而直接執行__destruct方法。

總結:

通過上面的實驗可以看出,不管是反序列化漏洞還是CVE-2016-7124漏洞的利用,最根本的還是通過unserialize()函數來進行反序列化時,沒有對傳入的參數進行過濾所導致的。只要記住一點,所以由用戶輸入的值都是不可靠的,經過合理的過濾和處理,也就可以很好的預防此類漏洞的產生。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章