摘要:函數(shù)并不屬于擴展的部分,標(biāo)準(zhǔn)安裝的都可以使用這個函數(shù)。執(zhí)行一下你會發(fā)現(xiàn)完全可以執(zhí)行成功。所以,對于正常的圖片文件,完全可以勝任,但是對于一些有心構(gòu)造的文件結(jié)構(gòu)卻不行。
getimagesize 函數(shù)并不屬于 GD 擴展的部分,標(biāo)準(zhǔn)安裝的 PHP 都可以使用這個函數(shù)??梢韵瓤纯催@個函數(shù)的文檔描述:http://php.net/manual/zh/function.getimagesize.php
如果指定的文件如果不是有效的圖像,會返回 false,返回數(shù)據(jù)中也有表示文檔類型的字段。如果不用來獲取文件的大小而是使用它來判斷上傳文件是否是圖片文件,看起來似乎是個很不錯的方案,當(dāng)然這需要屏蔽掉可能產(chǎn)生的警告,比如代碼這樣寫:
但是如果你僅僅是做了這樣的驗證,那么很不幸,你成功的在代碼里種下了一個 webshell 的隱患。
要分析這個問題,我們先來看一下這個函數(shù)的原型:
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); }限于篇幅上面隱藏了一些細(xì)節(jié),現(xiàn)在從上面的代碼中我們知道兩件事情就夠了:
最終處理的函數(shù)是 php_getimagesize_from_stream
負(fù)責(zé)判斷文件類型的函數(shù)是 php_getimagetype
接下來看一下 php_getimagetype 的實現(xiàn):
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)) { ... } }去掉了一些細(xì)節(jié),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 是根據(jù)文件流的前幾個字節(jié)(文件頭)來判斷的。那么既然如此,我們可不可以構(gòu)造一個特殊的 PHP 文件來繞過這個判斷呢?不如來嘗試一下。
找一個十六進制編輯器來寫一個的 PHP 語句,比如:
這幾個字符的十六進制編碼(UTF-8)是這樣的:
3C3F 7068 7020 7068 7069 6E66 6F28 293B 203F 3E我們構(gòu)造一下,把 PNG 文件的頭字節(jié)加在前面變成這樣的:
8950 4E47 0D0A 1A0A 3C3F 7068 7020 7068 7069 6E66 6F28 293B 203F 3E最后保存成 .php 后綴的文件(注意上面是文件的十六進制值),比如 test.php。執(zhí)行一下 php test.php 你會發(fā)現(xiàn)完全可以執(zhí)行成功。那么能用 getimagesize 讀取它的文件信息嗎?新建一個文件寫入代碼試一下:
執(zhí)行結(jié)果:
Array ( [0] => 1885957734 [1] => 1864902971 [2] => 3 [3] => width="1885957734" height="1864902971" [bits] => 32 [mime] => image/png )成功讀取出來,并且文件也被正常識別為 PNG 文件,雖然寬和高的值都大的有點離譜。
現(xiàn)在你應(yīng)該明白為什么上文說這里留下了一個 webshell 的隱患的吧。如果這里只有這樣的上傳判斷,而且上傳之后的文件是可以訪問的,就可以通過這個入口注入任意代碼執(zhí)行了。
那么為什么上面的文件可以 PHP 是可以正常執(zhí)行的呢?用 token_get_all 函數(shù)來看一下這個文件:
如果顯示正常的話你能看到輸出數(shù)組的第一個元素的解析器代號是 312,通過 token_name 獲取到的名稱會是 T_INLINE_HTML,也就是說文件頭部的信息被當(dāng)成正常的內(nèi)嵌的 HTML 代碼被忽略掉了。
至于為什么會有一個大的離譜的寬和高,看一下 php_handle_png 函數(shù)的實現(xiàn)就能知道,這些信息也是通過讀取特定的文件頭的位來獲取的。
所以,對于正常的圖片文件,getimagesize 完全可以勝任,但是對于一些有心構(gòu)造的文件結(jié)構(gòu)卻不行。
在處理用戶上傳的文件時,先簡單粗暴的判斷文件擴展名并對文件名做一下處理,保證在服務(wù)器上不是 php 文件都不能直接執(zhí)行也是一種有效的方式。然后可以使用 getimagesize 做一些輔助處理。
個人博客地址:http://0x1.im
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/21197.html
摘要:一執(zhí)行系統(tǒng)外部命令輸出并返回最后一行結(jié)果。相同點都可以獲得命令執(zhí)行的狀態(tài)碼用提供的專門函數(shù)提供共了個專門的執(zhí)行外部命令的函數(shù),,。第二個參數(shù)是可選的,用來得到命令執(zhí)行后的狀態(tài)碼。 php高級語法總結(jié)。 一、執(zhí)行系統(tǒng)外部命令 system() 輸出并返回最后一行shell結(jié)果。 exec() 不輸出結(jié)果,返回最后一行shell結(jié)果,所有結(jié)果可以保存到一個返回的數(shù)組里面。 pass...
摘要:二維碼圖片寬度二維碼圖片高度圖片寬度圖片高度重新組合圖片并調(diào)整大小最后直接輸出圖像即可還是老樣子,我們給圖片來個名字這次用為隨機數(shù)時間戳而后輸出圖像整個帶的二維碼生成就完成了。 利用 phpqrcode 二維碼生成類庫和 imagecopymerge 函數(shù)制作帶二維碼的圖片 首先引用phpqrcode類庫 下載phpqrcode類庫 下載地址就不提供了,百度一搜一大把; 新建...
摘要:二維碼圖片寬度二維碼圖片高度圖片寬度圖片高度重新組合圖片并調(diào)整大小最后直接輸出圖像即可還是老樣子,我們給圖片來個名字這次用為隨機數(shù)時間戳而后輸出圖像整個帶的二維碼生成就完成了。 利用 phpqrcode 二維碼生成類庫和 imagecopymerge 函數(shù)制作帶二維碼的圖片 首先引用phpqrcode類庫 下載phpqrcode類庫 下載地址就不提供了,百度一搜一大把; 新建...
閱讀 1101·2021-11-15 18:00
閱讀 2815·2021-09-22 15:18
閱讀 1977·2021-09-04 16:45
閱讀 758·2019-08-30 15:55
閱讀 3870·2019-08-30 13:10
閱讀 1345·2019-08-30 11:06
閱讀 1994·2019-08-29 12:51
閱讀 2302·2019-08-26 13:55