我又回來更新了,這次是關于web方面的檔案包含漏洞.我會在後面以詳細的角度來寫清楚這個漏洞的利用方法.
當然,以下都是我自己的了解,表述什麼的都有些野人化了.是以希望各位大佬手下留情.
一.漏洞産生的原因
這個漏洞可以追溯到很久.更準确來說,其實是人為産生的.由于我php學的不是很專業,是以我就拿c語言來舉例了.php裡面使用的是include指令,c語言使用的是#include預處理指令.作用是相似的.
我建立了兩個檔案,内容如圖.
wzc.h:
#include "stdio.h"
void print_f(){
printf("this is test file");
}
main.c:
#include "wzc.h"
int main(){
print_f();
return 0;
}
程式運作結果如圖:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiclRnblN2XjlGcjAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLzcmeNhXU65ENFpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLygTOwQTNxcTM1EDMxAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
學過c語言的應該都知道我剛才幹了件什麼事情.我先寫了一個自己建立的頭檔案庫,裡面存放了一個"print_f"函數,作用是在螢幕上顯示一句話.
然後我使用預處理指令将檔案進行了一個包含的操作,這樣我後面就可以多次調用這個print_f函數了.
這麼做在工程量少的情況下是看不出來有什麼作用的,但是當你在做一個大工程時,需要多次實作某個功能的時候,就可以派上用場了,很完美的一個操作,對吧?
但是,凡事都有例外.
假如我是黑客,然後我在某個公司開發的c語言程式的被包含檔案中,偷偷把被包含的檔案中添加一些後門指令,是不是就産生問題了呢?最起碼能讓你這個程式崩潰不能運作.
不過這并不是我們檔案包含漏洞主要被利用的地方,我們要說的是利用文法來鑽空子.
觀察我們的代碼第一行:
在c語言中,被包含的檔案名左右的包含符号有兩種.
第一種是兩個尖括号.(<>)
這種寫法編譯器會在系統自帶的頭檔案庫中尋找所需要的檔案進行包含.例如:
第二種是兩個雙引号("")
這種寫法編譯器會首先在c檔案目錄下尋找需要被包含的檔案,如果沒有,就會去系統檔案夾下尋找.
ok,了解這兩種寫法以後,我們可以繼續往下走了.
雖然說如果使用("")這種寫法,機器會自動尋找目前目錄下的規定檔案,但是有時候我們的頭檔案庫特别多,需要專門建一個檔案夾來存放,那這時候該怎麼引用呢?
比如我們如圖的形式.
就像這樣,當我們檔案存放在不同地方的時候,我們的c程式在引用的時候就需要改變一下形式了.
main.c:
#include <stdio.h>
#include "..\\include\\wzc.h"
#include "..\\include\\wzc2.h"
int main(){
print_one();
print_two();
return 0;
}
像這樣,類似于cmd指令中寫路徑的時候一樣的,"…“代表上一級目錄.不過由于需要考慮c語言的轉義字元的關系,是以我們需要把反斜杠打成兩個才可以.
很好,我想你們應該已經認識到了關于使用c語言的預處理指令來進行檔案包含了.
但是,想一想這個問題,假如說被包含的檔案名如果可以通過使用者自己輸入的話,那是不是就很恐怖?如果我在被包含的檔案多寫幾個”…/",是不是就代表着我們就可以沒有限制的通路任何目錄下的某個檔案的内容了?
當然,由于c語言是編譯器處理這個事情的,是以不會産生這個問題.
但是php就不一樣了,因為它可以直接使用類似于echo的方法直接使用include,甚至允許使用者自己輸入被包包含的内容,這樣做的後果就不需要我說了吧?十分危險.
是以.一些網站在需要使用這個指令的時候,就會對使用者的輸入進行一個過濾,進而防止使用者使用這個漏洞進行一些非法操作.
二.漏洞的實際利用
1)分析源碼
這個是我偶然遇到的一個web題,來自于buuctf網站的web的第一題.如圖,我們首先通路靶機,顯示的是一張圖檔.
右鍵檢視源碼.
發現在注釋中有個source.php,我們嘗試進行通路發現成功.
很好,又到了我們喜聞樂見的代碼審計環節.我對代碼的分析放在了下面.
<?php
highlight_file(__FILE__);
//這個函數是将檔案按照文法的高亮來顯示.後面那個__FILE__我沒有查到,應該是代表本檔案的意思
class emmm
//建立一個emmm類
{
public static function checkFile(&$page)
//類中的checkFile函數
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
//建立一個清單,清單中添加了兩個key,并且有兩個對應關系.這個清單的作用類似于白名單
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
//這是第一個對page進行判斷的地方.檢查page變量,判斷是否是空變量和判斷是否是字元串
if (in_array($page, $whitelist)) {
return true;
}
//判斷page變量是否存在于白名單之中
$_page = mb_substr($page,0,mb_strpos($page. '?','?'));
//_page變量儲存在page變量中從第一個字元開始搜尋"?"号出現的位置,"."代表将"?"号和字元串相連.
if (in_array($_page, $whitelist)) {
return true;
}
//也就是說,這個其實是實作了一個截斷的方法,因為我們下面會使用get的方法進行送出參數,但是将我們的"?"号和"?"号後面的内容給截斷了,是以就造成了我們無法送出參數的尴尬情況.想想看該怎麼辦.
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
//這也是進行了一個截斷,不過這個是進行一次url解碼以後的截斷.
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
//我們看到有include這個指令,是以我們就可以利用這個漏洞.
//我們觀察發現,腳本使用get的方式送出file變量的内容,然後進行檢查.假如我們能繞過這三個檢查,我們就能成功讀取到flag.
?>
2)解法分析
我們大概走了一遍這個流程,大概知道了怎麼運作的.是以,就需要更深入的研究了.
我們的目的是為了過三個if保護,代碼:
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
這三個條件是:
1)判斷file變量是否為空
2)判斷file變量是否為字元
3)用checkFile函數進行檢查
前兩個比較簡單,關鍵是最後一個.剛才分析的過程中,我們最主要過的保護其實的就是關于那個截斷的保護.
要知道,我們的目的就是為了使checkFile函數傳回ture才可以。四個判斷我們經過觀察發現,最後一個判斷是最容易過的保護。
是以,我們想想該如何繞過這個截斷?
其實很簡單,我們找一個和"?"作用像同的符号但是不是問号的符号不就行了麼?
是以,我們要學會一個最簡單的繞過方法–url編碼繞過
首先,當我們在百度搜尋一些東西的時候,我們會發現我們輸入的漢字都變成了一些由百分号開頭的奇怪編碼.
這些編碼就是經過浏覽器url編碼以後得到的結果.但是假如我們直接在網址欄輸入編碼後的代碼,浏覽器是不會進行url編碼的.
最後伺服器那頭解碼一次,就得到我們輸入的結果了.
是以,我們就可以利用這個原理來進行一個簡單的繞過.詳細來看下過程.
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
這個是進行url解碼以後進行的一個判斷.此處就是我們要利用的地方.
假如我們将編碼一次後的問号傳過來,伺服器解碼一次得到問号.
然後網站背景腳本會進行一次解碼,上一步出來的是問号,解碼出來依然是問号,仍舊能發現我們的操作.是以,我們需要編碼兩次.
第一次,伺服器将編碼解碼,得到問号編碼一次的結果.
第二次解碼是由背景腳本解碼的.仔細研究一下這段代碼的邏輯就明白了.
先解碼,解出來是問号,然後将問号後面的都截掉,放到白名單裡面一判斷,成功傳回truth,我們就成功了.
很好,此刻我們已經成功将三個判斷都繞過了,接下來執行的就是include指令,将我們需要的檔案包含進來就行了.這個用法和我前面說的c語言類似,就不贅述了.這個是關于這個漏洞的官方描述,被收錄在了cve上面,英語好的同學可以看看.
文章參考連結:https://www.jianshu.com/p/36eaa95068ca