SQL注入的一些注入

order by limit注入方法

  • 测试代码如下:
<?php 
header("Content-Type: text/plain; charset=utf-8");
mysql_connect("localhost","root","root");
mysql_select_db("test");
$row = mysql_fetch_array(mysql_query("SELECT * FROM users where uid < 100 ORDER BY uid limit {$_GET['p']}, 10"));
if($row){
    var_dump($row);
}else{
    echo mysql_error();
}
  • 在mysql中新建test库,新建users表,并插入数据
    在这里插入图片描述

  • order by limit注入方法适用于<=MySQL 5.5中,在limit语句后面的注入

SELECT * FROM table WHERE id > 0 ORDER BY id LIMIT injection_point
  • 上面的语句包含了ORDER BY,MySQL当中UNION语句不能在ORDER BY的后面,而注入点又在 limit后面,我们先看一下select语法
SELECT 
    [ALL | DISTINCT | DISTINCTROW ] 
      [HIGH_PRIORITY] 
      [STRAIGHT_JOIN] 
      [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT] 
      [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS] 
    select_expr [, select_expr ...] 
    [FROM table_references]
    [WHERE where_condition] 
    [GROUP BY {col_name | expr | position} 
      [ASC | DESC], ... [WITH ROLLUP]] 
    [HAVING where_condition] 
    [ORDER BY {col_name | expr | position} 
      [ASC | DESC], ...] 
    [LIMIT {[offset,] row_count | row_count OFFSET offset}] 
    [PROCEDURE procedure_name(argument_list)] 
    [INTO OUTFILE 'file_name' export_options 
      | INTO DUMPFILE 'file_name' 
      | INTO var_name [, var_name]] 
    [FOR UPDATE | LOCK IN SHARE MODE]]
  • 可以看到在select语法中,limit后面可以跟两个函数:procedureinto,先了解这两个函数是做什么的

procedure:存储过程;SELECT....PROCEDURE指定了一个procedure,用于处理结果集中的数据
存储过程是一组为了完成特定功能的 SQL 语句集合。使用存储过程的目的是将常用或复杂的工作预先用 SQL 语句写好并用一个指定名称存储起来,这个过程经编译和优化后存储在数据库服务器中,因此称为存储过程。
into:SELECT...INTO用来将查询结果存储在变量或者写入文件中。
SELECT…INTO var_list,将查询结果存储在变量中;
SELECT…INTO OUTFILE 将查询结果写入一个文件,还可以指定列和行终止符以生成特定的输出格式。
SELECT…INTO DUMPFILE 将单行数据写入文件,没有任何格式。

  • INTO除非有写入shell的权限,否则是无法利用的,那么使用PROCEDURE函数能否注入呢?先了解下列语句:
SELECT ... FROM ... WHERE ... PROCEDURE ANALYSE([max_elements,[max_memory]])
  • ANALYSE ()通过分析select查询结果对现有的表的每一列给出优化的建议,好像和我们注入没什么关系,在本地MySQL下进行尝试
SELECT * FROM users where uid > 0 ORDER BY uid LIMIT 1,1 PROCEDURE ANALYSE(1,1); 

在这里插入图片描述

  • 看起来好像不行,继续尝试
SELECT * FROM users where uid > 0 ORDER BY uid LIMIT 1,1 PROCEDURE ANALYSE((select IF(MID(version(),1,1) LIKE 5, sleep(5),1)),1); 

在这里插入图片描述

  • 最终发现可以通过报错注入获取数据
SELECT * FROM users where uid > 0 ORDER BY uid LIMIT 1,1 PROCEDURE ANALYSE(EXTRACTVALUE(1,CONCAT(0x3e,VERSION())),1); 

在这里插入图片描述

  • 报错注入获得用户:
    在这里插入图片描述

宽字节注入

  • 宽字节注入主要是源于程序员设置数据库编码与PHP编码设置为不同的两个编码格式从而导致产生宽字节注入。如果数据库使用的的是GBK编码而PHP编码为UTF8就可能出现注入问题,原因是程序员为了防止SQL注入,就会调用函数,将单引号或双引号进行转义操作,转义无非便是在单或双引号前加上斜杠(\)进行转义,但这样并非安全,因为数据库使用的是宽字节编码,两个连在一起的字符会被当做是一个汉字,而在PHP使用的UTF8编码则认为是两个独立的字符,如果我们在单或双引号前添加一个字符,使其和斜杠(\)组合被当作一个汉字,从而保留单或双引号,使其发挥应用的作用。
  • 搭建环境测试,注意先关闭自己php环境的magic_quotes_gpc()

