當前位置:
首頁 > 最新 > 一個CTF GAME引發的php內核分析

一個CTF GAME引發的php內核分析

solveme是一個CTF的練習平台,其中winter sleep題目是這樣的。

輸入一個字元串,通過is_numric的判斷,要大於5184000小於777600,最後通過sleep函數,就可以輸出flag。顯然,如果輸入一個較大的數,會sleep很長時間。需要一個數大於5184000,然後int之後又要是一個很小的數。

解決的方案是這樣的:

可以看以上腳本輸出內容:

使用科學計數法。

看了一些writeup,只是給出了解決的辦法,但是並沒有詳細的說明,為什麼會這樣。有的地方提到說是弱類型,雖然這幾次比較存在類型的自動轉換,但是跟我理解的弱類型的自動轉換存在差異。所以想要探究一番。

黑盒測試

可以看到當接收到科學計數法表示的字元串跟一個整型變數運算(『6e6』-0),6e6自動並不是自動轉換成了int型,而是轉換成了float,所以最終的數字是float型的6000000。最後兩行代碼可以直接的說明了問題。使用int強制轉換一個科學計數法表示的字元串,轉換過程中並不能識別科學計數法,只是把e當做普通字元了。效果跟6a6是一樣的。而用float轉成浮點數,則可以成功識別科學計數法。

feature or bug

我的感覺是這應該是php的一個bug。同一個字元串,轉換成int型和float型有著兩種解釋。正常的邏輯應該是(int)』6e6』 = (int)(float)』6e6』。這樣才比較符合正常的一個理解邏輯。

找了幾個php的版本,分別做了下測試:

測試腳本如下:

結果如下:

在php7.0以前的版本中(int)』6e6』結果是6,但是在7.1以後的版本中,(int)』6e6』已經是6000000,符合(int)』6e6』 = (int)(float)』6e6』這個邏輯了。

php內核分析

以下內容引用自《php7內核剖析》:

PHP是弱類型語言,不需要明確的定義變數的類型,變數的類型根據使用時的上下文所決定,也就是變數會根據不同表達式所需要的類型自動轉換,比如求和,PHP會將兩個相加的值轉為long、double再進行加和。每種類型轉為另外一種類型都有固定的規則,當某個操作發現類型不符時就會按照這個規則進行轉換,這個規則正是弱類型實現的基礎。

除了自動類型轉換,PHP還提供了一種強制的轉換方式:

(int)/(integer):轉換為整形 integer

(bool)/(boolean):轉換為布爾類型 boolean

(float)/(double)/(real):轉換為浮點型 float

(string):轉換為字元串 string

(array):轉換為數組 array

(object):轉換為對象 object

(unset):轉換為 NULL

無論是自動類型轉換還是強制類型轉換,不是每種類型都可以轉為任意其他類型。

4.1.3 轉換為整型

其它類型轉為整形的轉換規則:

NULL:轉為0

布爾型:false轉為0,true轉為1

浮點型:向下取整,比如:(int)2.8 => 2

字元串:就是C語言strtoll()的規則,如果字元串以合法的數值開始,則使用該數值,否則其值為 0(零),合法數值由可選的正負號,後面跟著一個或多個數字(可能有小數點),再跟著可選的指數部分

數組:很多操作不支持將一個數組自動整形處理,比如:array() + 2,將報error錯誤,但可以強制把數組轉為整形,非空數組轉為1,空數組轉為0,沒有其他值

對象:與數組類似,很多操作也不支持將對象自動轉為整形,但有些操作只會拋一個warning警告,還是會把對象轉為1操作的,這個需要看不同操作的處理情況

資源:轉為分配給這個資源的唯一編號

具體處理:

ZEND_API zend_long ZEND_FASTCALL _zval_get_long_func(zval *op)

{

try_again:

}

4.1.4 轉換為浮點型

除字元串類型外,其它類型轉換規則與整形基本一致,就是整形轉換結果加了一位小數,字元串轉為浮點數由zend_strtod()完成,這個函數非常長,定義在zend_strtod.c中,這裡不作說明。

書中提到,字元串轉換為整型,是C語言strtol()的規則,由ZEND_STRTOL函數完成的,字元串轉換成浮點數,是用zend_strtod函數完成的。

對比一下C語言的strtol和strtod

strtol不能識別科學計數法,字元串6e6轉成整型是6,而strtod可以識別科學計數法,6e6轉成浮點數是6000000。

動態調試php內核

編譯debug版php。

gdb調試

在類型轉換函數上下斷點:

可以看到使用zend_strtol函數進行轉換。

zent_strtol 直接是使用strtoll。

調試一下7.1版本php

可以看到7.1版中使用了新的函數is_numeric_string替代strtoll。注釋中說明使用新函數是為了避免strtoll的溢出問題,自己實現了is_number_string函數來替代strtoll。然而並沒有提到科學計數法表示的字元串的問題。但是實際實現上跟strtoll有不同。妥善的處理科學計數法表示的數字。

最終的字元串轉整型的邏輯如下:

最終的處理邏輯是如果發現了小數點或者數字e,就採用zend_strtod來處理,這樣就跟字元串轉浮點數是一模一樣的處理邏輯了。所以最終的結果也就符合了(int)』6e6』 = (int)(float)』6e6』這個邏輯。

思考

那麼這到底是個bug還是feature呢。最終的結果來看,php7.0及以前的版本使用strtoll轉字元串到整型,7.1以後的版本使用了strtod來轉換。所以strtoll不能識別科學計數法表示的數字是不是一個bug。

參考資料

solveme.peng.kr的中文writeup:http://www.freebuf.com/articles/web/165537.html

php源碼調試的文章:https://gywbd.github.io/posts/2016/2/debug-php-source-code.html

php7內核分析:https://github.com/pangudashu/php7-internal

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 隨風小記 的精彩文章:

TAG:隨風小記 |