php代碼注入漏洞PHP中的魔法函數(shù)反序列化漏洞原理(2)_php注入代碼
2022-04-26
序列化和反序列化簡(jiǎn)介
() 將對(duì)象轉(zhuǎn)換為字符串, () 將字符串恢復(fù)為對(duì)象。在PHP應(yīng)用中,序列化和反序列化一般用于緩存,比如緩存等。簡(jiǎn)單來(lái)說(shuō),序列化就是將一個(gè)對(duì)象轉(zhuǎn)換成可以傳輸?shù)淖址?,而反序列化就是用一個(gè)對(duì)象替換原來(lái)的字符。
簡(jiǎn)單示例
php
class test{
public $suifeng="shuai";
}
$a=new test(); //實(shí)例化一個(gè)對(duì)象
$b=serialize($a); //進(jìn)行序列化
echo $b; //輸出序列化后的字符串
echo '
';
echo "我是分割線";
$c=unserialize($b); //把序列化后的字符串反序列化
echo '
';
echo $c->suifeng;
?>
下面我們來(lái)看看反序列化后輸出字符的含義
第一個(gè)輸出是
O:4:"test":1:{s:7:"";s:5:"";}
O->object
4->object的長(zhǎng)度
test->object的名稱
1->object中變量個(gè)數(shù)
s->變量名數(shù)據(jù)類(lèi)型
7->變量名長(zhǎng)度
suifeng->變量名
S->變量值數(shù)據(jù)類(lèi)型
5->變量值長(zhǎng)度
shuai->變量的值
PHP 其他數(shù)據(jù)類(lèi)型
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 常用魔法函數(shù)
() //創(chuàng)建對(duì)象時(shí)調(diào)用
() //在對(duì)象被銷(xiāo)毀之前調(diào)用
() //調(diào)用類(lèi)中不存在的方法時(shí)執(zhí)行
() //調(diào)用類(lèi)中不存在的靜態(tài)方法方法時(shí)執(zhí)行。
() //反序列化后會(huì)立即調(diào)用
() //在對(duì)象序列化之前調(diào)用
() //對(duì)象作為字符串時(shí)調(diào)用
() //用于從不可訪問(wèn)的屬性中讀取數(shù)據(jù)
() //用于將數(shù)據(jù)寫(xiě)入不可訪問(wèn)的屬性
() //調(diào)用函數(shù)的方式,調(diào)用對(duì)象時(shí)的響應(yīng)方式
() // 在不可訪問(wèn)的屬性上調(diào)用 () 或 () 來(lái)觸發(fā)
() // 當(dāng) () 用于不可訪問(wèn)的屬性時(shí)觸發(fā)
,, 序列化對(duì)象的區(qū)別
php v7.x在反序列化時(shí)對(duì)訪問(wèn)類(lèi)型不敏感
變量
直接變量名反序列化
變量
\x00 + * + \x00 + 變量名
您可以使用 S:5:"\00*\00op" 代替 s:5:"?*?op"
變量
\x00 + 類(lèi)名 + \x00 + 變量名
反序列化漏洞的條件
1、函數(shù)的參數(shù)可控
2、后臺(tái)使用PHP中對(duì)應(yīng)的魔術(shù)函數(shù)
反序列化漏洞原理
讓我們先運(yùn)行下面的代碼
';
}
function __destruct(){
echo '調(diào)用了析構(gòu)函數(shù)
';
}
function __wakeup(){
echo '調(diào)用了蘇醒函數(shù)
';
}
}
echo '創(chuàng)建對(duì)象a
';
$a=new ABC;
echo '序列化
';
$a_ser=serialize($a);
echo '反序列化
';
$a_unser=unserialize($a_ser);
echo '對(duì)象快死了!';
?>
PHP語(yǔ)言本身的漏洞
還有一個(gè)反序列化漏洞是由于PHP語(yǔ)言本身的一個(gè)漏洞遇到了某些特征而導(dǎo)致的
示例:導(dǎo)致失敗 (CVE-2016-7124)
php版本
在序列化字符串中,如果代表對(duì)象屬性個(gè)數(shù)的值大于實(shí)際屬性個(gè)數(shù),則跳過(guò)()的執(zhí)行
序列化問(wèn)題
當(dāng) () 被調(diào)用或 .在php.ini中為1,PHP內(nèi)部調(diào)用會(huì)話管理器,將訪問(wèn)用戶序列化后存放在指定目錄下(默認(rèn)為/tmp)。
PHP中共有三種序列化處理器,如下:
處理器
對(duì)應(yīng)的存儲(chǔ)格式
php
鍵名+豎線+()函數(shù)反序列化的值
鍵名長(zhǎng)度+鍵名+()函數(shù)反序列化的值對(duì)應(yīng)的字符
(php>=5.5.4)
()函數(shù)反序列化處理的數(shù)組
配置文件php.ini包含這些與存儲(chǔ)配置相關(guān)的配置項(xiàng):
.="" -- 設(shè)置的存儲(chǔ)路徑,默認(rèn)為/tmp
。 --指定會(huì)話模塊是否在請(qǐng)求開(kāi)始時(shí)啟動(dòng)會(huì)話網(wǎng)站開(kāi)發(fā),默認(rèn)為0不啟動(dòng)
。 --定義用于序列化/反序列化的處理程序的名稱。默認(rèn)使用php
.="" --設(shè)置用戶自定義存儲(chǔ)函數(shù),如果要使用PHP內(nèi)置的存儲(chǔ)機(jī)制php代碼注入漏洞,可以使用這個(gè)函數(shù)(數(shù)據(jù)庫(kù)等),比如存儲(chǔ)為文件默認(rèn)情況下
并且PHP中默認(rèn)使用PHP引擎。如果我們要修改到另一個(gè)引擎,我們需要添加代碼('.', 'The to be set'),例如:
;
:即元數(shù)據(jù)、壓縮文件的屬性等信息,以序列化方式存儲(chǔ)
:壓縮文件的內(nèi)容
:簽名,放在文件末尾
這里有兩個(gè)關(guān)鍵點(diǎn),一個(gè)是文件標(biāo)識(shí)符,必須以();?>結(jié)尾,但是對(duì)前面的內(nèi)容沒(méi)有限制,也就是說(shuō)我們可以很容易的偽造一個(gè)圖片文件或者其他文件來(lái)繞過(guò)一些上傳限制;二是反序列化。存儲(chǔ)在phar中的元數(shù)據(jù)信息是以序列化的方式存儲(chǔ)的。當(dāng)文件操作函數(shù)通過(guò)phar://偽協(xié)議解析phar文件時(shí),數(shù)據(jù)會(huì)被反序列化,這樣的文件操作函數(shù)有很多
先決條件
Phar.=Off 在 php.ini 中設(shè)置
php >=5.3.0
演示測(cè)試
根據(jù)文件結(jié)構(gòu),我們自己構(gòu)建一個(gè)phar文件。 PHP 有一個(gè)內(nèi)置的 Phar 類(lèi)來(lái)處理相關(guān)操作
startBuffering();
$phar->setStub(""); //設(shè)置stub
$o = new TestObject();
$phar->setMetadata($o); //將自定義的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要壓縮的文件
//簽名自動(dòng)計(jì)算
$phar->stopBuffering();
?>
很明顯是以序列化的形式存儲(chǔ)的
如果我們現(xiàn)在通過(guò) phar:// 包裝器對(duì)現(xiàn)有的 Phar 文件執(zhí)行文件操作,它的序列化元數(shù)據(jù)將被反序列化。這意味著我們?cè)谠獢?shù)據(jù)中注入的對(duì)象被加載到應(yīng)用程序的范圍內(nèi)。如果這個(gè)應(yīng)用程序有一個(gè)命名類(lèi)并且定義了魔法方法() 或 has(),這些方法將被自動(dòng)調(diào)用。這意味著我們可以在我們的代碼庫(kù)中觸發(fā)任何析構(gòu)函數(shù)或喚醒方法。更糟糕的是,如果這些方法對(duì)我們注入的數(shù)據(jù)進(jìn)行操作,這可能會(huì)導(dǎo)致更多漏洞。
以下是受影響的功能列表
此時(shí)可以使用phar://協(xié)議
使用條件
可以上傳phar文件
文件操作函數(shù)的參數(shù)可控,不過(guò)濾:,/phar等特殊字符
有一些魔術(shù)方法可用作“跳板”
反序列化字符轉(zhuǎn)義
當(dāng) PHP 反序列化時(shí),底層代碼使用 ;作為字段分隔符,}作為結(jié)尾(字符串除外),根據(jù)長(zhǎng)度判斷內(nèi)容。同時(shí),反序列化過(guò)程必須嚴(yán)格按照序列化過(guò)程進(jìn)行。成功實(shí)現(xiàn)反序列化的規(guī)則。
我們來(lái)分析一段代碼
';
echo $c->suifeng;
?>
發(fā)現(xiàn)序列化為O:4:"test":1:{s:7:"";s:5:"";},反序列化也正常進(jìn)行。當(dāng)我們修改序列化結(jié)果為 O:4:"test":1:{s:7:"";s:5:"";}i:1;s:4:"test";正常解析。
但是如果我們修改它的長(zhǎng)度,就會(huì)報(bào)錯(cuò),比如O:4:"test":1:{s:7:"";s:4:"";}
知道了這個(gè)特性,下面我們來(lái)分析一下代碼
可以看到反序列化為a:2:{i:0;s:5:"";i:1;s:4:"1234";},當(dāng)我們修改參數(shù)為 進(jìn)程首先反序列化$user,然后執(zhí)行函數(shù)中的函數(shù),用它替換test,導(dǎo)致長(zhǎng)度不一致,最終導(dǎo)致反序列化失敗。
a:2:{i:0;s:9:"";i:1;s:4:"1234";}
a:2:{i:0;s:9:"";i:1;s:4:"1234";}
假設(shè)這個(gè)代碼流是創(chuàng)建賬戶的代碼流,此時(shí)$可以被用戶控制,那么我們可以通過(guò)控制可控參數(shù)使反序列化字符轉(zhuǎn)義。它的本質(zhì)其實(shí)和sql注入一樣,雙引號(hào)和大括號(hào)的閉合,但是反序列化字符的轉(zhuǎn)義需要滿足其特性的一些條件。接下來(lái)我們構(gòu)建它。
因?yàn)樗怯?; 嚴(yán)格分隔的作為字段并以 } 結(jié)尾(字符串除外),我們可以這樣關(guān)閉它。
可以看到我們構(gòu)造了$=";i:1;s:6:"";},它被序列化為a:2:{i:0;s:29:"";i: 1; s:6:"";}";i:1;s:4:"1234";},經(jīng)過(guò)這個(gè)序列化后,我們會(huì)反序列化它,紅色部分不會(huì)進(jìn)入反序列化。但是可以看到替換后反序列化還是沒(méi)有成功,我們來(lái)分析一下。
替換后我們得到
a:2:{i:0;s:29:"";i:1;s:6:"";}";i:1;s:4:"1234";},紅色部分is 反序列化時(shí)會(huì)被忽略,要反序列化的字段是
a:2:{i:0;s:29:"";i:1;s:6:"";}
可以看到這里的 s:29:"" 顯然是錯(cuò)誤的,所以我們的反序列化會(huì)失敗,那么我們?nèi)绾伪3终_呢?這是我們?cè)诜葱蛄谢址D(zhuǎn)義特征時(shí)需要考慮的。東西。
在 ('test','',$) 代碼中,test 被替換為比前一個(gè) test 多一個(gè)字符,所以只要我們添加足夠多的 test 來(lái)替換它相同的長(zhǎng)度,這將允許我們的反序列化正常進(jìn)行。
我們構(gòu)建
";i:1;s:6:"";}
這樣php代碼注入漏洞網(wǎng)站開(kāi)發(fā),我們修改了零錢(qián)業(yè)務(wù)的密碼