magic_quotes_gpc函数在php中的作用是判断解析用户提示的数据,如包括有:post、get、cookie过来的数据增加转义字符“\”,对POST、GET以及进行数据库操作的sql进行转义处理,以确保这些数据不会引起程序,特别是数据库语句因为特殊字符引起的污染而出现致命的错误。防止sql注入
当magic_quotes_gpc = On时,输入数据中含单引号(’)、双引号(”)、反斜线(\)与 NULL(NULL 字符)等字符,都会被转义
注意:这个特性在PHP5.3.0中已经废弃并且在5.4.0中已经移除了,如果使用5.4之前的版本可以修改php.ini配置文件

<?php
//连接数据库部分,注意使用了gbk编码,把数据库信息填写进去
$conn = mysql_connect('localhost', 'root', 'root') 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 id='{$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>
</html>
  • 创建数据库和表
    在这里插入图片描述
  • 网页查看,在没有传入值的情况,id默认为1
    在这里插入图片描述
  • SQL语句是SELECT * FROM news WHERE tid='{$id}',就是根据文章的id把文章从news表中取出来。在这个sql语句前面,我们使用了一个addslashes函数,将$id的值转义。这是通常cms中对sql注入进行的操作,只要我们的输入参数在单引号中,就逃逸不出单引号的限制,无法注入。由于我设置的id类型为int型,1'在比较时转为数字变成了1,所以下图中依然查询到了结果
    在这里插入图片描述
  • 那么怎么逃过addslashes的限制?众所周知addslashes函数产生的效果就是,让'变成\',让引号变得不再是“单引号”,只是一撇而已。一般绕过方式就是,想办法处理\'前面的\

1.想办法给\前面再加一个\(或单数个即可),变成\\',这样\被转义了,'逃出了限制
2.想办法把\弄没有。

  • 宽字节注入是利用mysql的一个特性,mysql在使用GBK编码的时候,会认为两个字符是一个汉字(前一个ascii码要大于128,才到汉字的范围)。如果我们输入%df'看会怎样:
    在这里插入图片描述
  • 可以看到发生了报错,看出错说明可以注入了
  • 从报错信息可以看出我们输入的%df和不见了,这就是mysql的特性,因为gbk是多字节编码,他认为两个字节代表一个汉字,所以%df和后面的\也就是%5c变成了一个汉字“綅”,而'逃逸了出来。因为两个字节代表一个汉字,所以我们可以试试添加%df%df
    在这里插入图片描述
  • 不报错了。因为%df%df是一个汉字,%5c%27不是汉字,仍然是`';gbk编码如何判断一个字符是不是汉字,第一个字节ascii码大于128,基本上就可以了。比如我们不用%df,用%a1也可以:
    在这里插入图片描述
    -于是我可以构造一个语句,查询数据库名称和用户
    在这里插入图片描述

GB2312与GBK的不同

  • gb2312和gbk应该都是宽字节家族的一员,通过set names gb2312修改测试是否可以进行宽字节注入
    在这里插入图片描述
  • 结果注入失败
    在这里插入图片描述
  • gb2312编码的取值范围。它的高位范围是0xA1~0xF7,低位范围是0xA1~0xFE,而\是0x5c,是不在低位范围中的。所以,0x5c根本不是gb2312中的编码,所以自然也是不会被吃掉的。所以,把这个思路扩展到世界上所有多字节编码,我们可以这样认为:只要低位的范围中含有0x5c的编码,就可以进行宽字符注入。

mysql_real_escape_string

  • 在php官方文档中,mysql_real_escape_string会转义sql语句中特殊字符,并考虑当前字符集
    在这里插入图片描述
  • 所以有的cms把addslashes替换成mysql_real_escape_string来抵御宽字符注入,接下来,将代码中的 addslashes替换,进行测试
    在这里插入图片描述
  • 依然注入成功
    在这里插入图片描述
  • 为什么用了mysql_real_escape_string,但却仍然不能抵御宽字符注入,原因是没有指定php连接mysql的字符集。我们需要在执行sql语句之前调用一下mysql_set_charset函数,设置当前连接的字符集为gbk。
    在这里插入图片描述
  • 更改当前php连接mysql的字符集
    在这里插入图片描述
  • 测试,防御成功
    在这里插入图片描述

宽字符注入修复

  • 上面提到了使用mysql_set_charset函数设置连接字符集,在调用mysql_escape_string过滤用户输入来修复宽字符注入
  • 但如果源代码多处使用addslashes过滤,我们也不可能一个一个取修改。我们第二个解决方案是:将character_set_client设置为binary(二进制),只需要在所有sql语句中执行一下语句:
SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary
  • 为什么要这样做呢:当我们的mysql接收到客户端的数据后,会认为他的编码为charac_set_client,使用charac_set_client进行解码,然后通过character_set_connection编码,然后进入具体表和字段后,在转换为字段对应的编码,当查询结果产生后,会从表和字段的编码,转换成character_set_results编码,返回给客户端。
  • 所以,我们将character_set_client设置成binary,就不存在宽字节或多字节的问题了,所有数据以二进制的形式传递,就能有效避免宽字符注入
    在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值