摘要:函數并不屬于擴展的部分,標準安裝的都可以使用這個函數。執行一下你會發現完全可以執行成功。所以,對于正常的圖片文件,完全可以勝任,但是對于一些有心構造的文件結構卻不行。
getimagesize 函數并不屬于 GD 擴展的部分,標準安裝的 PHP 都可以使用這個函數。可以先看看這個函數的文檔描述:http://php.net/manual/zh/function.getimagesize.php
如果指定的文件如果不是有效的圖像,會返回 false,返回數據中也有表示文檔類型的字段。如果不用來獲取文件的大小而是使用它來判斷上傳文件是否是圖片文件,看起來似乎是個很不錯的方案,當然這需要屏蔽掉可能產生的警告,比如代碼這樣寫:
但是如果你僅僅是做了這樣的驗證,那么很不幸,你成功的在代碼里種下了一個 webshell 的隱患。
要分析這個問題,我們先來看一下這個函數的原型:
static void php_getimagesize_from_stream(php_stream *stream, zval **info, INTERNAL_FUNCTION_PARAMETERS) { ... itype = php_getimagetype(stream, NULL TSRMLS_CC); switch( itype) { ... } ... } static void php_getimagesize_from_any(INTERNAL_FUNCTION_PARAMETERS, int mode) { ... php_getimagesize_from_stream(stream, info, INTERNAL_FUNCTION_PARAM_PASSTHRU); php_stream_close(stream); } PHP_FUNCTION(getimagesize) { php_getimagesize_from_any(INTERNAL_FUNCTION_PARAM_PASSTHRU, FROM_PATH); }限于篇幅上面隱藏了一些細節,現在從上面的代碼中我們知道兩件事情就夠了:
最終處理的函數是 php_getimagesize_from_stream
負責判斷文件類型的函數是 php_getimagetype
接下來看一下 php_getimagetype 的實現:
PHPAPI int php_getimagetype(php_stream * stream, char *filetype TSRMLS_DC) { ... if (!memcmp(filetype, php_sig_gif, 3)) { return IMAGE_FILETYPE_GIF; } else if (!memcmp(filetype, php_sig_jpg, 3)) { return IMAGE_FILETYPE_JPEG; } else if (!memcmp(filetype, php_sig_png, 3)) { ... } }去掉了一些細節,php_sig_gif php_sig_png 等是在文件頭部定義的:
PHPAPI const char php_sig_gif[3] = {"G", "I", "F"}; ... PHPAPI const char php_sig_png[8] = {(char) 0x89, (char) 0x50, (char) 0x4e, (char) 0x47, (char) 0x0d, (char) 0x0a, (char) 0x1a, (char) 0x0a};可以看出來 image type 是根據文件流的前幾個字節(文件頭)來判斷的。那么既然如此,我們可不可以構造一個特殊的 PHP 文件來繞過這個判斷呢?不如來嘗試一下。
找一個十六進制編輯器來寫一個的 PHP 語句,比如:
這幾個字符的十六進制編碼(UTF-8)是這樣的:
3C3F 7068 7020 7068 7069 6E66 6F28 293B 203F 3E我們構造一下,把 PNG 文件的頭字節加在前面變成這樣的:
8950 4E47 0D0A 1A0A 3C3F 7068 7020 7068 7069 6E66 6F28 293B 203F 3E最后保存成 .php 后綴的文件(注意上面是文件的十六進制值),比如 test.php。執行一下 php test.php 你會發現完全可以執行成功。那么能用 getimagesize 讀取它的文件信息嗎?新建一個文件寫入代碼試一下:
執行結果:
Array ( [0] => 1885957734 [1] => 1864902971 [2] => 3 [3] => width="1885957734" height="1864902971" [bits] => 32 [mime] => image/png )成功讀取出來,并且文件也被正常識別為 PNG 文件,雖然寬和高的值都大的有點離譜。
現在你應該明白為什么上文說這里留下了一個 webshell 的隱患的吧。如果這里只有這樣的上傳判斷,而且上傳之后的文件是可以訪問的,就可以通過這個入口注入任意代碼執行了。
那么為什么上面的文件可以 PHP 是可以正常執行的呢?用 token_get_all 函數來看一下這個文件:
如果顯示正常的話你能看到輸出數組的第一個元素的解析器代號是 312,通過 token_name 獲取到的名稱會是 T_INLINE_HTML,也就是說文件頭部的信息被當成正常的內嵌的 HTML 代碼被忽略掉了。
至于為什么會有一個大的離譜的寬和高,看一下 php_handle_png 函數的實現就能知道,這些信息也是通過讀取特定的文件頭的位來獲取的。
所以,對于正常的圖片文件,getimagesize 完全可以勝任,但是對于一些有心構造的文件結構卻不行。
在處理用戶上傳的文件時,先簡單粗暴的判斷文件擴展名并對文件名做一下處理,保證在服務器上不是 php 文件都不能直接執行也是一種有效的方式。然后可以使用 getimagesize 做一些輔助處理。
個人博客地址:http://0x1.im
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/21197.html
摘要:二維碼圖片寬度二維碼圖片高度圖片寬度圖片高度重新組合圖片并調整大小最后直接輸出圖像即可還是老樣子,我們給圖片來個名字這次用為隨機數時間戳而后輸出圖像整個帶的二維碼生成就完成了。 利用 phpqrcode 二維碼生成類庫和 imagecopymerge 函數制作帶二維碼的圖片 首先引用phpqrcode類庫 下載phpqrcode類庫 下載地址就不提供了,百度一搜一大把; 新建...
摘要:二維碼圖片寬度二維碼圖片高度圖片寬度圖片高度重新組合圖片并調整大小最后直接輸出圖像即可還是老樣子,我們給圖片來個名字這次用為隨機數時間戳而后輸出圖像整個帶的二維碼生成就完成了。 利用 phpqrcode 二維碼生成類庫和 imagecopymerge 函數制作帶二維碼的圖片 首先引用phpqrcode類庫 下載phpqrcode類庫 下載地址就不提供了,百度一搜一大把; 新建...
閱讀 1091·2021-11-15 18:00
閱讀 2803·2021-09-22 15:18
閱讀 1965·2021-09-04 16:45
閱讀 750·2019-08-30 15:55
閱讀 3853·2019-08-30 13:10
閱讀 1332·2019-08-30 11:06
閱讀 1984·2019-08-29 12:51
閱讀 2294·2019-08-26 13:55