基于 WAF 研发视角的 WAF 绕过原理
市面上聊 WAF 绕过的文章,大多还停留在红队式的经验主义阶段——Payload 往上一贴,通了,截图,收工。但这种黑盒试错给不了我想要的答案,作为一个习惯性深挖底层逻辑的人,这种“知其然而不知其所以然”的空洞感,在很长一段时间里都让我如鲠在喉。大学时期我把天赋点加在了代码审计和漏洞挖掘上,对 WAF 绕过也仅止于技巧层面的“略知一二”。直到毕业后误打误撞进了现在的公司(一家领先的ict设备厂商)做 WAF 研发,身份的转换让我得以从“修墙人”的视角复盘全局。那一刻我才意识到,所谓的骚操作,在研发视角下其实都是必然。如果非要给千变万化的绕过技巧总结一个本质,笔者认为其实只有一句话:WAF 规范化 (Normalization) 链路与后端服务端处理逻辑之间的解析不一致性 (Parsing Discrepancy)。
什么意思呢,其实就拿一个最简单的绕过实践来举例最好不过了:
编码绕过
我想每个人在代码层面上写waf功能的时候,都必定会想到,使用正则,把关键字过滤。而这其实也是大多数WAF的做法,虽然现在主流都是推行语义分析,但是不免得许多WAF的底层规则终究绕不开有正则的存在。比如说知名的WAF引擎modsecurity,里面的crs规则库,大部分就是以正则取实现,而世界上百分之80的WAF都是由ModSecurity而驱动的,我认为以此为研究是十分必要的。
举个例子,我们随便对一些关键字符做url编码。然后想想是什么情况呢,首先,waf拿到的时候,是编码的,符合以上正则吗,显然,不符合,当然很多小伙伴会觉得,可是你这个编码过后的东西也不符合真正能执行语句的要求啊,可是别忘了,后端很多服务,例如express,都会自动帮你给url解码。又或者说mysql,他支持十六进制输入,又或者是fastjson 对 key,value 值会自动进行 hex 解码和 unicode解码,这些都是WAF层不知道的。
所以,这里可以抛出一个共同的认识,在WAF架构设计中,有一个层次是必须放在检测层的,那就是多维度解码。这点可以在华为防火墙中WAF设计图中也可以得到印证,它被形象的成为了漏斗形检测。
这里给一个华为云 WAF 的流量处理图

