xhcms全漏洞代码审计记录

admin 2023年8月1日13:20:52评论27 views字数 8700阅读29分0秒阅读模式

点击蓝字 关注我们

xhcms全漏洞代码审计记录

免责声明

本文发布的工具和脚本,仅用作测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断。

如果任何单位或个人认为该项目的脚本可能涉嫌侵犯其权利,则应及时通知并提供身份证明,所有权证明,我们将在收到认证文件后删除相关内容。

文中所涉及的技术、思路及工具等相关知识仅供安全为目的的学习使用,任何人不得将其应用于非法用途及盈利等目的,间接使用文章中的任何工具、思路及技术,我方对于由此引起的法律后果概不负责。

添加星标不迷路

由于公众号推送规则改变,微信头条公众号信息会被折叠,为了避免错过公众号推送,请大家动动手指设置“星标”,设置之后就可以和从前一样收到推送啦

0x01 环境搭建

小皮面板安装步骤

不推荐用这个,因为后面有一个漏洞需要使用php5.2.17,小皮面板安装不上。

下载xhcms,解压要phpstudy的web路径下

https://www.lanzoux.com/izeFjfxbxah

在小皮面板中创建网站并添加数据库

xhcms全漏洞代码审计记录

访问www.xhcms.com/install

xhcms全漏洞代码审计记录

phpstudy2018安装步骤

下载xhcms,解压要phpstudy的web路径下https://www.lanzoux.com/izeFjfxbxah访问站点的install目录,我这里是http://127.0.0.1/www.xhcms.com/install/

xhcms全漏洞代码审计记录

xhcms全漏洞代码审计记录

0x02 代码审计

使用seay代码审计系统,新建项目,选择xhcms源码,选择自动审计,点击开始

xhcms全漏洞代码审计记录

xhcms全漏洞代码审计记录

2.1 文件包含

/index.php 和/admin/index.php

<?php
//单一入口模式
error_reporting(0); //关闭错误显示
$file=addslashes($_GET['r']); //接收文件名,接收get参数r的值,并添加转义
$action=$file==''?'index':$file; //判断为空或者等于index
include('files/'.$action.'.php'); //载入相应文件
?>

代码分析
第一行:“单一入口模式”,在页面中所有的页面都通过index.php来访问
第二行:error_reporting(0);表示关闭所有PHP错误报告。
addslashes() 函数返回在预定义字符(单·双引号、反斜杠()、NULL)之前添加反斜杠的字符串。
第四行、五行:通过三元运算符判断传入的file变量是否为空,为空则action变量的值为index,然后包含files/index.php文件。不为空则将file的值复制给action然后包含file/$action.php文件。

这个代码存在俩个问题

  1. 可以使用…/来实现路径穿越,但是后缀是代码中固定的只能读取.php文件

  2. 读取其他文件的办法:
    Windows的文件名字最后面加.是不影响文件的,Windows文件名的最大长度是260个字节,后面超出部分被丢弃。
    需要满足条件:php版本=5.2.17、Virtual Directory Support=enable

示例:

在网站的WWW下放一个文件111.txt,内容如下

<?php phpinfo();?>

xhcms全漏洞代码审计记录

00截断利用条件 //此处由于addslashes()函数导致不可用
1、magic_quotes_gpc =off
2、php版本小于5.3.4


payload:
1.?r=../111.txt........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
2.?r=../111.txt/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././.

xhcms全漏洞代码审计记录

2.2 SQL注入

有很多/admin目录下的,那些是需要登录的,才有权限访问,先审计前端

xhcms全漏洞代码审计记录

/files/content.php

/files/content.php有一个传入get型cid参数的SQL语句

xhcms全漏洞代码审计记录

代码分析:
第八行:get型接收一个cid的参数,使用addslashes进行转义,最后赋值给id变量
第九、十行: 将id拼接到查询语句中,使用mysql_query()执行语句,当语句执行失败,则抛出die()中的内容以及错误。

既然输出报错信息了,还存在注入,那这里肯定要尝试报错注入了。

访问这个URL,其中的cid存在注入http://127.0.0.1/www.xhcms.com/?r=content&cid=15

