前言
从大佬那里听说,xhcms
很适合代码审计的新手。
环境
php 5.4.45 + Apache 2.4.39 + Mysql 5.7.26
文件上传配合文件包含
/index.php
和 /admin/index.php
error_reporting(0); //关闭错误显示
$file=addslashes($_GET['r']); //接收文件名
$action=$file==''?'index':$file; //判断为空或者等于index
include('files/'.$action.'.php'); //载入相应文件
虽然参数用 addslashes
处理了一下,但是 addslashes
只会转义 ' " \ null
,也就是说我们是可以目录穿越的,那么我们是不是可以找一个可以上传文件或者图片马的地方,利用 /index.php
目录穿越去包含它,从而达到任意代码执行,那么要怎么去掉最后面的 .php
呢?
暂时想到两种方法:
1、00截断,但是这种有php版本的限制,且会被 addslashes 转义。
2、利用 windows 解析文件名的特性和文件路径长度限制。
(windows 下 index.php. 后面的点会被忽略且最长解析长度为260个字符,如果文件路径超过最大长度,将导致使用问题。)
也就是说可以:
/index.php?r=shell.png................260
/index.php?r=shell.png/./././././.....260
但是这种也有使用限制,我们看看微软的官方说明:
最大路径长度限制
在版本 1607 之前Windows版本中Windows 10,路径的最大长度为 MAX _ PATH, 定义为 260 个字符。 在更高版本的 Windows中,更改注册表项或组策略工具才能删除限制。 有关 完整详细信息,请参阅最大路径 长度限制。
也就是说 1607 windows 10
之后的版本 要删除 MAX _ PATH 限制,显然这种情况基本不存在。
垂直越权
/inc/checklogin.php
<?php
$user=$_COOKIE['user'];
if ($user==""){
header("Location: ?r=login");
exit;
}
?>
判断的很简单粗暴,直接传一个 cookie:user=admin
就可以登录后台了。
SQL注入
/admin/files/login.php
$login=$_POST['login'];
$user=$_POST['user'];
$password=$_POST['password'];
$checkbox=$_POST['checkbox'];
if ($login<>""){
$query = "SELECT * FROM manage WHERE user='$user'";
$result = mysql_query($query) or die('SQL语句有误:'.mysql_error());
没有过滤直接把 user
值代入查询判断,也没有屏蔽报错,可以报错注入。
利用:
' and updatexml(2,concat(0x7e,(version())),0)--+
在 seay
中有 /admin/files/adset.php
的 sql
可能是漏洞提示。我们要绕过 addslashes
函数,但是里面没有其他对参数的解码函数,且我设置的数据库字符集是 utf8
而不是 gbk
所以也就无法宽字节注入,当然二次注入就更谈不上了,所以无法绕过了。
$ad1=addslashes($_POST['ad1']);
$ad2=addslashes($_POST['ad2']);
$ad3=addslashes($_POST['ad3']);
/admin/files/editcolumn.php
$id=$_GET['id'];
$type=$_GET['type'];
if ($type==1){
$query = "SELECT * FROM nav WHERE id='$id'";
$resul = mysql_query($query) or die('SQL语句有误:'.mysql_error());
$nav = mysql_fetch_array($resul);
}
if ($type==2){
$query = "SELECT * FROM navclass WHERE id='$id'";
$resul = mysql_query($query) or die('SQL语句有误:'.mysql_error());
$nav = mysql_fetch_array($resul);
}
$query = "UPDATE nav SET
name='$name',
keywords='$keywords',
description='$description',
xs='$xs',
px='$px',
content='$content',
date=now()
WHERE id='$id'";
@mysql_query($query) or die('修改错误:'.mysql_error());
可以看到漏洞和上面一样,只是多了一个 update
注入,当然直接访问这个页面是肯定不行的,因为它设定了一个入口,也就是 /admin/index.php
。
/admin/index.php?r=editcolumn&type=1&id=1'and updatexml(2,concat(0x7e,(version())),0)--+
或者利用 update 注入
其他
/admin/files/editlink.php
/admin/files/editsoft.php
/admin/files/editwz.php
/admin/files/imageset.php
/admin/files/manageinfo.php
/admin/files/newlink.php
/admin/files/reply.php
/admin/files/seniorset.php
/admin/files/siteset.php
/files/content.php
中虽然前面用 addslashes
转义了,但是后面没有用引号包裹起来。
$id=addslashes($_GET['cid']);
$query = "SELECT * FROM content WHERE id='$id'";
$resul = mysql_query($query) or die('SQL语句有误:'.mysql_error());
$content = mysql_fetch_array($resul);
$navid=$content['navclass'];
$query = "SELECT * FROM navclass WHERE id='$navid'";
$resul = mysql_query($query) or die('SQL语句有误:'.mysql_error());
$navs = mysql_fetch_array($resul);
//浏览计数
$query = "UPDATE content SET hit = hit+1 WHERE id=$id";//没有引号包裹
@mysql_query($query) or die('修改错误:'.mysql_error());
总之就是sql
语句基本没有过滤,学学审计思路还是可以的。
XSS
/admin/files/adset.php
$query = "SELECT * FROM adword";
$resul = mysql_query($query) or die('SQL语句有误:'.mysql_error());
$ad = mysql_fetch_array($resul);
$save=$_POST['save'];
$ad1=addslashes($_POST['ad1']);
$ad2=addslashes($_POST['ad2']);
$ad3=addslashes($_POST['ad3']);
if ($save==1){
$query = "UPDATE adword SET
ad1='$ad1',
ad2='$ad2',
ad3='$ad3',
date=now()";
@mysql_query($query) or die('修改错误:'.mysql_error());
..........
<textarea name="ad1" class="form-control col-lg-12" placeholder="ad-1"><?php echo $ad['ad1']?></textarea>
上面的代码会产生存储型 xss
,不是有 addslashes
转义吗?非也,它是转义了,但是在写入数据的时候会自动去掉转义符号,那么等再读取这个数据的时候不就是转义前的了吗,也就是说addslashes
只是帮助mysql完成了sql语句的执行,并未将转义符写入。
</textarea><script>alert('hahhahahaa')</script>
那么也就是说所有存在类似操作的文件都存在 xss。
CSRF
/admin/files/wzlist.php
<?php
require '../inc/checklogin.php';
require '../inc/conn.php';
$wzlistopen='class="open"';
$pageyema="?r=wzlist&page=";
$delete=$_GET['delete'];
if ($delete<>""){
$query = "DELETE FROM content WHERE id='$delete'";
$result = mysql_query($query) or die('SQL语句有误:'.mysql_error());
echo "<script>alert('亲,ID为".$delete."的内容已经成功删除!');location.href='?r=wzlist'</script>";
exit;
}
?>
可以看到没有任何验证,可以随便删除。
reference
https://xz.aliyun.com/t/11310