0x00 前言
這幾天做了兩道有關php反序列化字符逃逸的題目,故來自己本地實驗總結一下,以后可以時不時拿起來看.
這種題目有個共同點:
- php序列化后的字符串經過了替換或者修改,導致字符串長度發生變化.
- 總是先進行序列化,再進行替換修改操作.
經典題目:
- [0CTF 2016]piapiapia (替換變長)
- [安洵杯 2019]easy_serialize_php (替換變短)
0x01 漏洞淺析
第一種情況:替換修改之后導致序列化字符串長度變長
- 先看一段php序列化的代碼(本代碼基於7.0.9版本的php,經過測試較低版本的php會使"被轉義導致實驗失敗:
- 代碼功能很簡單,輸入name,並和sign一同傳入到user數組中,user數組序列化后的字符串經過test函數檢測之后,輸出反序列化之后的結果.
- 關於php反序列化引擎在進行反序列化的時候是以字符長度來進行判斷的,關於這一點,前言中的參考文章已經解釋的很詳細了,這里不再贅述.
- 下面進行漏洞測試,通過修改name的值從而改變sign的值.
- 關鍵點在於test函數,這個函數檢測並替換了非法字符串,看似增加了代碼的安全系數,實則讓整段代碼更加危險.test函數中檢測序列化后的字符串,如果檢測到了非法字符'x',就把它替換為'ha'.
- 如果輸入name=evALx
- 可以看到被替換成了'evALha',這個字符串長度為6,從而導致反序列化失敗,無法輸出結果.利用這個漏洞,就可以對sign的值進行修改.輸入name=evALxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";i:1;s:14:"hello hackerrr";}
- 可以就看到sign的值被成功修改.
- 原因分析
- test函數把'x'替換成'ha',換句話來說,就是把一個字符替換成了兩個字符,造成了序列化字符串的"膨脹".
- 首先來考慮要注入的數據,在這個例子中把sign替換為"hello hackerrr",這個字符串在本實驗的序列化結果是i:1;s:14:"hello hackerrr,由於要閉合name的雙引號以及結束的花括號,所以payload應該是";i:1;s:14:"hello hackerrr";}
- 下面來考慮長度溢出,payload的字符串的長度是29,所以要在name中輸入29個x,再加上evAL,整體長度就是62,在經過test函數替換之后,x被替換成了ha,如下圖所示:
- 溢出的部分成功逃逸,經過雙引號閉合name,以及閉合結束時的花括號,導致sign被成功修改.
第二種情況:替換之后導致序列化字符串長度變短
-
本地實驗代碼如下:
-
輸入name和sign,number值是固定的'2020',經過[序列化->敏感字替換為空(長度變短)->反序列化]的過程之后再輸出結果.
-
通過輸入name和sign修改number:
payload:name=testtesttesttesttesttest&sign=hello";s:4:"sign";s:4:"eval";s:6:"number";s:4:"2000";}
-
原因分析:
在str_rep函數中如果檢測到'php','test'關鍵字就把其替換為空,那么就利用這一點,我們故意輸入敏感字符,替換為空之后來實現字符逃逸.(注意考慮閉合問題)