注:http://127.0.0.1/www.xhcms.com/?r=content&cid=15这个路径是怎么来的?
http://127.0.0.1/www.xhcms.com/其实是访问根目录的index.php文件
xhcms全漏洞代码审计记录

在index.php中通过r来传入一个文件名,然后去files目录下包含这个php文件,
http://127.0.0.1/www.xhcms.com/?r=content 其实就是访问
http://127.0.0.1/www.xhcms.com/files/content.php

正常访问如下图所示:

xhcms全漏洞代码审计记录

加一个单引号后报错。

xhcms全漏洞代码审计记录

payload如下:

xhcms全漏洞代码审计记录

/admin/files/adset.php

接着看/admin目录下的注入adset.php,其中有三个参数,但是都被addslashes进行转义,无法闭合单引号,是误报。

xhcms全漏洞代码审计记录

/admin/files/editcolumn.php

editcolumn.php代码的前面有一个id参数没有进行过滤,直接拼接到了SQL

xhcms全漏洞代码审计记录

payload如下:

[http://127.0.0.1/www.xhcms.com/admin/?r=editcolumn&type=1&id=1%27%20and%20(extractvalue(1,concat(0x5c,user(),0x5c)))%23](http://127.0.0.1/www.xhcms.com/admin/?r=editcolumn&type=1&id=1%27%20and%20(extractvalue(1,concat(0x5c,user(),0x5c)))%23)

xhcms全漏洞代码审计记录

editcolumn.php代码的后面,还使用POST方法接收了一些参数,但是没有进行过滤

xhcms全漏洞代码审计记录

访问这个路径,输入内容后抓包

http://127.0.0.1/www.xhcms.com/admin/?r=editcolumn&type=1&id=1

xhcms全漏洞代码审计记录

替换name参数为如下语句

1' and (extractvalue(1,concat(0x7e,(select USER()),0x7e))) and'

xhcms全漏洞代码审计记录

/admin/files/editlink.php

editlink.php与上面的代码类似,id处存在注入

http://127.0.0.1/www.xhcms.com/admin/?r=editlink&type=1&id=1%27%20and%20updatexml(1,concat(0x7b,database(),0x7d),1)%20and%20%27

xhcms全漏洞代码审计记录

同样,访问链接,保存表单后对name参数进行注入,注入payload使用editcolumn.php部分的代码即可

[http://127.0.0.1/www.xhcms.com/admin/?r=editlink&type=1&id=1](http://127.0.0.1/www.xhcms.com/admin/?r=editlink&type=1&id=1)

基本上扫描器后面扫出来的SQL注入都存在这个问题,都是类似的,不在进行列举了。

2.3 任意用户登录

2.3.1登陆页面sql注入

/admin/files/login.php登录页面进行审计在代码的第10行,可以看到存在注入,直接拼接了user变量,并且user变量也没有进行过滤,但是seay没扫出来。所以在测试的过程中可以全局搜索。

xhcms全漏洞代码审计记录

2.3.2任意用户登录

login.php代码中首先接收四个POST参数:loginuserpasswordcheckboxuserpassword肯定是登录时输入的内容,login是登录按钮,checkbox不知道是什么,我们可以在登陆页面进行访问抓包看一下,发现理解的没问题,checkbox是记住的功能

xhcms全漏洞代码审计记录

接着我们读一下代码逻辑

代码解析
整个代码先判断login参数是否有值,无值不执行代码。
第二、三、四行SQL语句拼接登陆页面的用户名参数,执行sql语句,语句错误时抛出错误信息,将查询结果转换为数组传入users变量,
第6-14行mysql_num_rows($result)返回result中的行数,当result为0行,则执行第七行代码,输出错误信息,并中断后续代码执行。当result不为0行,则获取到用户的密码复制给passowrds,然后将用户输入的password进行md5加密与数据库中存储的密码passwords进行比较,如果不相等,则输出错误信息,然后退出,不执行后面的语句。
第16-20行:判断checkbox是否为1,是设置cookiecookie的键为user值为$usercookie的过期时间30天。不是1则设置cookie的过期时间为0。
第21行:执行script跳转到/admin/files/index.php页面。

if ($login<>""){
$query = "SELECT * FROM manage WHERE user='$user'";
$result = mysql_query($query) or die('SQL语句有误:'.mysql_error());
$users = mysql_fetch_array($result);

if (!mysql_num_rows($result)) {
echo "<Script language=JavaScript>alert('抱歉,用户名或者密码错误。');history.back();</Script>";
exit;
}else{
$passwords=$users['password'];
if(md5($password)<>$passwords){
echo "<Script language=JavaScript>alert('抱歉,用户名或者密码错误。');history.back();</Script>";
exit;
}
//写入登录信息并记住30天
if ($checkbox==1){
setcookie('user',$user,time()+3600*24*30,'/');
}else{
setcookie('user',$user,0,'/');
}
echo "<script>this.location='?r=index'</script>";
exit;
}
exit;
ob_end_flush();
}

继续跟进登录成功后的页面admin/files/index.php,首先包含了一个/inc/checklogin.php

xhcms全漏洞代码审计记录

/inc/checklogin.php中,判断了cookieuser的值是否为空,为空跳转回login页面。

xhcms全漏洞代码审计记录

所以当我们直接访问/admin/files/index.php时,只要设置了COOKIE就能登录

xhcms全漏洞代码审计记录

3.1 XSS

3.1.1 后台存储型XSS

/admin/files/adset.php文件中,有一个UPDATE将前端的数据写入到adword表中,这个地方在写入的时候没有进行过滤,如果在前端页面调用我们写入的值,有可能存在XSS,所以我们去全局搜索adword

xhcms全漏洞代码审计记录

双击文件路径进去看一下

xhcms全漏洞代码审计记录

直接将查询结果输出到页面,存在存储型XSS

xhcms全漏洞代码审计记录

登录后台----设置----广告设置,三个广告都可以

xhcms全漏洞代码审计记录

在前台访问/files/content.php即可触发。/files/software.php/template/sidebar.php同理

xhcms全漏洞代码审计记录

3.1.2 反射型XSS

/files/contact.php文件下,有一个page变量,在html中直接输出,没有进行过滤

xhcms全漏洞代码审计记录

payload如下:

http://127.0.0.1/www.xhcms.com/?r=contact&page=%3Cscript%3Ealert(1)%3C/script%3E

xhcms全漏洞代码审计记录

4.1 CSRF

在代码审计中没有必要去关注csrf,原理主要是观察一些危险的功能(删除、修改、增加)是否存在token或者其他校验。
在重点功能处抓包测试更加方便

在代码中/admin/files/linklist.php中,在第七行接收到delete参数后,在执行sql语句前,并没有进行token验证,也没有过滤。

xhcms全漏洞代码审计记录

进入后台,点击友情连接,点击删除按钮抓包

xhcms全漏洞代码审计记录

重放数据包即可

xhcms全漏洞代码审计记录

5.1 文件上传

不存在文件上传漏洞,只是把扫描器误报的文件上传漏洞判断一下

首先说一下php中文件上传的思路:
客户端请求上传后,服务端接收文件,存储为临时文件,转换正式文件。

$_FILES:PHP中的全局变量,用来接收上传的文件内容,是一个二维数组。
$_FILES['uploadFile']['name'] 客户端文件的原名称。
$_FILES['uploadFile']['type'] 文件的 MIME 类型,例如"image/gif"。
$_FILES['uploadFile']['size'] 已上传文件的大小,单位为字节。
$_FILES['uploadFile']['tmp_name'] 文件被上传后在服务端储存的临时文件名,一般是系统默认。可以在php.ini的upload_tmp_dir指定。
$_FILES['uploadFile']['error'] 该文件上传相关的错误代码。

move_uploaded_file()移动文件,重复则覆盖


扫描出来的疑似的文件上传,代码可以看出来是一个类,后面写了构造方法,以及文件上传的方法,说明肯定有文件调用这个类,直接全局搜索Uploader

xhcms全漏洞代码审计记录

发现俩个文件包含了类并且new了一个对象,进去文件看一下

xhcms全漏洞代码审计记录

简单读一下代码逻辑,接收get参数actionswitch判断action的值是那种类型的文件,分别进入不同的case,在case中定义config列表,列表键值对中的值通过$CONFIG来获取(稍后看一下CONFIG是什么),还定义了fieldName。最后new了一个Uploader对象,将参数传入其中,最后返回UploadergetFileInfo方法的值。

白话:判断用户上传的是图片、媒体还是文件,有不同的处理方法,处理方法中调用了一个全局$CONFIG

include "Uploader.class.php";

/* 上传配置 */
$base64 = "upload";
switch (htmlspecialchars($_GET['action'])) {
case 'uploadimage':
$config = array(
"pathFormat" => $CONFIG['imagePathFormat'],
"maxSize" => $CONFIG['imageMaxSize'],
"allowFiles" => $CONFIG['imageAllowFiles']
);
$fieldName = $CONFIG['imageFieldName'];
break;
case 'uploadscrawl':
$config = array(
"pathFormat" => $CONFIG['scrawlPathFormat'],
"maxSize" => $CONFIG['scrawlMaxSize'],
"allowFiles" => $CONFIG['scrawlAllowFiles'],
"oriName" => "scrawl.png"
);
$fieldName = $CONFIG['scrawlFieldName'];
$base64 = "base64";
break;
case 'uploadvideo':
$config = array(
"pathFormat" => $CONFIG['videoPathFormat'],
"maxSize" => $CONFIG['videoMaxSize'],
"allowFiles" => $CONFIG['videoAllowFiles']
);
$fieldName = $CONFIG['videoFieldName'];
break;
case 'uploadfile':
default:
$config = array(
"pathFormat" => $CONFIG['filePathFormat'],
"maxSize" => $CONFIG['fileMaxSize'],
"allowFiles" => $CONFIG['fileAllowFiles']
);
$fieldName = $CONFIG['fileFieldName'];
break;
}


$up = new Uploader($fieldName, $config, $base64);
return json_encode($up->getFileInfo());

全局搜索config,发现config变量在controller.php中定义,内容为:读取config.json文件的内容,使用正则表达式替换指定内容为空,然后使用json_decode解码。

xhcms全漏洞代码审计记录

现在得知,获取到文件后会交给Uploader.class.php处理,所以审计一下这个文件判断文件上传主要是判断有没有白名单和过滤,在uploader.class.php中看到,使用了checkType()函数来判断

xhcms全漏洞代码审计记录

checkType()的位置也在uploader.class.php中,函数内容是从config["allowFiles"]中寻找是否存在getFileExt()config["allowFiles"]在上面跟踪到最后是config.json文件

xhcms全漏洞代码审计记录

getFileExt()也在uploader.class.php中,跟进getFileExt()发现是获取文件扩展名

xhcms全漏洞代码审计记录

所以现在的逻辑是这样的,在uploader.class.php中,获取到用户上传文件的扩展名去config.json中查找是否存在这个扩展名。config.json位置以及内容如下,发现其中设置了文件的上传格式,图片的上传格式,音频的上传格式等等。

xhcms全漏洞代码审计记录

对扩展名判断的流程图调用如下:其实就是是从config.json中获取用户上传的扩展名,然后对比config.jsonallowFiles键的值是否存在这个扩展名,allowFiles是写的白名单

xhcms全漏洞代码审计记录


欢迎关注SecHub网络安全社区,SecHub网络安全社区目前邀请式注册,邀请码获取见公众号菜单【邀请码】

#


企业简介   


赛克艾威 - 专注政企安全服务


       北京赛克艾威科技有限公司(简称:赛克艾威),成立于2016年9月,提供全面的安全解决方案和专业的技术服务,帮助客户保护数字资产和网络环境的安全。


安全评估|渗透测试|漏洞扫描|安全巡检

代码审计|钓鱼演练|应急响应|安全运维

重大时刻安保|企业安全培训

xhcms全漏洞代码审计记录

联系方式

电话|010-86460828 

官网|http://www.secevery.com

xhcms全漏洞代码审计记录

关注我们

xhcms全漏洞代码审计记录
xhcms全漏洞代码审计记录
xhcms全漏洞代码审计记录

公众号:sechub安全

哔哩号:SecHub官方账号


原文始发于微信公众号(SecHub网络安全社区):xhcms全漏洞代码审计记录

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年8月1日13:20:52
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   xhcms全漏洞代码审计记录http://cn-sec.com/archives/1925109.html

发表评论

匿名网友 填写信息