PHP反序列化中過濾函式使用不當導致的物件注入
1.漏洞產生的原因
#### 正常的反序列化語句是這樣的
$a='a:2:{s:8:"username";s:7:"dimpl3s";s:8:"password";s:6:"abcdef";}';
但是如果寫成這樣
$b='a:2:{s:8:"username";s:7:"dimpl3s";s:8:"password";s:6:"123456";}s:8:"password";s:6:"abcde";}';
也可以正常的編譯, 而且下面一條語句的結果是 password=“123456” 而不是abcde
結果
這就說明一個問題,在反序列化的時候,只要求第一個序列化字串合法就行,換我個理解,就是反序列話時,他會從前往後讀取,當讀取第一個合法的序列化的字串時,就會反序列化。
### 當過濾使用者輸入引數的時候,如果先序列化再對序列化過後的字串進行過濾,而且在過濾的過程中會導致原本的長度改變,就可能造成序列化物件注入漏洞。
此處參考別人的程式碼:
可以看到,這裡過濾函式將原來的x換成了zz,但是長度卻超過了原來的長度 ,但是原來長度的數字時沒變的,這就導致報錯。但是試想一下,如果這裡的密碼是可控的,然後我們輸入字元
的時候帶入雙引號和} 會怎麼樣呢? 看如下程式碼
結果
第一排是我們構造的東西序列化過後的值,
第二排是序列化過後的值進行過濾過後的值,可以看到,此時由於x換成了z,而前面讀40的時候正好會讀到最後一個x,從而使我們輸入的新物件得以注入,而且得到正常的反序列化。
第三排是反序列化過後的到的值,此時原本的aaaaaa的值已經被我們覆蓋。
二:例項分析
根據上面的原因可知,產生漏洞最直接的原因是因為序列化過後的字串被過濾的時長度發生變化, 根據這個這個原因,我們就可以把漏洞分為 長度變長,和長度變短兩種情況,注意! 如果長度不變的話,不會引起漏洞產生。
(1) 長度變短。
題目: 安洵杯2019 easy_serialize_php // 在https://buuoj.cn/ 這個靶場裡又復現
原始碼:
根據提示在phpinfo拿到
很顯然答案在 d0g3_f1ag.php裡面,關鍵是我們怎麼去讀取他的原始碼 ,可以看到最後一排的會獲取 ['img'] 中的 的原始碼,我們僅需要覆蓋img的值將他變成d0g3_f1ag.php就行。
在看這個過濾函式
他會使得輸入的相應字元變為空,也就是讓序列化後的字串變短,我們就可以利用此來吞掉原本的變數名,而注入我們想注入的程式碼。
第一種解法:值逃逸
d0g3_f1ag.php的base64 編碼 ZDBnM19mMWFnLnBocA== 長度20
在本地測試的時候得到正常的 序列化字元是這樣的
a:3:{s:4:"user";s:5:"guest";s:8:"function";s:3:"123";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
前者user,和function 的值都是我們可控的
我們想要構造的是 s:3:"img";s:20:"ZDBnM19mMWFnLnBocA=="; 設想一下 ,如果我們把它設定function的值,並且在前面user的值利用過濾函式將後面的 "s:8:"function";s:xx:" 吞掉,那麼function的值,也就是我們想要注入的物件,不就正好上位了嗎? 但是注意閉合前面的由於吞掉而缺少的分號和雙引號,而且,這裡兩個雙引號緊挨著會報錯,所以我們加一個字元,再把這個字元一起吞掉就行,還有 這裡前面是 a:3: 所以我在最後還要新增一個屬性。
payload
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}
讀到原始碼
再去修改payload的檔案中的值,然後再去訪問,發現什麼也沒有返回,然後嘗試 /../d0g3_fllllllag 然後base64編碼 去訪問就會返回flag
(2)長度變長
題目 [0CTF] piapiapia // 同樣在buu裡又復現
wp參考這裡 ->https://blog.csdn.net/zz_Caleb/article/details/96777110
a:4:{s:5:"phone";s:11:"12345678901";s:5:"email";s:8:"[email protected]";s:8:"nickname";s:8:"sea_sand";s:5:"photo";s:10:"config.php";}s:39:"upload/804f743824c0451b2f60d81b63b6a900";}
紅色部分是我們想要注入的,這道題的過濾函式有三個 ,但是導致長度變化的過濾是這個
where->hacker 多出了一個字元
但是另一個過濾使 nickname 有長度限制
這裡strlen我們可以用陣列繞過,但是如果使用陣列就會引起序列化字串產生變化
a:4:{s:5:"phone";s:11:"12345678901";s:5:"email";s:8:"[email protected]";s:8:"nickname";a:1:{i:0;s:3:"xxx"};s:5:"photo";s:10:"config.php";}s:39:"upload/804f743824c0451b2f60d81b63b6a900";}
注意陣列在序列化中的表示 是 先; 再 }
這裡我進行了本地測試
結果:
紅色部分為我們想注入的,藍色的是我們提交payload的地方,後面實際上根本不用管
現在我們想的是通過where ->hacker 多了一個字元,這樣使我們輸入的nickname的值逃逸出去變成物件,
加上閉合前面的單引號和反括號 就是這樣 ";}s:5:"photo";s:10:"config.php";}
一共就是34個字元, 一個where 逃逸出一個字元,這裡就需要34個where
payload:wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
三.總結
武漢加油!中國加油!我加油!
&n