BUUCTF 的第一題,上來就給搞懵了。。 。這要是我自己做出來的,豈不是相當於挖了一個 CVE ?(菜雞這樣安慰自己)
問題在 index.php 的 55~63 行
// If we have a valid target, let's load that script instead 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; }
對於傳遞的參數有 5 個條件,如果滿足就 include 包含參數
1.不為空
2.是字符串
3.不以 index 開頭
4.不能出現在:$target_blacklist 中
5.Core 類的 checkPageValidity 方法判斷
前三個基本忽略,第四個發現
/index.php //line 50-52 $target_blacklist = array ( 'import.php', 'export.php' );
也就是 target 不能是 import.php 和 export.php
最后是 Core 類 checkPageValidity 函數判斷
//443-478 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; }
函數里又是五個判斷:
1.$whitelist 為空則引用靜態聲明:$goto_whitelist
2.如果 $page 沒有被定義不是字符串則返回 false
3.如果 $page 存在 $whitepage 則返回 true
4.如果 $_page 存在 $whitelist 則返回 true($_page是取出$page問號前面的東西)
5.經過 url 解碼后 $_Page 存在 $whitelist 中則返回 true
當 index.php 調用 checkPageValidity 時並沒有傳 $whitelist 的值,所以會進入 self::$goto_whitelist;
public static $goto_whitelist = array( 'db_datadict.php', 'db_sql.php', 'db_events.php', 'db_export.php', 'db_importdocsql.php', 'db_multi_table_query.php', 'db_structure.php', ...... 'user_password.php', );
在 $goto_whitelist 中定義了可以被包含的文件名(很多,少些一部分)
第二個滿足,直接跳過,第三個:
$_page = mb_substr( $page, 0, mb_strpos($page . '?', '?') ); if (in_array($_page, $whitelist)) { return true; }
如果 $page 等於 $goto_whitelist 某個值,則 return true
這里考慮到 target 后面再跟參數的情況將 $_page 以?分割,然后去前面的字符再判斷是否存在於 $goto_whitelist 中
還考慮到 url 編碼的情況如果未成功下一步 url 解碼
傳入二次編碼的內容就會讓 checkPageValidity 后返回 true
比如傳入:?target=db_datadict.php%253f
服務器自動解碼一次是 ?target=db_datadict.php%3f
在經過 checkPageValidity 函數解碼變成:?target=db_datadict.php?
這便符合了 ? 前面內容在白名單里會返回 true,但是 index.php 中的 $_REQUEST['target'] 仍然是 db_datadict.php%3f,而且會被包含,通過目錄穿越就會造成任意文件包含漏洞