phpjiami 數種解密方法
Pwnhub公開賽出了個簡單的PHP代碼審計題目,考點有兩個:
如果說僅為了做出題目拿到flag,這個題目太簡單,後台也有數十名選手提交了答案和writeup。但深入研究一下這兩個知識點,還是很有意思的。
0x01 phpjiami 代碼分析破解法
這種方法我最佩服了,作者甚至給出了解密腳本,文章如下: http://sec2hack.com/web/phpjiami-decode.html 。
我自己在出題目之前也進行過分析,但後面並沒有耐心寫一個完整的腳本出來,所以我十分佩服這個作者。
我們分析phpjiami後的文件,可以看到他有如下特點:
函數名、變數名全部變成「亂碼」
改動任意一個地方,將導致文件不能運行
之所以函數名、變數名可以變成「亂碼」,是因為PHP的函數名、變數名是支持除了特殊符號以外大部分字元的,比如漢字等。利用這一特點,phpjiami就將所有正常的英文變數給轉換了一下形式,其實沒有什麼特別的奧秘。
那麼,為了方便分析,我們可以想辦法再將其轉換回英文和數字。比如,作者使用的是 http://zhaoyuanma.com/phpcodefix.html 對混淆過的代碼進行美化;而我是使用 https://github.com/nikic/PHP-Parser 對整個代碼進行了結構化的分析,並將所有變數和函數名進行了美化。
方法一的好處是我不需要寫任何代碼,就可以大致進行美化,但顯然,美化後的代碼是有錯誤的,原文中也提到了這一點;方法二,雖然需要自己寫代碼,但美化後的代碼沒有語法錯誤,看起來更加直觀,並且我還能進一步的進行美化,比如將字元串中的亂碼轉換成 的形式。
我美化後的代碼如下:
後續的操作和上文也差不多,通過源碼的分析,正如上文中所說,phpjiami加密源碼的整個流程是:
加密流程:源碼 -> 加密處理(壓縮,替換,BASE64,轉義)-> 安全處理(驗證文件 MD5 值,限制 IP、限域名、限時間、防破解、防命令行調試)-> 加密程序成品,再簡單的說:源碼 + 加密外殼 == 加密程序 (該段出處)
所以,其實這種方法並沒有對源碼進行混淆,只是對「解密源碼的殼」進行了混淆。所以你看到的中文變數、中文函數,其實是一個殼,去掉這層殼,我可以拿到完整的PHP源碼。
所以呀,後台提交的writeup里,有的同學想當然地認為修改eval為echo就能輸出源碼了……實際上根本沒實際試過,改動文件是會導致不能運行的;還有同學認為這裡僅是將源碼混淆為用戶體驗極差的代碼,導致人眼無法閱讀,並沒有理解這裡其實混淆的不是源碼。
0x01中說到的方法固然是很美好的,但是假如加密者隨意改動一點加密的邏輯,可能導致我們需要重新分析加密方法,寫解密腳本。我們有沒有更通用的方法?
HOOK EVAL應該是被提到過最多的方法,我也看到了Medici.Yan發布的一篇文章: http://blog.evalbug.com/2017/09/21/phpdecode_01/
我前文說過,phpjiami其實是只是混淆了殼,這個殼的作用是執行真正的源碼。那麼,執行源碼必然是會經過eval之類的「函數」(當然也不盡然),那麼,如果我們能夠有辦法將eval給替換掉,不就可以獲得源碼了么?
遺憾的是,如果我們僅僅簡單地將eval替換成echo,將導致整個腳本不能運行——因為phpjiami檢測了文件是否被修改。
那麼,我們可以尋求更底層的方法。就是很多人以前提到過的,將PHP底層的函數 給攔截下來,並輸出值。Medici.Yan的文章中說的很清楚,也給出了參考文檔和源碼,我就不再贅述了。
我自己簡單寫了一個擴展,並用php5.6編譯: https://drive.google.com/open?id=0B4uxE69uafD5anVTZ1VwNXN0WEU
下載之,在php.ini中添加 ,然後直接訪問加密過的php代碼即可(當時參考tool.lu的站長xiaozi的代碼 http://type.so/c/php-dump-eval.html ,所以分隔符里有關鍵字):
16年kuuki曾分享過一個在線解密的工具: https://xianzhi.aliyun.com/forum/read/64.html ,但測試了一下phpjiami解密不了。原因是,phpjiami在解密的時候會進行驗證:
php_sapi_name() == cli ?die(): ;
所以如果這個源碼是在命令行下運行,在執行這條語句的時候就die了。所以,即使你編譯好了hookeval.so並開啟了這個擴展,也需要在Web環境下運行。
提高篇:有沒有什麼簡單的辦法在命令行下也能模擬web環境呢?方法我先不說,大家可以自己思考思考。
那麼有的同學說:php擴展太難了,我不會寫C語言,怎麼辦?
不會寫C語言也沒關係,你只需要會寫PHP即可。這是我鳳凰師傅提到的一個方法,也是我理想中的一個解,非常簡單,兩行代碼搞定,解密用時比你去網上花錢解密還短:
原理其實也很簡單。phpjiami的殼在解密源碼並執行後,遺留下來一些變數,這些變數里就包含了解密後的源碼。
雖然我們不能直接修改index.php,將這些變數列印出來,但是我們可以動態包含之,並列印下所有變數,其中必定有我們需要的源碼( 輸出的不完整,只是用它舉個例子):
當然,這個方法雖然簡單,但有個很嚴重的問題:假如在執行源碼的過程中 了,我們就執行不到列印變數的地方了。
所以,這個方法並不一定適用於所有情景,但對於本題來說,已經足夠了。
那麼,如果我們遇到0x03解決不了的情況怎麼辦?
這時候就要祭出動態調試武器了。儘管加密後的文件看起來亂七八糟,但其仍然是一個符合php語法的php文件,那麼我們就可以直接利用動態調試工具進行單步調試,拿到源碼。
簡單拿xdebug進行調試,不停單步調試後,就可以發現我們需要的源碼已經在上下文變數中的:
右鍵「複製值」,即可拿到源碼。這也算一個比較簡單的方法了。
當然,假如有一天phpjiami修改了混淆流程,源碼不再儲存於變數中,那麼就需要分析一下代碼執行的流程。所謂萬變不離其中,最終斷在eval的那一步,一定有你需要的源碼。
0x05 代碼審計Getshell
分析FileUpload類,發現其取後綴有兩種方式:將文件名用 分割成數組 ,一是用 的方式取數組最後一個元素,二是用 的方式取數組最後一個元素。
正常來說,字元串用 分割成的數組,用這兩種方法取到的末元素應該是相同的。但取文件名的時候,如果我們已經傳入的是數組,則不會再次進行分割:
$filename = $_POST[...];if(!is_array($filename)) { $filename = explode( . , $filename);}
也就是說我能控制 這個數組。所以,我只需要找到 和 的區別,即可繞過後綴檢查。
顯然,前者是取根據數組下標來取的值,後者取的永遠是數組裡最後一個元素。所以,我們只需要讓下標等於 的元素不是數組最後一個元素即可。
比如: 或者 。
最後想說一句話:不求甚解是阻礙部分人進步的一大阻力。共勉。
※Swoft-基於swoole2.0的高性能微服務框架
※如何面試 PHP 工程師?
※一個MySQL 5.7分區表性能下降的案例分析
※代碼質量管控的四個階段
※利用sed批量更改文件名
TAG:PHP技術大全 |