php代码审计3审计sql注入漏洞

SQL注入攻击(sql injection)被广泛用于非法获取网站控制权,在设计程序时,忽略或过度任性用户的输入,从而使数据库受到攻击,可能导致数据被窃取,更改,删除以及导致服务器被嵌入后门程序等


sql注入的条件
用户输入数据可控,源程序要执行代码,拼接了用户输入的数据

注入两大类型(不同数据库的比较方式不同)

数字型注入:当输入的参数为整数时,可认为是数字型注入(如 id 年龄,页码等都可以认为是数字型注入,并且不需要单引号来闭合)
$result = "select * freo admin where id=$id";


字符型注入:当输入的参数为字符串时。称为字符型,需要单引号来闭合
$result = "select * from admin where id='$id'";

<?php
$conn = mysql_connect('localhost', 'root', 'root') or die('bad!');
mysql_query("SET NAMES binary'");
mysql_select_db('test', $conn) OR emMsg("数据库连接失败");
//这里id没有做整形转换
$id = isset($_GET['id']) ? $_GET['id'] : 1;//sql语句没有做过滤,造成注入
$sql = "SELECT * FROM news WHERE id={$id}";
$result = mysql_query($sql, $conn) or die(mysql_error());
?>

注入的利用方式
查询数据
读写文件
执行命令

注入过程
攻击者访问页面
将包含有攻击字符组成的sql语句转发给数据库执行
数据库执行发回应用程序服务器
应用程序将内容返回浏览器,即成功攻击


盲注:在sql注入过程中,sql语句执行的选择后,选择的数据不在页面显示
基于布尔sql盲注
基于时间sql盲注
基于报错sql盲注

修复方案
使用预编译语句
使用存储过程
检查数据类型
使用安全函数过滤



宽字节注入及二次注入

宽字节注入:字符(charater)是组成字符集(character set)的基本单位,对字符赋予一个数值(encoding)来确定这个字符在该字符集中的位置,字符序(collation)指同一字符集内字符间的比较规则,由于ascll表示的字符只有128个,因此规范使用Unicode编码,但是使用ascll表示的字符使用Unicode并不高效,所以出现了中间格式字符集,被称为通用转换格式及UTF。GB2312 GBK GB118030 BIG5 Shift JIS等这些都是常说的宽字节,实际只有两字节,宽字节带来的安全问题主要是ascll字符(一字节)的现象

<?php
//连接数据库部分,注意使用了gbk编码,把数据库信息填写进去
$conn = mysql_connect('localhost', 'root', 'toor!@#$') or die('bad!');
mysql_query("SET NAMES 'gbk'");
mysql_select_db('test', $conn) OR emMsg("连接数据库失败,未找到您填写的数据库");
//执行sql语句
$id = isset($_GET['id']) ? addslashes($_GET['id']) : 1;
$sql = "SELECT * FROM news WHERE tid='{$id}'";
$result = mysql_query($sql, $conn) or die(mysql_error()); //sql出错会报错,方便观察
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="gbk" />
<title>新闻</title>
</head>
<body>
<?php
$row = mysql_fetch_array($result, MYSQL_ASSOC);
echo "<h2>{$row['title']}</h2><p>{$row['content']}<p>\n";
mysql_free_result($result);
?>
</body>
2</html>

SQL语句是SELECT * FROM news WHERE tid='{$id}',就是根据文章的id把文章从news表中取出来,在这个sql语句前面,使用了一个addslashes函数,将$id的值转义。这是通常cms中对sql注入进行的操作,只要我们的输入参数在单引号中,就逃逸不出单引号的限制,无法注入,addslashes函数产生的效果就是,让’变成\’,让引号变得不再是“单引号”,只是一撇而已。一般绕过方式就是,想办法处理\’前面的\,我们这里的宽字节注入是利用mysql的一个特性,mysql在使用GBK编码的时候,会认为两个字符是一个汉字(前一个ascii码要大于128,才到汉字的范围)。如果我们输入%df’会看到报错,说明sql语句出错,看到出错说明可以注入了,为什么在’也就是%27前面加了一个%df就报错了?而且可以看到,报错的原因就是多了一个单引号,而单引号前面的反斜杠不见了,这就是mysql的特性,因为gbk是多字节编码,他认为两个字节代表一个汉字,所以%df和后面的\也就是%5c变成了一个汉字“運”,而’逃逸了出来。

