type
status
date
slug
summary
tags
category
icon
password
想了一下,原本博客的记录方式更像是知识介绍,就是整合各个网站的内容,反而和学习这一初衷相违背,所以重新以边学边记的方式记录,自己也就当从0开始学习。
数据库识别
在进行SQL注入前,你必须要知道数据库的类型和版本,再查询对应的语法。
假设注入点是
id=1,则有下表的区别: Payload | MySQL | MSSQL | Oracle | PostgreSQL |
'a'||'b'='ab' | False (0=0) | False (语法错) | True | True |
'a'+'b'='ab' | True (0=0) | True (拼接) | False (语法错) | False (语法错) |
'a' 'b'='ab' | True (MySQL 允许空格连接) | False (语法错) | False (语法错) | False (语法错) |
length('a'+'b') | 1 (是'0') | 2 (是'ab') | - | - |
对于Oracle和PostgreSQL,再细分下可以通过
(SELECT 1 FROM dual)=1判断(只有 Oracle 需要 dual 表)弄清楚数据库类型后再用联合注入查验版本:
- MySQL/PG:
UNION SELECT version(), NULL...
- MSSQL:
UNION SELECT @@version, NULL...
- Oracle:
UNION SELECT banner, NULL FROM v$version
大致语法可访问SQL injection cheat sheet
联合注入
要使
UNION 查询生效,必须满足两个关键要求:- 各个查询必须返回相同数量的列。
- 每个查询中各列的数据类型必须兼容。
所以获取原来查询的相关信息是非常重要的,有两种常见的方法:
ORDER BY
注入一系列的
ORDER BY子句,并递增指定的列索引,直到出现错误。例如,如果注入点是原始查询中WHERE子句内的带引号字符串,提交:直至报错。
UNION SELECT
提交一系列指定不同数量空值的
UNION SELECT负载:和
ORDER BY不同的是要等不报错并且UNION SELECT可以更进一步的推断数据类型,例如把NULL换成1或者’1‘,有下面三种反应:- 报错 = 数据类型错了。
- 没反应(页面正常) = 数据类型对了,但这一列不显示数据(运气不好)。
- 看到字符 = 数据类型对了,且找到了输出位置(运气爆棚,注入成功)。
当SELECT只有一列可以查询相关信息而你懒的查两次的时候(肯定不是我),可以用|| '~' ||连接符,例如:
盲注
大多数情况下我们没那么容易可以直接注入得到信息,网站可能不直接显示敏感的信息,只是对于对错进行响应,基于网站的响应不同我们可以进行盲注。
布尔盲注
布尔盲注的核心在于:通过页面返回内容的“真/假”差异,来逐个比特地推断数据。
- 寻找注入点
在URL参数、POST数据、Cookie、HTTP头等等中寻找交互点。
- 构造逻辑判断
分别注入
'AND '1'='1 --和'AND '1'='2 --查看页面的响应的区别,如果有则纯在布尔盲注。- 长度判断
判断想要得到信息的长度,这里以密码为例,注入:
- 爆破信息
一般来说手动一个个判断比较繁琐,所以我们可以通过使用Burp Intruder(Cluster Bomb模式)来进行爆破,示例:
隐错注入
通过页面状态码判断条件是否为真,一般语法(不同数据库有细微差别):
页面不报错,条件为假。
页面报错,条件为真。
显错注入
顾名思义是通过故意报错来展示信息:
由于密码一般是字符串型,所以会报错,并且打印。
时间盲注
当前面的都不起效时就可能是时间盲注了,一般的逻辑就是当判断条件为真时,延迟响应:
总结
对于这四种盲注,我整理出了一个表格记入:
注入类型 | 核心特征 | 提取数据方式 | 典型 Payload (非同一数据库写法) | 适用场景 | 攻击效率 |
布尔盲注
| 页面内容发生变化。
真 -> 显示“欢迎回来”
假 -> 显示“用户不存在” | 二分法推断。
根据页面内容差异判断真假。 | AND 1=1 (正常)
AND 1=2 (缺字/空白) | 页面屏蔽了报错,但会根据查询结果显示不同内容。 | 低 🐢
(需逐字爆破) |
隐式报错注入
| 页面状态码发生变化。
真 -> HTTP 500 (崩溃)
假 -> HTTP 200 (正常) | 二分法推断。
制造逻辑:猜对就崩溃(除以零),猜错就正常。 | CASE WHEN (1=1) THEN 1/0 ELSE 'a' END
(利用数学错误炸掉数据库) | 页面屏蔽了报错内容,且内容无变化,但会返回 500 状态码。 | 低 🐢
(需逐字爆破) |
显式报错注入
| 页面直接打印出类似 XPATH syntax error: 'p@ssword' 的错误信息。 | 直接读取。
构造错误让数据库把数据“吐”在报错信息里。 | AND updatexml(1, concat('~', (SELECT pass...), '~'), 1)
CAST((SELECT...) AS int) | 页面没有屏蔽报错。 | 极高 🚀
(1次请求 = 1条数据) |
时间盲注
| 页面响应时间发生变化。
真 -> 10秒后才加载
假 -> 毫秒级加载 | 时序推断。
制造逻辑:猜对就睡,猜错就醒。 | IF (1=1) WAITFOR DELAY '0:0:10'
pg_sleep(10) | 最后手段。
无回显、无报错、状态码永远 200。 | 极低 🐌
(受网速影响大,最慢) |
Loading...




