本文僅供安全研究、程式碼審計、防禦加固參考。嚴禁用於攻擊實際站點。所有漏洞已在 Discuz! 後續版本修復,請務必升級至最新版。
Discuz! 早期版本 (2.x, 3.x) 多處直接輸出用戶可控資料 (標題、用戶名、投票選項) 至 HTML/JS 語境,未經 htmlspecialchars() 或白名單過濾,導致 反射型/儲存型 XSS。
# URL 範例
/post.php?action=newthread&fid=2&subject=
# 程式碼流程 (簡化)
$subject = $_GET['subject']; // 或 $_POST['subject']
// 缺乏過濾直接入庫
// 顯示時:
echo "$subject
"; // 直接輸出,腳本執行
利用:惡意標題儲存後,所有瀏覽版面、主題列表、RSS 的用戶皆中招 (儲存型 XSS)。
# URL 範例
/pm.php?action=send&username=victim">";
// 導致屬性逃逸,執行腳本
進階利用鏈:
HttpOnly 繞過 (早期瀏覽器不支援)、MD5 碰撞/暴力破解密碼。# 正常投票
/misc.php?action=votepoll&fid=2&tid=16980&pollanswers[]=0
# 異常參數
/misc.php?action=votepoll&fid=2&tid=16980&pollanswers[]=999
# n > 最大選項數,仍提交成功,新增空標題選項
# 可能導致邏輯錯誤、統計異常、陣列越界 (舊版 PHP)
# 1. 攻擊者在論壇發帖,標題包含:
# 2. 管理員/用戶瀏覽版面 → 腳本執行 → Cookie 發送至 evil.com
# 3. 攻擊者拿到 Cookie,在瀏覽器設定 Cookie:discuz_auth=xxx; discuz_uid=1
# 4. 直接訪問論壇,以管理員身分操作 (若無 HttpOnly、IP 綁定、UA 檢查)
# 5. 進一步:上傳 Webshell、修改模板、植入持久後門
<?php
// source/function/function_core.php
function dhtmlspecialchars($string) {
if (is_array($string)) {
foreach ($string as $key => $val) {
$string[$key] = dhtmlspecialchars($val);
}
} else {
$string = str_replace(['&', '"', "'", '<', '>'],
['&', '"', ''', '<', '>'], $string);
// 或 PHP 5.4+ 直接用 htmlspecialchars($string, ENT_QUOTES, 'UTF-8')
}
return $string;
}
// 模板/顯示層統一呼叫
echo "" . dhtmlspecialchars($thread['subject']) . "
";
echo "";
{$var|escape:'htmlall'} 或設定 $smarty->escape_html = true。{{ $var }} 自動轉義,需原始輸出用 {!! $var !!} / {% autoescape false %}。{$var} 應在編譯期自動注入 dhtmlspecialchars。<?php
// 發帖/回帖儲存前 (或輸出前)
require_once 'HTMLPurifier.auto.php';
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.Allowed', 'p,b,strong,i,em,u,a[href|title],img[src|alt],br,ul,ol,li,blockquote');
$purifier = new HTMLPurifier($config);
$clean_content = $purifier->purify($raw_content);
// 入口檔案 (index.php, admin.php) 最早處
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https:; connect-src 'self'; frame-ancestors 'self'; form-action 'self'; base-uri 'self';");
header("X-Content-Type-Options: nosniff");
header("X-Frame-Options: SAMEORIGIN");
header("Referrer-Policy: strict-origin-when-cross-origin");
// Cookie 設定
// setcookie('discuz_auth', $value, ['httponly' => true, 'secure' => true, 'samesite' => 'Lax']);
<?php
// misc.php votepoll
$pollanswers = $_GET['pollanswers'] ?? [];
$max_option = $poll['options']; // 從資料庫讀取選項數
foreach ($pollanswers as $aid) {
$aid = intval($aid);
if ($aid < 0 || $aid >= $max_option) {
// 記錄異常、拒絕處理、回傳錯誤
showmessage('vote_invalid_option');
}
// 正常計票邏輯
}
| 項目 | 檢查點 | 工具/方法 |
|---|---|---|
| 所有輸出 | 是否經過 htmlspecialchars/模板自動轉義 | grep -r "echo \$" --include="*.php" | grep -v dhtmlspecialchars |
| HTML 屬性 | 單引號包裹 + 轉義單引號 | 統一雙引號屬性 + ENT_QUOTES |
| JavaScript 內嵌 | json_encode() 輸出變數 | var x = = json_encode($val) ?>; |
| URL 參數 | urlencode() | <a href="?q== urlencode($kw) ?>"> |
| 富文本 | HTMLPurifier 白名單 | 單元測試驗證 XSS Payload 全部失效 |
| Cookie | HttpOnly, Secure, SameSite | 瀏覽器開發者工具 Application 面板檢查 |
| CSP | 部署報告模式 → 正式模式 | Report-URI / Sentry 收集違規報告 |
Discuz! 早期 XSS 漏洞本質是「輸出端未編碼」。修復核心:統一輸出函數、模板自動轉義、富文本白名單、CSP、Cookie 安全屬性。現代框架 (Laravel, Symfony, ThinkPHP 6+) 預設解決 90% XSS,遺留專案請優先引入統一輸出函數並全站替換,再配合 CSP 部署。安全無絕對,持續審計、升級、監控才是正道。