问题:登录回跳 referer 未严格校验,存在开放重定向风险
此文章为XIUNOX版本重构审计时发现问题,分享出来方便后续想基于xiuno bbs4.0.4版本制作维护版本或插件模板等需求的开发者和站长参考。
现象
Xiuno BBS 4.0.4 的 user_http_referer() 函数用于登录后回跳,它优先从 param('referer')(GET/POST 参数)获取回跳地址,仅在为空时才回退到 HTTP_REFERER。虽然函数对回跳地址做了正则校验(要求 ^[\w\/]*$ 或 ^\??[\w\-/]+\.htm$),但该校验:
- 允许任意以字母数字下划线开头的相对路径,攻击者可构造
//evil.com/path 形式(双斜杠开头)绕过同站假设,浏览器会将其解析为协议相对 URL 跳转到外部域名
- 正则
^[\w\/]*$ 允许纯 / 与字母组合,配合 http_location() 直接 header('Location:'.$url) 输出,无 host 白名单
http_location() 函数本身对传入 URL 完全不校验,任意路由调用它都可被用于重定向。
源码证据
文件:xiunobbs_4.0.4/route/user.php 行 405-423(user_http_referer 函数)
function user_http_referer() {
$referer = param('referer');
empty($referer) AND $referer = array_value($_SERVER, 'HTTP_REFERER', '');
$referer = str_replace(array('\"', '"', '<', '>', ' ', '*', "\t", "\r", "\n"), '', $referer);
if(
!preg_match('#^(http|https)://[\w\-=/\.]+/[\w\-=.%\#?]*$#is', $referer)
&& !preg_match('#^\??[\w\-/]+\.htm$#', $referer2)
|| strpos($referer, 'user-login.htm') !== FALSE
|| strpos($referer, 'user-logout.htm') !== FALSE
|| strpos($referer, 'user-create.htm') !== FALSE
|| strpos($referer, 'user-setpw.htm') !== FALSE
|| strpos($referer, 'user-resetpw_complete.htm') !== FALSE
) {
$referer = './';
}
return $referer;
}
注意:第二个正则分支引用了未定义变量 $referer2(应为 $referer),导致该分支永远不匹配,校验逻辑存在 bug。
文件:xiunobbs_4.0.4/xiunophp/misc.func.php 行 1363-1366(http_location 无校验)
function http_location($url) {
header('Location:'.$url);
exit;
}
调用点 route/user.php 行 60、116:登录成功后 message(0, jump(..., $referer, 1)),jump 最终通过 http_location 输出 Location 头。
风险等级与结论
中危
危害后果:
- 钓鱼攻击:攻击者构造
user-login.htm?referer=//evil.com/phishing 链接诱骗用户登录,登录成功后跳转到钓鱼站点窃取二次凭据
- OAuth/SSO 回调劫持:若站点接入第三方登录,回跳地址可控可导致授权码被劫持
- 校验函数引用未定义变量
$referer2,第二正则分支永远失效,部分本应被拒的 URL 漏过
http_location 无任何 host 白名单,业务代码任意调用即形成开放重定向
修复建议:
- 修复
$referer2 未定义变量 bug
- 校验回跳 URL 必须以
/ 开头且不以 // 开头(防协议相对 URL)
- 解析 URL 后校验 host 必须等于当前站点 host
http_location 增加 host 白名单参数,拒绝外部域名