• SQL注入的一些注入


    order by limit注入方法

    • 测试代码如下:
     
    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 在mysql中新建test库,新建users表,并插入数据
      在这里插入图片描述

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

    SELECT * FROM table WHERE id > 0 ORDER BY id LIMIT injection_point
    
    • 1
    • 上面的语句包含了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]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 可以看到在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]])
    
    • 1
    • ANALYSE ()通过分析select查询结果对现有的表的每一列给出优化的建议,好像和我们注入没什么关系,在本地MySQL下进行尝试
    SELECT * FROM users where uid > 0 ORDER BY uid LIMIT 1,1 PROCEDURE ANALYSE(1,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); 
    
    • 1

    在这里插入图片描述

    • 最终发现可以通过报错注入获取数据
    SELECT * FROM users where uid > 0 ORDER BY uid LIMIT 1,1 PROCEDURE ANALYSE(EXTRACTVALUE(1,CONCAT(0x3e,VERSION())),1); 
    
    • 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配置文件

    
    //连接数据库部分,注意使用了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 "

    {$row['title']}

    {$row['content']}

    \n"; mysql_free_result($result); ?> </body> </html>

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 创建数据库和表
      在这里插入图片描述
    • 网页查看,在没有传入值的情况,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
    
    • 1
    • 为什么要这样做呢:当我们的mysql接收到客户端的数据后,会认为他的编码为charac_set_client,使用charac_set_client进行解码,然后通过character_set_connection编码,然后进入具体表和字段后,在转换为字段对应的编码,当查询结果产生后,会从表和字段的编码,转换成character_set_results编码,返回给客户端。
    • 所以,我们将character_set_client设置成binary,就不存在宽字节或多字节的问题了,所有数据以二进制的形式传递,就能有效避免宽字符注入
      在这里插入图片描述

    在这里插入图片描述

  • 相关阅读:
    203、RabbitMQ 之 使用 direct 类型的 Exchange 实现 消息路由 (RoutingKey)
    StripedFly恶意软件:悄无声息运行5年,感染百万设备
    太硬核了 GitHub上堪称完美的神仙并发编程笔记,请收下我的下巴
    add_precompiled_header
    比特币 ZK 赏金系列:第 1 部分——支付解密密钥
    Python:函数使用
    88.(前端)商品分类TreeTable的显示——前端层级数据展示
    【综述】Transformers in Remote Sensing: A Survey
    Patroni for opengauss 9:basebackup
    【前端修炼场】 — 这些标签你学会了么?快速拿下 “hr”
  • 原文地址:https://blog.csdn.net/qq_57686163/article/details/126254839