PHP檔案包含 整理
阿新 • • 發佈:2020-06-06
# 檔案包含
[TOC]
參考資料:
[檔案包含漏洞簡介](https://www.cnblogs.com/bmjoker/p/9035259.html)
[利用phpinfo條件競爭](https://github.com/vulhub/vulhub/blob/master/php/inclusion/README.zh-cn.md)
[PHP檔案包含漏洞利用思路與Bypass總結手冊](https://mp.weixin.qq.com/s/ZJPfgtEGx4jA_jj0wkqAJA)
## 1. 概述
**什麼是檔案包含:**檔案包含函式所載入的引數沒有經過過濾或者嚴格的定義,可以被使用者控制,包含其他檔案或惡意程式碼,導致資訊洩露或程式碼注入。
要求:包含的檔案路徑攻擊者可控,被包含的檔案web伺服器可訪問。
### 1.1 常見的引發漏洞的函式:
1. `include()`執行到`include`時才包含檔案,檔案不存在時提出警告,但是**繼續執行**。
2. `require()`只要程式執行就會包含檔案,檔案不存在產生致命錯誤,並**停止指令碼**。
3. `include_once()`和`require_once()`只執行一次,如果一個檔案已經被包含,則這兩個函式不會再去包含(==即使檔案中間被修改過==)。
當利用這四個函式來包含檔案時,**不管檔案是什麼型別**(圖片、txt等等),其中的文字內容都會直接作為php程式碼進行解析。
### 1.2 利用條件
- 包含函式通過動態變數的方式引入需要包含的引數。
- PHP中只要檔案內容符合PHP語法規範,**不管是什麼字尾**,都會被解析。
### 1.3 分類和利用思路
檔案包含通常按照包含檔案的位置分為兩類:本地檔案包含(LFI)和遠端檔案包含(RFI),顧名思義,本地檔案包含就是指包含本地伺服器上儲存的一些檔案;遠端檔案包含則是指被包含的檔案不儲存在本地。
**本地檔案包含**
1. 包含本地檔案、執行程式碼
2. 配合檔案上傳,執行惡意指令碼
3. 讀取本地檔案
4. 通過包含日誌的方式GetShell
5. 通過包含`/proc/self/envion`檔案GetShell
6. 通過偽協議執行惡意指令碼
7. 通過`phpinfo`頁面包含臨時檔案
**遠端檔案包含**
1. 直接執行遠端指令碼(在本地執行)
> 遠端檔案包含需要在`php.ini`中進行配置,才可開啟:
>
> `allow_url_fopen = On`:本選項激活了 URL 風格的 fopen 封裝協議,使得可以訪問 URL 物件檔案。預設的封裝協議提供用 ftp 和 http 協議來訪問遠端檔案,一些擴充套件庫例如 zlib 可能會註冊更多的封裝協議。*(出於安全性考慮,此選項只能在 php.ini 中設定。)*
>
> `allow_url_include = On`:此選項允許將具有URL形式的fopen包裝器與以下功能一起使用:include,include_once,require,require_once。(該功能要求`allow_url_fopen`開啟)
## 2. 利用方法
### 2.1 配合檔案解析漏洞來包含
`http://target.com/?page=../../upload/123.jpg/.php`
### 2.2 讀取系統敏感檔案(路徑遍歷)
`include.php?file=../../../../../../../etc/passwd`
**Windows:**
> C:\boot.ini //檢視系統版本
> C:\Windows\System32\inetsrv\MetaBase.xml //IIS配置檔案
> C:\Windows\repair\sam //儲存系統初次安裝的密碼
> C:\Program Files\mysql\my.ini //Mysql配置
> C:\Program Files\mysql\data\mysql\user.MYD //Mysql root
> C:\Windows\php.ini //php配置資訊
> C:\Windows\my.ini //Mysql配置資訊
**Linux:**
> /root/.ssh/authorized_keys
> /root/.ssh/id_rsa
> /root/.ssh/id_ras.keystore
> /root/.ssh/known_hosts
> /etc/passwd
> /etc/shadow
> /etc/my.cnf
> /etc/httpd/conf/httpd.conf
> /root/.bash_history
> /root/.mysql_history
> /proc/self/fd/fd[0-9]*(檔案識別符號)
> /proc/mounts
> /porc/config.gz
### 2.3 包含http日誌檔案
通過包含**日誌檔案**,來執行夾雜在URL請求或者`User-Agent`頭中的惡意指令碼
1. 通過讀取配置檔案確定日誌檔案地址
預設地址通常為:`/var/log/httpd/access_log`或`/var/log/apache2/access.log`
![1568647706502](https://img2020.cnblogs.com/blog/1913322/202006/1913322-20200606145430731-1096854852.png)
2. 請求時直接在URL後面加上指令碼即可`http://www.target.com/index.php`,之後去包含這個日誌檔案即可。
3. **注意:**日誌檔案會記錄最為原始的URL請求,在瀏覽器位址列中輸入的地址會被URL編碼,通過CURl或者Burp改包繞過編碼。
> apache+Linux 日誌預設路徑
> /etc/httpd/logs/access_log
> /var/log/httpd/access_log
> xmapp日誌預設路徑
> D:/xampp/apache/logs/access.log
> D:/xampp/apache/logs/error.log
> IIS預設日誌檔案
> C:/WINDOWS/system32/Logfiles
> %SystemDrive%/inetpub/logs/LogFiles
> nginx
> /usr/local/nginx/logs
> /opt/nginx/logs/access.log
通過包含**環境變數**`/proc/slef/enversion`來執行惡意指令碼,修改HTTP請求的`User-Agent`報頭,**但是**沒復現成功 :cry:
### 2.4 包含SSH日誌
和包含HTTP日誌類似,登入使用者的使用者名稱會被記錄在日誌中,如果可以讀取到ssh日誌檔案,則可以利用惡意使用者名稱注入php程式碼。
SSH登入日誌常見儲存位置:`/var/log/auth.log`或`/var/log/secure`
![image-20200602222554534](https://img2020.cnblogs.com/blog/1913322/202006/1913322-20200606145430945-674974455.png)
![image-20200602223106890](https://img2020.cnblogs.com/blog/1913322/202006/1913322-20200606145431463-944454659.png)
### 2.5 使用PHP偽協議
PHP內建了很多URL 風格的封裝協議,除了用於檔案包含,還可以用於很多檔案操作函式。在phpinfo的`Registered PHP Streams`中可以找到目前環境下可用的協議。
![image-20200601222936655](https://img2020.cnblogs.com/blog/1913322/202006/1913322-20200606145431691-941053346.png)
```
file:// — 訪問本地檔案系統
http:// — 訪問 HTTP(s) 網址
ftp:// — 訪問 FTP(s) URLs
php:// — 訪問各個輸入/輸出流(I/O streams
zlib:// — 壓縮流
data:// — 資料(RFC 2397)
glob:// — 查詢匹配的檔案路徑模式
phar:// — PHP 壓縮檔案
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音訊流
expect:// — 處理互動式的流
```
1. `file://`訪問**本地**檔案系統`http://target.com/?page=file://D:/www/page.txt`,正反斜線都行(windows),對於共享檔案伺服器可以使用`\\smbserver\share\path\to\winfile.ext`。
2. `php://input`訪問輸入輸出流:`?page=php://input`,在POST內容中輸入想要執行的指令碼。
3. `php://filter`:是一種元封裝器, 設計用於資料流開啟時的篩選過濾應用。
![image-20200601223225081](https://img2020.cnblogs.com/blog/1913322/202006/1913322-20200606145431884-687877650.png)
**全部可用過濾器列表:**https://www.php.net/manual/zh/filters.php
通常利用該偽協議來讀取php原始碼,通過設定編碼方式(以base64編碼為例),可以防止讀取的內容被當做php程式碼解析,利用方式(就是read寫不寫的區別):
```
index.php?file=php://filter/read=convert.base64-encode/resource=index.php
index.php?file=php://filter/convert.base64-encode/resource=index.php
```
4. `data://`資料流封裝:`?page=data://text/plain,指令碼`
![img](https://img2020.cnblogs.com/blog/1913322/202006/1913322-20200606145432088-791098096.png)
5. `zip://`壓縮流:建立惡意程式碼檔案,新增到壓縮資料夾,上傳,**無視字尾**。通過`?page=zip://絕對路徑%23檔名`訪問,5.2.9之前是隻能絕對路徑。
**備註:**
1. 檔案需要絕對路徑才能訪問
2. 需要通過`#`(也就是URL中的`%23`)來指定程式碼檔案
6. `compress.bzip2://`和`compress.zlib://`壓縮流,與zip類似,但是**支援相對路徑**,**無視字尾**
`bzip`和`gzip`是對單個檔案進行壓縮(不要糾結要不要指定壓縮包內的檔案:smile:)
```
?file=compress.bzip2://路徑
?file=compress.zlib://路徑
```
7. `phar://`支援zip、phar格式的壓縮(歸檔)檔案,**無視字尾**(也就是說jpg字尾照樣給你解開來),`?file=phar://壓縮包路徑/壓縮包內檔名`,絕對路徑和相對路徑都行。
利用方法:
```
index.php?file=phar://test.zip/test.txt
index.php?file=phar://test.xxx/test.txt
```
> 製作phar檔案(php5.3之後):
>
> 1. 設定`php.ini`中`phar.readonly=off`
> 2. 製作生成指令碼
>
> ```php
> @unlink("phar.phar");
> $phar = new Phar("phar.phar");
> $phar->startBuffering();
> $phar->setStub(""); //設定stub
> $phar->addFromString("test.txt", ""); //新增要壓縮的檔案及內容
> $phar->stopBuffering(); //簽名自動計算
> ?>
> // 這個指令碼需要使用php.exe 來生成
> ```
>
> 3. 生成指令碼2
>
> ```php
> $p = new PharData(dirname(__FILE__).'./test.123', 0,'test',Phar::ZIP);
> $p->addFromString('test.txt', '');
> ?>
> //這個指令碼可以通過訪問來觸發,在本地生成一個test.123,但是不能生成字尾為phar的檔案(其他的都行,甚至是php)
> ```
>
>
### 2.6 配合phpinfo頁面包含臨時檔案
向phpinfo頁面上傳檔案的時候,phpinfo會返回臨時檔案的儲存路徑
![1568784122538](https://img2020.cnblogs.com/blog/1913322/202006/1913322-20200606145432257-1322033833.png)
臨時檔案存活時間很短,當連線結束後,臨時檔案就會消失。**條件競爭**
只要傳送足夠多的的資料,讓頁面還未反應過來的時候去包含檔案,即可。
1. 傳送包含了webshell的上傳資料包給phpinfo頁面,這個資料包的header、get等位置需要塞滿垃圾資料
2. 因為phpinfo頁面會將所有資料都打印出來,1中的垃圾資料會將整個phpinfo頁面撐得非常大
3. php預設的輸出緩衝區大小為4096,可以理解為php每次返回4096個位元組給socket連線
4. 所以,我們直接操作原生socket,每次讀取4096個位元組。只要讀取到的字元裡包含臨時檔名,就立即傳送第二個資料包
5. 此時,第一個資料包的socket連線實際上還沒結束,因為php還在繼續每次輸出4096個位元組,所以臨時檔案此時還沒有刪除
6. 利用這個時間差,第二個資料包,也就是檔案包含漏洞的利用,即可成功包含臨時檔案,最終getshell
利用指令碼[exp](https://github.com/vulhub/vulhub/blob/master/php/inclusion/exp.py)
### 2.7 包含Session
1. PHP將使用者Session以檔案的形式儲存在主機中,通過`php.ini`檔案中的`session.save_path`欄位可以設定具體的儲存位置,通過phpinfo頁面也可以查詢到;檔案命名格式為