SQL注入漏洞
免责声明:本文仅用于网络安全学习与研究,所有漏洞测试必须在得到明确授权的情况下进行。未经授权的渗透测试行为是违法的,请遵守法律法规。
一、什么是SQL注入?一句话讲明白
SQL注入= 用户传的参数没经过严格检查,直接拼接到SQL语句里,导致坏人可以篡改SQL命令
打个比方:
• 正常情况:你去银行取钱,柜员问你取多少,你说"取1000",柜员照办
• SQL注入:坏人说"取1000,顺便把所有人的钱都转到我账户",柜员没检查,直接执行了
就这么简单!核心就是用户输入的东西被当成了SQL命令执行。
二、SQL注入有什么危害?
| 危害等级 | 具体危害 | 大白话解释 |
|---|---|---|
| ⭐⭐⭐ | 泄露数据库数据 | 把用户账号、密码、订单全偷了 |
| ⭐⭐⭐⭐ | 破坏数据库 | 删库跑路,数据全没了 |
| ⭐⭐⭐⭐⭐ | 控制服务器 | 直接拿下服务器,想干嘛干嘛 |
所以SQL注入被评为"Web漏洞之王"不是没有道理的!
三、怎么学习SQL注入?
学习SQL注入,按这个顺序来:
1.漏洞产生的原因(原理)—— 先搞懂为什么会有这个漏洞
2.危害—— 知道它能干什么
3.怎么挖掘—— 黑盒测试、白盒测试
4.怎么利用—— 拿到漏洞后怎么搞数据
5.怎么修复—— 开发怎么防
6.怎么绕过拦截—— 进阶技巧
四、怎么发现SQL注入漏洞?
方法一:代码审计(白盒测试)
白盒就是能看到源代码,找起来最准。
三步走:
| 步骤 | 做什么 | 通过条件 |
|---|---|---|
| 第1步 | 定位SQL语句,找有没有可控变量 | 有 |
| 第2步 | 看变量有没有被过滤 | 没有 |
| 第3步 | 看变量是不是从前端传来的 | 是 |
三个条件都满足 = 一定有SQL注入!
就这么简单,白盒测试找SQL注入就看这三点。
方法二:漏洞挖掘(黑盒测试)
黑盒就是看不到源码,只能靠猜和试。
两种方式:
1.扫描器扫—— 用工具扫所有参数(快但不准)
2.手工测—— 手动测试和数据库有交互的参数(慢但准)
怎么判断哪些参数可能有注入?
很简单:凡是跟数据查询有关的功能,都可能有注入!
• 成绩查询?有!
• 商品搜索?有!
• 用户登录?有!
• 订单查询?有!
只要是需要"查数据库"的地方,都有可能!
五、6种测试闭合的方法(重点!)
找到可疑参数后,怎么确认有没有SQL注入?用下面6种方法!
方法一⭐:引号测试闭合
原理:加个引号,看会不会报错。
正常: ?id=1 测试: ?id=1' (单引号) ?id=1" (双引号)判断方法:
• 加单引号报错,加双引号不报错 →单引号闭合,可能有注入!
• 加双引号报错,加单引号不报错 →双引号闭合,可能有注入!
举个例子:
假设后端SQL是这样的:
SELECT name, score FROM students WHERE student_id = '$student_id' limit 0,1;传id=1'后,SQL变成:
SELECT name, score FROM students WHERE student_id = '1'' limit 0,1;看到没?多了一个单引号,语法错了,就报错了!
为什么报错就可能有注入?
因为报错说明你的输入真的被拼到SQL里了!如果输入被过滤了,就不会报错。
方法二⭐:and测试
前提:你得知道一个正确的值
正常: ?id=20210101 (正常显示) 测试正常: ?id=20210101' and 1=1# (还是正常显示) 测试异常: ?id=20210101' and 1=2# (不显示了)原理:
•and 1=1两边都是真,所以SQL正常执行,页面正常显示
•and 1=2一边真一边假,所以SQL查不到数据,页面不显示
如果加不加and 1=1页面都一样,说明很可能有SQL注入!
#是MySQL的注释符,意思是"后面的内容都不算了"方法三⭐:or测试
适用场景:不知道正确值的时候用
测试: ?id=-1' or 1=1#原理:
•-1是个不存在的值,正常查不到
•or 1=1是永远成立的条件
• 加在一起就变成了"查id=-1的,或者1=1的",因为1=1永远成立,所以能查到所有数据
⚠ 慎用!or测试可能会查出整张表的数据,影响比较大,尽量用and测试。
方法四⭐:不加注释符闭合测试
有些情况注释符被过滤了怎么办?不用注释符也行!
正确值测试: ?id=1' and 1='1 错误值测试: ?id=-1' or 1='1原理:用单引号自己闭合,就不用注释符了。
假设原SQL是:
SELECT * FROM users WHERE id = '$id'传id=1' and 1='1后:
SELECT * FROM users WHERE id = '1' and 1='1'看到没?两边的单引号都对上了,完美闭合!
MySQL的注释符有两种:#和--(注意--后面有个空格)
方法五⭐:sleep测试(盲注用)
适用场景:页面不报错也不显示,啥都看不到的时候
测试: ?id=1' and sleep(5)#原理:
•sleep(5)意思是"睡5秒钟"
• 如果页面延迟了5秒才加载出来,说明有注入!
• 如果页面秒开,说明没有注入
这个方法很管用,因为不需要看页面内容,只需要看加载时间就行。
方法六⭐:order by测试
测试: ?id=1' order by 999#原理:
•order by 999意思是"按第999列排序"
• 一般的表哪有999列啊,所以肯定报错
• 一报错就说明闭合方式是对的,可能有注入!
六、SQL注入怎么利用?(联合查询注入)
确认有注入后,怎么拿数据?最经典的就是联合查询注入。
联合查询(union query)= 把两个SELECT语句的结果拼在一起,一起显示出来
打个比方:
• 正常查询:查学生成绩
• 注入查询:查学生成绩 + 查管理员密码
• 联合起来:两个结果一起显示在页面上
前置知识:information_schema
MySQL里有个很重要的数据库叫information_schema,它里面存了所有数据库的元信息。
| 表名 | 存了什么 | 作用 |
|---|---|---|
| schemata | 所有数据库名 | 查有哪些数据库 |
| tables | 所有表的信息 | 查某个库里有哪些表 |
| columns | 所有列的信息 | 查某个表里有哪些字段 |
这个库是SQL注入的"字典",拿数据全靠它!
七、联合查询注入四步走
第1步:判断查询结果的列数
用 order by 猜:
?id=-1' order by 999# (报错,说明没有999列) ?id=-1' order by 1# (正常,说明至少有1列) ?id=-1' order by 2# (正常,说明至少有2列) ?id=-1' order by 3# (报错,说明没有3列)结论:查询结果有2列
💡小技巧:用-1而不是正常值,是因为正常值会返回数据,把我们注入的结果挤掉。用-1(不存在的值),原查询查不到东西,就只会显示我们注入的内容。第2步:判断回显位
知道有几列后,用 union select 看哪一列会显示在页面上:
?id=-1' union select 1,2#如果页面显示了1和2,说明两列都会显示。
如果只显示了2,说明只有第2列是回显位。
回显位= 数据会显示在页面上的位置,我们把要查的数据放这里就行。
第3步:用函数获取基础信息
把回显位替换成MySQL函数,获取数据库的基本信息:
| 函数 | 作用 | 示例 |
|---|---|---|
version() | 获取MySQL版本 | 5.7.26 |
user() | 获取MySQL用户名 | root@localhost |
database() | 获取当前数据库名 | test_db |
@@basedir | 获取MySQL安装路径 | /usr/local/mysql |
示例:
?id=-1' union select version(),2#💡SRC挖洞提示:如果是挖SRC漏洞,到这一步就可以了!能证明有注入就行,不用往下拖库。
第4步:获取数据
重头戏来了!怎么把数据库里的数据全拖出来?
第一步:获取所有数据库名
?id=-1' union select schema_name,2 from information_schema.schemata#information_schema.schemata表里存了所有数据库的名字。
⚠ 常见问题:显示不完整怎么办?
有时候数据库太多,页面显示不全,两种解决方法:
解决方法1:group_concat 聚合函数
?id=-1' union select group_concat(schema_name),2 from information_schema.schemata#group_concat()的作用:把多行数据合并成一行,用逗号隔开。这样一整行就能显示所有数据库名了。
解决方法2:limit 分页
?id=-1' union select schema_name,2 from information_schema.schemata limit 0,1# (第1个) ?id=-1' union select schema_name,2 from information_schema.schemata limit 1,1# (第2个) ?id=-1' union select schema_name,2 from information_schema.schemata limit 2,1# (第3个)limit 2,1的意思:从第2行开始(不包括第2行),取1条结果。也就是取第3条。
注意:MySQL是从0开始数的,第1条是0,第2条是1,以此类推。
八、获取表名和字段名
拿到数据库名后,继续拿表名和字段名。
获取表名
?id=-1' union select group_concat(table_name),2 from information_schema.tables where table_schema='数据库名'#获取字段名
?id=-1' union select group_concat(column_name),2 from information_schema.columns where table_name='表名'#获取具体数据
?id=-1' union select username,password from 表名#九、Linux环境补充:文件上传与解压
课程里还提到怎么把Windows的文件传到Linux里并解压:
# 解压zip文件 unzip 文件名.zip十、总结速查表
SQL注入判断方法
| 方法 | 适用场景 | 原理 |
|---|---|---|
| 引号测试 | 快速判断 | 加引号看会不会报错 |
| and测试 | 有正确值 | and 1=1正常, and 1=2异常 |
| or测试 | 无正确值 | or 1=1 返回所有数据 |
| 无注释闭合 | 注释符被过滤 | 用引号自己闭合 |
| sleep测试 | 盲注(无回显) | 看页面延迟时间 |
| order by测试 | 通用 | 大数字排序会报错 |
联合查询步骤
| 步骤 | 做什么 | 示例 |
|---|---|---|
| 1 | 猜列数 | order by N |
| 2 | 找回显位 | union select 1,2,3 |
| 3 | 拿基础信息 | version()、user()、database() |
| 4 | 拿数据库名 | information_schema.schemata |
| 5 | 拿表名 | information_schema.tables |
| 6 | 拿字段名 | information_schema.columns |
| 7 | 拿数据 | select 字段 from 表 |
最后说几句
SQL注入的核心就一句话:用户输入的参数被直接拼到SQL里了。
学习建议:
1.先搞懂原理,别上来就背payload
2.多在靶场里练,比如sqli-labs
3.理解每一步为什么这么做,而不是死记硬背
4.挖到SRC漏洞点到为止,别拖库
记住:技术是把双刃剑,用在正途上是保护网络安全,用在歪路上就是违法犯罪!
学习路线提醒:这是SQL注入的第一篇,讲的是最基础的显错注入(联合查询)。后面还有报错注入、盲注、堆叠注入等等,一步一步来,先把基础打牢!
