Bootstrap

代码审计学习 : xhcms

前言

从大佬那里听说,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.phpsql 可能是漏洞提示。我们要绕过 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

;