从GET到POST:SQL注入实战进阶与盲注技术详解
1. 从GET到POSTsqli-labs通关思路的转变如果你已经跟着我一起通关了sqli-labs的前10关那么恭喜你你已经对基于GET请求的SQL注入有了比较扎实的实战理解。从第11关开始sqli-labs靶场将我们带入了一个全新的、更贴近真实Web应用场景的领域基于POST请求的SQL注入。这不仅仅是请求方式的简单变化它意味着我们注入的入口点、数据包的构造方式、以及绕过某些限制的思路都需要进行相应的调整。很多朋友在通关less11到less20时会觉得难度陡然提升或者感觉无从下手核心原因就在于没有理解这种“场景切换”带来的根本性变化。简单来说前10关的注入点大多在URL参数里比如?id1我们直接在浏览器地址栏或者用Burp Suite截获GET请求进行修改就行。但从第11关起注入点转移到了登录框、搜索框这类通过表单提交数据的地方数据是通过HTTP请求的正文Body以POST方式发送的。你不能再通过改URL来注入了必须学会如何构造和修改POST数据包。这对于理解现代Web应用的交互逻辑至关重要因为GET请求通常用于获取资源而涉及用户数据提交如登录、注册、搜索的操作绝大部分都采用POST请求。本篇文章我将带你手把手通关less11到less20。我不会仅仅给你最终的payload然后说“就这样注”那样你只是背答案下次遇到变种还是不会。我会重点拆解每一关的设计意图、漏洞原理、我们该如何一步步测试和判断注入类型并分享我在实战中总结出的高效技巧和容易踩坑的地方。我们的目标不仅是“通关”更是建立起一套应对POST型SQL注入的完整方法论。2. 环境准备与核心工具配置工欲善其事必先利其器。通关POST型注入你手头的工具需要做一些微调思维模式也要从“修改URL”切换到“拦截并修改HTTP请求体”。2.1 浏览器与代理工具你的眼睛和手首先确保你有一个可以方便切换代理的浏览器比如Chrome或Firefox。核心工具依然是Burp Suite它是我们拦截和修改HTTP请求的瑞士军刀。你需要确保Burp Suite的代理监听是开启的默认127.0.0.1:8080并且浏览器已正确配置代理指向Burp。这里有一个关键细节配置浏览器绕过本地地址代理。很多人在本地搭建sqli-labs通常是http://localhost/sqli-labs/如果浏览器所有流量都走Burp可能会导致访问缓慢或Burp社区版出现限速提示。我建议在浏览器的代理设置中将localhost、127.0.0.1以及你的本地IP地址加入“不使用代理服务器”的列表。这样只有对靶场的请求会被Burp捕获其他本地流量直接通行效率更高。2.2 必备浏览器插件简化重复操作面对登录框我们需要反复提交不同的用户名和密码进行测试。每次都手动输入并抓包效率太低。我强烈推荐安装两个浏览器插件HackBar 这是一个集成在浏览器开发者工具中的插件它允许你直接编辑URL参数和POST数据并快速发送请求。对于快速构造payload进行测试非常方便。Max HackBar或Cookie Editor 有些关卡会涉及Cookie操作这类插件可以方便地查看和修改当前页面的Cookie值。2.3 思维转变关注请求体Request Body在Burp Suite的Proxy - Intercept标签页下当你提交一个表单后截获的请求会是这样POST /sqli-labs/Less-11/ HTTP/1.1 Host: localhost Content-Type: application/x-www-form-urlencoded Content-Length: 38 unameadminpasswd123456submitSubmit你的注意力必须从第一行的URL (/sqli-labs/Less-11/) 转移到最下面的请求体 (unameadminpasswd123456)。我们的所有注入测试都将围绕修改uname和passwd这两个参数的值展开。Content-Type: application/x-www-form-urlencoded是最常见的表单提交格式参数以连接。3. Less-11POST型单引号字符型注入入门第11关是一个经典的登录框注入。页面有两个输入框Username和Password以及一个Submit按钮。我们的目标是绕过登录验证。3.1 漏洞点探测与注入类型判断首先我们尝试一个正常的登录比如unameadminpasswdadmin。大概率会返回登录失败。现在开始我们的注入测试流程单引号测试 在用户名框输入一个单引号‘密码任意如1提交。如果页面返回了SQL语法错误如You have an error in your SQL syntax...这强烈暗示后台SQL语句中用户名参数被单引号包裹。可能的语句是SELECT ... FROM ... WHERE username$uname AND password$passwd。逻辑测试 为了确认我们使用经典的‘ or ‘1’’1构造。在用户名框输入admin‘ or ‘1’’1。注意这里我故意在admin后加了一个单引号与前面的单引号闭合然后跟上or ‘1’’1。此时整个SQL语句可能变为WHERE usernameadmin or 11 AND passwordxxx。根据SQL运算符优先级AND优先于OR所以逻辑是(usernameadmin) OR (11 AND passwordxxx)。由于‘1’’1‘永远为真整个WHERE条件恒为真从而可能绕过登录。注释符测试 更优雅的方式是使用注释符--注意后面有个空格或#来注释掉后面的密码检查部分。输入用户名admin‘ --密码任意。生成的SQL可能是WHERE usernameadmin -- AND passwordxxx。--之后的内容被注释密码检查失效只要admin用户存在即可登录成功。3.2 实战注入与获取信息通过上面的测试我们确认了这是基于单引号的字符型注入并且注入点在uname参数。现在我们的目标不仅是登录还要像GET注入一样获取数据。确定字段数 使用order by子句。在用户名框输入admin‘ order by 1 --admin‘ order by 2 --admin‘ order by 3 --…… 直到页面返回错误。如果order by 2成功而order by 3失败说明当前查询结果有2个字段。确定回显点 使用union select。假设字段数是2。输入admin‘ union select 1,2 --。如果登录成功并且页面某处显示了1或2那就说明该位置可以回显我们查询的数据。获取数据库信息 利用回显点。例如回显点在位置2。我们可以输入admin‘ union select 1,database() --获取当前数据库名。admin‘ union select 1,group_concat(table_name) from information_schema.tables where table_schemadatabase() --获取所有表名。admin‘ union select 1,group_concat(column_name) from information_schema.columns where table_name‘users‘ --获取users表的所有列名。admin‘ union select 1,group_concat(username,0x3a,password) from users --最终获取用户名和密码。0x3a是冒号:的十六进制用于分隔。注意 在POST注入中#在URL中表示锚点但在请求体中它就是一个普通字符。然而有些后端代码在解析时可能会将#后的内容截断。为了确保万无一失在POST注入中我强烈推荐使用--杠杠空格作为注释符并且要注意空格不能被URL编码掉。在Burp中直接输入空格即可它会自动编码为或%20后端通常能正确识别。4. Less-12双引号带括号的字符型注入第12关的页面和第11关一模一样但如果你照搬第11关的‘ --payload会发现失效了。这就是sqli-labs的巧妙之处它改变了参数的包裹方式。4.1 识别包裹符号当单引号注入失败时我们就要系统性地测试其他可能的包裹方式双引号“单引号加括号(‘...‘)双引号加括号(“...”)等等。测试方法在用户名和密码框分别尝试输入“、‘、)等符号观察错误信息。对于第12关如果你输入admin“可能会看到类似“admin“”) AND password(“xxx“)的错误信息。这揭示了后台的SQL语句结构是WHERE username(“$uname“) AND password(“$passwd“)。4.2 构造Payload既然包裹符号是(“...”)我们的闭合方式也要与之匹配。正确的payload应该是admin“) --。我们来拆解一下我们输入admin“)。与原始的(“结合形成(“admin“)。这里我们输入的双引号闭合了前面的双引号我们输入的右括号闭合了前面的左括号。后面的--注释掉剩余的AND password(“xxx“)部分。最终有效的SQL片段是WHERE username(“admin“) -- “) AND ...成功绕过。4.3 经验技巧系统化的闭合测试面对一个未知的注入点不要盲目尝试。建立一个简单的测试向量输入‘- 看错误或行为变化。输入“- 看错误或行为变化。输入‘)- 看错误或行为变化。输入“)- 看错误或行为变化。输入‘))- 看错误或行为变化。通过观察页面的不同反应报错、空白、正常可以快速推断出后端代码是如何拼接SQL语句的。这是基本功必须熟练掌握。5. Less-13 14基于布尔与时间的盲注实战从第13关开始sqli-labs的难度再次升级。你输入经典的‘ or ‘1’’1或‘) --发现即使逻辑上应该成立页面也没有显示登录成功而是统一返回一个固定的错误页面比如一张“背景图”。这就是**盲注Blind SQL Injection**的典型特征应用没有将数据库错误或查询结果直接回显在页面上但我们依然可以通过观察页面行为的“真假”差异来推断信息。第13关是单引号带括号的盲注((‘...‘))第14关是双引号盲注(“...”)。我们以第13关为例进行详解。5.1 确认盲注类型首先我们需要确认是布尔盲注还是时间盲注。布尔盲注 页面对于SQL查询条件“真”和“假”有两种不同的响应状态。虽然都不显示数据但可能是不同的图片、细微的文字差异、或者HTTP响应码/长度的不同。时间盲注 无论查询真假页面返回的内容都一样但我们可以通过让数据库执行延时函数如sleep()来制造时间差从而判断条件真假。对于第13关我们先测试布尔盲注。尝试两个payloadadmin‘) and ‘1‘‘1- 这是一个永真条件。admin‘) and ‘1‘‘2- 这是一个永假条件。提交后仔细观察页面。你可能会发现永真时页面显示一张图A永假时显示图B或者一个极细微的差别比如页面标题不同。一定要用Burp Suite的Repeater模块并开启“差异对比”功能或者直接查看响应长度Response length。如果真假条件下响应长度稳定不同那就确认是布尔盲注。如果页面响应完全一致则尝试时间盲注payloadadmin‘) and sleep(5) --。如果页面响应时间明显增加了5秒左右就是时间盲注。经测试第13关是布尔盲注。5.2 布尔盲注手动利用流程布尔盲注的核心思想是通过构造SQL条件逐个字符地“猜解”我们想要的数据。例如我们想知道当前数据库名的第一个字符是什么。假设我们通过测试知道admin‘) and ‘1‘‘1返回“真”页面响应长度L1admin‘) and ‘1‘‘2返回“假”页面响应长度L2。猜解数据库名长度admin‘) and length(database())1 --- 看返回页面是L1还是L2。admin‘) and length(database())2 --- 继续测试直到返回L1假设长度是8。猜解数据库名每个字符 数据库名通常是字母、数字、下划线。我们可以用substr()或mid()函数结合ascii()函数进行二分法猜解效率远高于遍历。猜第一个字符的ASCII码是否大于100admin‘) and ascii(substr(database(),1,1))100 --根据返回的L1/L2调整比较值。例如如果大于100返回L1就再试是否大于150……通过二分法很快就能定位到准确的ASCII码然后转换为字符。重复此过程substr(database(),2,1),substr(database(),3,1)... 直到猜完8个字符。5.3 使用工具进行自动化盲注手动猜解一个字符尚可猜解整个数据库是不现实的。这时就需要工具。Sqlmap是绝对的首选。使用Sqlmap进行POST型盲注的命令如下sqlmap -u “http://localhost/sqli-labs/Less-13/” --data“unameadminpasswd1submitSubmit” --level3 --risk2-u: 指定目标URL。--data: 指定POST请求的数据直接从Burp里复制过来。--level和--risk: 提高测试等级和风险等级以进行更深入的测试。运行后Sqlmap会先检测注入点类型。对于盲注它会问你是否要使用“启发式测试”或“基于布尔的测试”通常按回车默认即可。确认注入后你就可以使用--dbs枚举数据库-D security --tables枚举表-D security -T users --dump导出数据。重要心得 在真实渗透测试或CTF比赛中时间有限。我的策略是先手工快速确认存在注入通过布尔或时间判断然后立即上Sqlmap进行自动化利用。手工注入是理解原理工具注入是提升效率两者结合才是王道。6. Less-15无错误信息的布尔盲注进阶第15关是第13/14关的变种它连明显的错误信息都不再提供了。无论你输入什么页面可能都只显示“Login failed”或一个固定页面。这要求我们更依赖页面状态的细微差异。6.1 寻找“真”“假”信号这种关卡关键在于找到一个能区分SQL查询“真”“假”的“信号”。这个信号可能是HTTP响应长度 最可靠的信号之一。使用Burp Repeater分别发送永真和永假条件的payload对比响应长度Burp会显示。即使页面看起来一样长度也可能差几个字节。页面中的隐藏标记 查看响应HTML源码也许“真”的时候多了一个!-- debug:1 --注释或者某个div的class不同。重定向 “真”条件可能触发一个302重定向到其他页面即使重定向失败响应头也不同“假”条件则没有。Cookie或Session 某些情况下查询成功可能会设置一个特定的Cookie值。对于sqli-labs第15关经过测试你会发现admin‘ and ‘1‘‘1和admin‘ and ‘1‘‘2的响应长度是不同的。一旦确认了这一点后续的利用流程就和第13关的布尔盲注完全一样了手工判断可注入然后用Sqlmap的--string或--not-string参数指定真假页面的特征字符串或者依靠响应长度差异自动判断。6.2 Sqlmap的精细化参数对于这种无错误信息的盲注给Sqlmap更明确的提示可以大大提高识别成功率sqlmap -u “http://localhost/sqli-labs/Less-15/” --data“unameadminpasswd1submitSubmit” --level3 --risk2 --techniqueB--techniqueB 指定使用布尔盲注技术。如果知道真假页面特征可以加上--string“Login success”如果真页面包含此字符串--not-string“Login failed”如果假页面包含此字符串7. Less-16双引号带括号的盲注第16关是第15关的双引号带括号版本。理解了前面的套路这一关就很简单了。确定包裹方式 尝试admin“、admin“)等。通过响应差异可以确定包裹方式是(“...”)。构造布尔测试payload永真admin“) and (“1“)(“1) - 注意闭合原始为(“$uname“)我们输入admin“)闭合后再跟and (“1“)(“1最后的“1是为了语法完整实际上可以被注释掉。更简洁的写法是admin“) or (“1“)(“1)。永假admin“) and (“1“)(“2)。观察响应差异 同样对比响应长度或页面特征找到布尔判断的依据。利用 确认存在布尔盲注后即可使用Sqlmap进行自动化利用记得在--data参数中构造正确的闭合payload或者让Sqlmap自动探测。8. Less-17Update语句注入与密码重置漏洞第17关场景变了它是一个“忘记密码”功能输入用户名会提示“新密码已发送到你的邮箱”。查看源码或抓包可知它首先用SELECT语句验证用户名是否存在如果存在则使用UPDATE语句重置该用户的密码。注入点发生在UPDATE语句的password字段。8.1 漏洞原理分析假设后端代码逻辑如下$username $_POST[‘uname’];$sql “SELECT * FROM users WHERE username‘$username’ LIMIT 1”;- 这里用户名参数如果被注入可能导致SELECT注入但通常这里会做检查或报错后终止。如果用户存在则执行$sql “UPDATE users SET password‘$new_pass’ WHERE username‘$username’”;-注入点在这里关键在于UPDATE语句的SET子句中的password字段值$new_pass是我们可以控制的。但题目设计是$new_pass是一个系统生成的随机值我们无法直接控制。然而UPDATE语句的WHERE子句中的username参数复用了一开始我们输入的$username。如果这个$username在第一次SELECT时被安全地处理了例如用了预编译但WHERE子句没用好或者存在二次逻辑漏洞就可能造成注入。实际上第17关模拟的是一种场景开发者错误地认为既然SELECT语句用了预编译或转义保证了username安全那么在后续的UPDATE语句中复用这个“安全”的变量也是没问题的。但预编译的作用域是单次查询。第一次SELECT预编译了第二次UPDATE如果直接拼接字符串之前的“安全处理”就失效了。8.2 利用方式堆叠查询与时间盲注这一关通常被设计为时间盲注。因为UPDATE语句不返回查询结果我们无法进行联合查询或基于错误的注入。布尔盲注也可能存在但时间盲注更通用。我们的目标是在UPDATE语句的WHERE子句中注入一个基于时间的条件。Payload构造如下admin‘ and if(ascii(substr(database(),1,1))100, sleep(5), 0) --当发送这个请求后如果数据库名的第一个字符ASCII码大于100数据库会sleep(5)导致HTTP响应延迟5秒否则立即返回。通过测量响应时间我们就能逐位猜解数据。8.3 利用工具手工进行时间盲注极其繁琐。必须使用Sqlmapsqlmap -u “http://localhost/sqli-labs/Less-17/” --data“unameadminsubmitSubmit” --techniqueT --time-sec5--techniqueT 指定使用时间盲注技术。--time-sec5 指定延迟时间与payload中的sleep(5)匹配。Sqlmap会自动探测和利用这个时间盲注漏洞。9. Less-18HTTP头部注入之User-Agent第18关引入了新的注入类型HTTP头部注入Header Injection。这一关即使你输入正确的用户名和密码注入点也不在POST参数里而是在HTTP请求的User-Agent头部。9.1 漏洞原理与发现这种漏洞通常发生在这样的场景应用会将用户的User-Agent、X-Forwarded-For等头部信息记录到数据库的日志表中。如果记录时没有过滤直接拼接SQL语句就形成了注入。如何发现需要一定的经验和对应用行为的观察。对于这一关你可以正常登录用户名admin 密码admin登录成功后页面会显示“你的User Agent是xxxx”。如果你用Burp拦截登录请求修改User-Agent头部为一个单引号‘然后转发请求。如果页面报出SQL错误那就证实了User-Agent头部存在注入点。9.2 利用过程抓包 用Burp拦截正常的登录请求unameadminpasswdadmin。修改头部 在Burp的Raw视图里找到User-Agent: xxxx这一行将其值修改为注入payload例如Mozilla/5.0 ... ‘ and ‘1‘‘1。判断类型 发送修改后的请求观察页面变化。如果‘ and ‘1‘‘1和‘ and ‘1‘‘2导致页面显示不同的User-Agent信息或一个显示一个不显示那就是基于User-Agent的回显注入。联合查询 通过错误或回显信息判断字段数然后使用union select。例如将User-Agent改为Mozilla/5.0 ‘ union select 1,version(),database() --如果字段数足够你可能会在页面显示的“你的User Agent是”后面看到数据库版本和名称。9.3 Sqlmap指定注入点对于头部注入需要告诉Sqlmap注入点在哪里sqlmap -u “http://localhost/sqli-labs/Less-18/” --data“unameadminpasswdadminsubmitSubmit” --level3 --risk2 --headers“User-Agent: Mozilla/5.0*”或者更直接地将整个请求保存为文本文件比如req.txt然后sqlmap -r req.txt-r参数会让Sqlmap从文件中加载原始HTTP请求并自动分析所有参数和头部寻找注入点。10. Less-19HTTP头部注入之Referer第19关是第18关的姊妹篇注入点从User-Agent换成了Referer头部。Referer头部表示当前请求是从哪个页面链接过来的。利用方式与第18关完全相同正常登录。抓包修改Referer头部的值为注入payload如http://localhost/sqli-labs/Less-19/‘ and ‘1‘‘1。观察页面回显成功登录后可能会显示Referer信息判断为回显注入。使用union select进行数据获取。使用Sqlmap时同样使用-r参数加载请求文件或者使用--headers“Referer: http://localhost/sqli-labs/Less-19/*”来指定注入点。11. Less-20Cookie注入与身份维持第20关模拟了另一种常见场景Cookie注入。很多应用会将用户身份信息如用户名、用户ID经过一些处理后存储在Cookie中后续的请求通过解析Cookie来维持会话状态。如果这个解析过程存在SQL注入漏洞攻击者就可以通过篡改Cookie值来进行注入。11.1 漏洞利用流程正常登录 使用正确账号如admin/admin登录。观察Cookie 登录成功后使用浏览器插件或Burp的Proxy历史记录查看服务器返回的Set-Cookie头部。你可能会看到一个像unameYWRtaW4的Cookie这看起来像是admin的Base64编码。篡改Cookie 关闭Burp拦截刷新页面或访问需要认证的页面。浏览器会自动带上这个Cookie。用Burp拦截这个请求修改Cookie的值。测试注入 将Cookieuname的值从YWRtaW4admin修改为一个单引号‘的Base64编码Jw。发送请求如果页面报错说明存在Cookie注入。构造Payload 由于Cookie值通常会被解码后使用我们需要将注入语句进行Base64编码。例如我们想进行联合查询原始Payloadadmin‘ union select 1,version(),database() --进行Base64编码注意空格也要编码。可以使用在线工具或Burp的Decoder模块。将编码后的字符串替换原来的Cookie值发送请求。11.2 使用Sqlmap自动化Cookie注入对于Sqlmap来说也很简单使用--cookie参数即可sqlmap -u “http://localhost/sqli-labs/Less-20/” --cookie“unameYWRtaW4*” --level3 --risk2这里的*号指示Sqlmap在Cookie的uname参数值处进行注入测试。Sqlmap会自动处理编码等问题。12. 通关总结与实战思维提炼从Less-11到Less-20sqli-labs系统地演练了POST请求下的各种SQL注入场景。回顾一下我们经历了基础POST注入Less-11, 12 理解如何从GET切换到POST掌握修改请求体的方法。盲注Less-13, 14, 15, 16 面对无回显场景掌握布尔与时间盲注的原理、手工判断方法以及Sqlmap的自动化利用。Update注入Less-17 理解非SELECT语句的注入点利用特别是时间盲注在其中的应用。HTTP头部注入Less-18, 19 将注入点探测范围从请求参数扩大到请求头部理解这种漏洞的成因和利用方式。Cookie注入Less-20 理解基于Cookie的身份维持机制如何产生漏洞掌握对编码后参数的注入方法。通关这些关卡不仅仅是记住了payload更重要的是建立起一套系统化的测试思维定位输入点 不光是表单还有HTTP头部、Cookie、JSON/XML数据体。判断包裹方式 系统化测试‘“)‘))等结合错误信息或行为差异进行判断。识别回显类型 是有显注、错误注入、布尔盲注还是时间盲注这决定了后续的利用手法。工具与手工结合 手工用于快速验证和原理理解Sqlmap用于高效自动化利用和数据获取。关注业务逻辑 像Less-17的Update注入源于对业务逻辑密码重置流程的不安全实现。在实战中理解应用的功能流程往往能发现更隐蔽的注入点。最后务必在授权的靶场环境中进行练习切勿对未授权的系统进行测试。将这些技术内化为自己的安全技能用于建设更安全的Web应用才是学习的最终目的。