1. 程式人生 > >PHP檔案包含 整理

PHP檔案包含 整理

# 檔案包含 [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頁面也可以查詢到;檔案命名格式為