原理:%df%27---->(addslashes)--->%df%5c%27------>(GBK)====運'用户输入---->g过滤函数--->代码层的$sql--->mysql处理请求--->mysql中的sql
默认编码character_set_client-->根据character_set_connection转换-->更新数据库是转换成字段对应的编码。

修复方案
使用mysql_set_charset(GBK)指定字符集
使用mysql_real_escape_string进行转义



二次注入
注入点因为经过过滤处理无法触发sqlsql注入漏洞,比如addslashes函数,将单引号等字符转义变成\’,但是存进数据库后数据又被还原,在这种情况下,如果发现一个新的注入同时引用了被插入的数据库数据时,就可以实现闭合新发现的注入漏洞引发二次注入,

利用思路:
现将注入语句插入到数据库,注册,留言等功能具有insert数据库的操作,然后在使用update的地方触发插入到数据库的注入语句。

一阶sql注入:发生在一个http请求和响应中,对系统的攻击是立即执行的,攻击者在http请求中提交非法输入,应用程序处理非法输入,使用非法输入构造sql语句,在攻击过程中向攻击者返回结果

二阶sql注入:攻击者在http请求中提交恶意输入,恶意输入保存在数据库中,攻击者提交第二次http请求,为处理第二次htt请求,程序在检索存储在数据库中的恶意输入,构造sql语句,如果攻击成功,在第二次请求响应中返回结果


代码1
<?php
    include('./connect.php'); //引入数据库配置文件
    $id=$_GET['id'];
    $select_sql="SELECT * FROM article WHERE title='$id'";
    echo $select_sql;
    mysql_query('set names utf8');
    $select_sql_result=mysql_query($select_sql);
    $date=mysql_fetch_assoc($select_sql_result);
 ?>
 <!DOCTYPE html>
 <html>
   <head>
     <meta charset="utf-8">
     <title><?php echo $date['title']."啦啦啦啦啦啦"?></title>
   </head>
   <body>
      <h1><?php echo $date['title'] ?></h1><br />
      作者:<?php echo $date['author'] ?><br/>
      时间:<?php echo date("Y-m-d H:i:s",$date['dateline'])?><br />
      概述: <?php echo $date['description']; ?><br />
      正文: <?php echo $date['content'] ?>
   </body>
 </html>

代码2
<?php
   include('../connect.php');
   $title=addslashes($_POST['title']);  //addslashes 将预定义字符串转义
   $author=addslashes($_POST['author']);
   $description=addslashes($_POST['description']);
   $content=addslashes($_POST['content']);
   $dataline=time();
   $insert="INSERT INTO article(title,author,description,content,dateline) VALUES('$title','$author','$description','$content','$dataline')";
   echo $insert;
   mysql_query("set names utf8"); //设置编码
   if($result=mysql_query($insert)){
     $num=mysql_affected_rows($con);
     echo $num;
     }
 ?>

代码1是一个内容显示页面,通过传入的title在数据库进行查询,然后在页面调用输出,可以看到传递的参数id并没有经过过滤,可以成为一个典型的字符串GET注入,但是我们今天要讨论的是二次注入,暂时不考虑这个注入。代码2是一个添加页面,通过表单POST的数据执行INSERT语句插入数据,成功后返回数据库影响行数,而且这里的每一个参数都用addslashes函数进行了转义。两段代码结合,是一个典型的二次注入,虽然文章添加页面中过滤的非常严格,但是addslashes有一个特点就是虽然参数在过滤后会添加 “\” 进行转义,但是“\”并不会插入到数据库中,再配合内容显示页面中的查询是通过title查询的,所以可以利用这个构造一个二次注入。

posted @ 2019-01-26 00:58  Hydraxx  阅读(649)  评论(0编辑  收藏  举报