然后再来讨论一下一些看着很奇怪,但是在研发视角是必然的一些绕过
脏数据绕过(Dirty Data Bypass)
在很多红队文章里,脏数据绕过往往被描述为:“在 Payload 前面疯狂填充几千上万个字符(比如 几千个X或者填充超大的 JSON 数组),就能神奇地穿透 WAF。”
这种现象如果用黑盒的眼光看,仿佛是 WAF “被塞晕了”、“崩溃了”。但作为研发的视角,我必须澄清:WAF 既没有晕,也没有崩溃,它只是在“保命”和“保业务”。
WAF 不是旁路分析的态势感知,它是串联在网络主链路上的。在 WAF 研发团队里,有一个绕不开的困扰——延迟。客户对 Web 请求的延迟容忍度极低,我们通常被要求用极高的性能去检测,如果接上我们的waf,每个请求的延迟都要几秒,那可以说,这个waf好像比攻击更危险hh。
为了防止 WAF 成为性能瓶颈,所有的 WAF 引擎都必须在架构上设置一个“最大检测深度”。
例如在 ModSecurity 中,有个至关重要的核心配置项:
SecRequestBodyNoFilesLimit(非文件上传的最大请求体检测大小,默认通常设为 131072 Byte / 128KB) 那当请求体大小超过 128KB 时,WAF 该怎么办?
-
做法 A:配置为 Reject(拒绝)。 只要超过 128KB 直接拦截。
- 研发视角复盘: 安全性最高,但实际不太可行。现实业务中,一个包含了 Base64 编码图片的富文本表单,或者一个包含复杂表格数据的 JSON 提交,分分钟超过这个大小。配置为 Reject 会导致大量正常业务瘫痪,也就是我们所说的误报。
-
做法 B:配置为 ProcessPartial(部分处理)。 WAF 只检测前 128KB 的数据,超过的部分直接跳过不检,放行给后端。
- 研发视角复盘: 这是绝大多数 WAF 开发者为了保证业务连续性而做出的妥协配置。**这就是脏数据绕过能够成功的底层原因。如果该waf是以原生modsecurity做底层驱动,**黑客只需生成 130KB 的垃圾参数,把 id=1’ and sleep(5)—+ 塞在最后面,WAF 只看了前 128KB 发现全是安全的 a=xxxxxxxxxx,就放行了。
还有很多种绕过的原理和这个相似,比如说大文件绕过,很多人传马的时候可能传一个很大的文件,把webshell写在文件末尾,这样就检测不到这个内容。
所以,看到这,是不是就能够映衬我在文章前文部分写下的那句话:“所谓的骚操作,在研发视角下其实都是必然“。
当然,我们也不是束手无策,现代 WAF 架构(包括我们正在做的下一代防护体系)正在试图打破这些壁垒:
**流式检测:**抛弃将全量数据读入内存的做法,采用状态机在数据流经过网卡时进行实时计算。
**异常检测:利用机器学习,**谁家正常业务会传 1MB 的无用参数?哪怕由于性能原因 WAF 不能对这 1MB 做正则匹配,也可以通过统计学模型判断其为“非正常业务报文”,在流量特征清洗阶段就将其丢弃。
接着我们再来谈一种原因相似的绕过
Multipart 变量覆盖绕过
其实这种绕过归根结底也是为性能所考虑。我们在做WAF性能测试的时候,都是在想如何改变检测的流向,能够让性能提升且正确率不减,其中的一个最简单的方法就是,根据包特征进行检测的分流。
举个简单的例子
POST /index.php HTTP/1.1
Host: localhost
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: multipart/form-data; boundary=--------1099641188
Content-Length: 101
----------1099641188
Content-Disposition: form-data; name=“id;filename=xx”;name=“id”
'and''=‘
----------1099641188--
而众所周知,在Content-Type: multipart/form-data;且filename这个属性存在的时候,这个请求基本上就是文件上传的请求,所以在实现数据包检测分流的时候,大部分都会把这种类型的包丢给文件上传等符合这类数据包格式的的规则检测,而不会丢去给sql注入、xss等规则检测。而这里又得谈到后端的事情,很多服务器是会对这样的参数进行提取的,只要请求方法对了,就会从body里面提取参数名然后读取,于是这样子就绕过了WAF层的sql注入检测。所以这也再次说明了,其实WAF绕过也并不是所谓的黑魔法,在研发视角来看,这样的绕过思路是必然的,而不是某年某月某人一拍脑门想到的。
同样原理的绕过还有很多,例如修改请求方法也是这个原理,每个请求方法在人们的规定中都会对应着不同的业务功能,比如说上传文件,就是POST,不可能是GET,所以现在是不是就很轻易的理解,这种修改请求的方式为什么会绕过WAF了吧,那便是,这个WAF为了性能做出了数据包分类流向的操作。由于改为了POST,数据包被丢向了检测文件上传等一系列用POST方法的规则去了,但是呢,后端接受的时候可能是写的$_request,不影响拿到参数的,而我们的sql注入的数据包过文件上传等一系列规则的时候,那肯定能顺利通过的。
所以在WAF开发的过程中,我们必须得注意对mutipart/form-data数据的处理。mutipart/form-data由于和普通的POSTDATA数据格式 不一样,从中提取出字段名和字段值需要按照正则匹配的方式,但是由于服务器对接受 的mutipart数据格式的宽松性,导致很多地方都可以通过插入无效字符或者修改字段顺序 来绕过WAF,WAF必须能识别多样性的mutipart数据。如果有幸读到这里,我想你应该对文章一开始提到的WAF 规范化 (Normalization) 链路与后端服务端处理逻辑之间的解析不一致性 (Parsing Discrepancy)有了更深刻的认识了。
而谈到服务器对数据格式的宽松性,就不得不提到一种常见的绕过,那便是
分块传输编码(Chunked Transfer Encoding)绕过
使用Transfer-Encoding: chunked头部,将请求体分块发送
官方表述:分块传输编码(Chunked transfer encoding)是超文本传输协议(HTTP)中的一种数据传输机制,允许HTTP由应用服务器发送给客户端应用( 通常是网页浏览器)的数据可以分成多个部分。分块传输编码只在HTTP协议1.1版本(HTTP/1.1)中提供。
其实简而言之,就是把数据包分成一块一块的丢过去,骗骗死脑筋的WAF。
因为在WAF的视角,我可不知道你这个数据包是怎么样处理的,我只知道这个包过来,过我的规则,有问题我就给你拦截,没有问题我就给你通过
相似的原理的绕过还有很多,比如说参数污染绕过
这里截取一张网上的图,这里有着不同的语言以及中间件对同名参数的处理时不一样的,比如说PHP通常取最后一个,ASP.NET取第一个。那如果是

-
提交多个同名参数,利用WAF与后端取参差异:
?id=1&id=union select 1,2,3
-
WAF可能检查第一个
id参数,而后端取最后一个
ok,到了这里,算是对WAF的绕过做了一些另一个层面的剖析,看到wafbypass的payload,我想想也不再是之前一样,感叹好神奇,为什么会绕过了呢,只会惊叹安全研究人员的鬼斧神工,居然能想到这样绕过,respect!