GoTestWAF
GoTestWAF 是一个用于API和OWASP攻击模拟的工具,支持多种API协议,包括REST、GraphQL、gRPC、SOAP、XMLRPC等。
它旨在评估Web应用程序安全解决方案,如API安全代理、Web应用防火墙、IPS、API网关等。
工作原理
GoTestWAF生成恶意请求,将编码后的有效载荷放置在HTTP请求的不同部分:主体、头部、URL参数等。生成的请求会发送到启动GoTestWAF时指定的应用安全解决方案URL。安全解决方案的评估结果会记录在您机器上创建的报告文件中。
请求生成的默认条件在testcases
文件夹中的YAML文件中定义,格式如下:
payload:
- '"union select -7431.1, name, @aaa from u_base--w-'
- "'or 123.22=123.22"
- "' waitfor delay '00:00:10'--"
- "')) or pg_sleep(5)--"
encoder:
- Base64Flat
- URL
placeholder:
- UrlPath
- UrlParam
- JSUnicode
- Header
type: SQL Injection
-
payload
是恶意攻击样本(例如XSS有效载荷如<script>alert(111)</script>
或更复杂的内容)。 由于YAML字符串格式对有效载荷是必需的,它们必须编码为二进制数据。 -
encoder
是在将有效载荷放入HTTP请求之前应用于有效载荷的编码器。可能的编码器有:- Base64
- Base64Flat
- JSUnicode
- URL
- Plain(保持有效载荷字符串原样)
- XML Entity
-
placeholder
是编码后的有效载荷应该放置的HTTP请求内部位置。可能的占位符有:- gRPC
- Header
- UserAgent
- RequestBody
- JSONRequest
- JSONBody
- HTMLForm
- HTMLMultipartForm
- SOAPBody
- XMLBody
- URLParam
- URLPath
- RawRequest
RawRequest
占位符允许您进行任意HTTP请求。通过替换URL路径、头部或主体中的{{payload}}
字符串来替换有效载荷。RawRequest
占位符的字段:method
path
headers
body
RawRequest
占位符的必填字段:method
字段
示例:
payload: - test encoder: - Plain placeholder: - RawRequest: method: "POST" path: "/" headers: Content-Type: "multipart/form-data; boundary=boundary" body: | --boundary Content-disposition: form-data; name="field1" Test --boundary Content-disposition: form-data; name="field2" Content-Type: text/plain; charset=utf-7 Knock knock. {{payload}} --boundary-- type: RawRequest test
-
type
是文件中整个有效载荷组的名称。它可以是任意的,但应反映文件中的攻击类型。
请求生成是一个三步过程,涉及有效载荷数量乘以编码器和占位符数量。假设您定义了2个有效载荷,3个编码器(Base64、JSUnicode和URL)和1个占位符(URLParameter - HTTP GET参数)。在这种情况下,GoTestWAF将在一个测试用例中发送2x3x1 = 6个请求。
在启动GoTestWAF时,您还可以在两个内置测试用例之间选择:OWASP Top-10、OWASP-API,或者使用您自己的(通过使用配置选项testCasePath
)。
要求
- GoTestWAF支持所有流行的操作系统(Linux、Windows、macOS),如果系统中安装了Go,可以进行本地构建。如果您想本地运行GoTestWAF,请确保安装了Chrome网络浏览器以生成PDF报告。如果没有Chrome,您可以创建HTML格式的报告。
- 如果将GoTestWAF作为Docker容器运行,请确保您已安装并配置Docker,并且GoTestWAF和被评估的应用安全解决方案连接到同一个Docker网络。
- 为了成功启动GoTestWAF,请确保运行GoTestWAF的机器的IP地址在运行应用安全解决方案的机器上被列入白名单。
使用Docker快速启动
以下步骤将指导您使用最少的配置在Docker上下载和启动GoTestWAF。
-
从Docker Hub拉取GoTestWAF镜像:
docker pull wallarm/gotestwaf
-
启动GoTestWAF镜像:
docker run --rm --network="host" -it -v ${PWD}/reports:/app/reports \ wallarm/gotestwaf --url=<被评估的安全解决方案URL>
如果需要,您可以将
${PWD}/reports
替换为用于放置评估报告的另一个文件夹的路径。如果您不想选择通过电子邮件发送报告,只需在出现电子邮件请求消息后按Enter键,或者可以使用--noEmailReport跳过该消息:
docker run --rm --network="host" -v ${PWD}/reports:/app/reports \ wallarm/gotestwaf --url=<被评估的安全解决方案URL> --noEmailReport
如果被评估的安全工具可以从外部访问,您可以跳过
--network="host"
选项。此选项允许在127.0.0.1上运行的Docker容器之间进行交互。要执行gRPC测试,您必须有一个可用的端点并使用--grpcPort <端口>命令行选项。
docker run --rm --network="host" -it -v ${PWD}/reports:/app/reports \ wallarm/gotestwaf --grpcPort 9000 --url=http://my.grpc.endpoint
-
检查您的电子邮件以获取报告。
您已成功使用最少的配置通过GoTestWAF评估了您的应用安全解决方案。要了解高级配置选项,请使用此链接。
检查评估结果
检查使用STDOUT
和STDERR
服务记录的评估结果。例如:
INFO[0000] GoTestWAF 已启动 版本=v0.4.11-1-g8ccc316
INFO[0000] 开始加载测试用例
INFO[0000] 测试用例加载完成
INFO[0000] 测试用例指纹 指纹=23c3ae919db5e6edcb62815de1a09fdf
INFO[0000] 尝试识别 WAF 解决方案
INFO[0000] 未识别出 WAF
INFO[0000] WAF 预检查 URL="http://localhost:8080"
INFO[0000] WAF 预检查 已拦截=true 状态码=403 状态=完成
INFO[0000] WebSocket 预检查 状态=已开始 URL="ws://localhost:8080"
INFO[0000] WebSocket 预检查 连接="不可用" 错误="websocket: 握手失败" 状态=完成
INFO[0000] gRPC 预检查 状态=已开始
INFO[0000] gRPC 预检查 连接="不可用" 状态=完成
INFO[0000] 开始扫描 URL="http://localhost:8080"
INFO[0025] 扫描完成 持续时间=25.043996212秒
真阳性测试:
+-----------------------+-------------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+
| 测试集合 | 测试用例 | 百分比, % | 已拦截 | 已绕过 | 未解决 | 已发送 | 失败 |
+-----------------------+-------------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+
| community | community-128kb-rce | 0.00 | 0 | 0 | 1 | 1 | 0 |
| community | community-128kb-sqli | 0.00 | 0 | 0 | 1 | 1 | 0 |
| community | community-128kb-xss | 0.00 | 0 | 0 | 1 | 1 | 0 |
| community | community-16kb-rce | 100.00 | 1 | 0 | 0 | 1 | 0 |
| community | community-16kb-sqli | 100.00 | 1 | 0 | 0 | 1 | 0 |
| community | community-16kb-xss | 100.00 | 1 | 0 | 0 | 1 | 0 |
| community | community-32kb-rce | 100.00 | 1 | 0 | 0 | 1 | 0 |
| community | community-32kb-sqli | 100.00 | 1 | 0 | 0 | 1 | 0 |
| community | community-32kb-xss | 100.00 | 1 | 0 | 0 | 1 | 0 |
| community | community-64kb-rce | 100.00 | 1 | 0 | 0 | 1 | 0 |
| community | community-64kb-sqli | 100.00 | 1 | 0 | 0 | 1 | 0 |
| community | community-64kb-xss | 100.00 | 1 | 0 | 0 | 1 | 0 |
| community | community-8kb-rce | 100.00 | 1 | 0 | 0 | 1 | 0 |
| community | community-8kb-sqli | 100.00 | 1 | 0 | 0 | 1 | 0 |
| community | community-8kb-xss | 100.00 | 1 | 0 | 0 | 1 | 0 |
| community | community-lfi | 100.00 | 8 | 0 | 0 | 8 | 0 |
| community | community-lfi-multipart | 0.00 | 0 | 0 | 9 | 9 | 0 |
| community | community-rce | 83.33 | 10 | 2 | 0 | 12 | 0 |
| community | community-sqli | 100.00 | 32 | 0 | 0 | 32 | 0 |
| community | community-user-agent | 70.00 | 7 | 3 | 0 | 10 | 0 |
| community | community-xss | 95.80 | 502 | 22 | 0 | 524 | 0 |
| community | community-xxe | 0.00 | 0 | 2 | 0 | 2 | 0 |
| owasp | crlf | 77.78 | 7 | 2 | 0 | 9 | 0 |
| owasp | ldap-injection | 3.13 | 2 | 62 | 0 | 64 | 0 |
| owasp | mail-injection | 12.50 | 3 | 21 | 0 | 24 | 0 |
| owasp | nosql-injection | 0.00 | 0 | 70 | 0 | 70 | 0 |
| owasp | path-traversal | 24.77 | 27 | 82 | 1 | 110 | 0 |
| owasp | rce | 33.33 | 22 | 44 | 0 | 66 | 0 |
| owasp | rce-urlparam | 33.33 | 3 | 6 | 0 | 9 | 0 |
| owasp | shell-injection | 27.08 | 13 | 35 | 0 | 48 | 0 |
| owasp | sql-injection | 24.36 | 38 | 118 | 0 | 156 | 0 |
| owasp | ss-include | 37.50 | 15 | 25 | 0 | 40 | 0 |
| owasp | sst-injection | 18.75 | 12 | 52 | 0 | 64 | 0 |
| owasp | xml-injection | 0.00 | 0 | 12 | 1 | 13 | 0 |
| owasp | xss-scripting | 33.20 | 167 | 336 | 1 | 504 | 0 |
| owasp-api | graphql | 0.00 | 0 | 6 | 0 | 6 | 0 |
| owasp-api | graphql-post | 50.00 | 2 | 2 | 0 | 4 | 0 |
| owasp-api | grpc | 0.00 | 0 | 0 | 0 | 0 | 0 |
| owasp-api | non-crud | 100.00 | 2 | 0 | 0 | 2 | 0 |
| owasp-api | rest | 23.08 | 3 | 10 | 0 | 13 | 0 |
| owasp-api | soap | 23.08 | 3 | 10 | 0 | 13 | 0 |
+-----------------------+-------------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+
| 日期: | 项目名称: | 真阳性得分: | 已拦截 (已解决): | 已绕过 (已解决): | 未解决 (已发送): | 总发送数: | 失败 (总数): |
| 2024-02-08 | GENERIC | 49.12% | 890/1812 (49.12%) | 922/1812 (50.88%) | 15/1827 (0.82%) | 1827 | 0/1827 (0.00%) |
+-----------------------+-------------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+
真阴性测试: +-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+ | 测试集 | 测试用例 | 百分比, % | 阻止 | 绕过 | 未解决 | 发送 | 失败 | +-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+ | 误报 | 文本 | 85.65 | 31 | 185 | 0 | 216 | 0 | +-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+ | 日期: | 项目名称: | 真阴性得分: | 阻止 (已解决): | 绕过 (已解决): | 未解决 (已发送): | 总发送数: | 失败 (总计): | | 2024-02-08 | 通用 | 85.65% | 31/216 (14.35%) | 185/216 (85.65%) | 0/216 (0.00%) | 216 | 0/216 (0.00%) | +-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+-----------------------+
总结: +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ | 类型 | 真阳性测试被阻止 | 真阴性测试通过 | 平均 | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ | API安全 | 26.32% | 不适用 | 26.32% | | 应用安全 | 49.61% | 85.65% | 67.63% | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+ | 得分 | 46.97% | +-----------------------------+-----------------------------+-----------------------------+-----------------------------+
报告文件 waf-evaluation-report-<日期>.pdf
可在用户目录的 reports
文件夹中找到。你也可以使用 reportPath
参数指定保存报告的目录,使用 reportName
参数指定报告文件的名称。要了解高级配置选项,请使用此链接。
选项:
--addDebugHeader 在每个请求中添加包含测试信息哈希的头部
--addHeader string 添加到请求中的HTTP头部
--blockConnReset 如果为true,连接重置将被视为阻塞
--blockRegex string 用于检测具有与未阻塞请求相同HTTP响应状态码的阻塞页面的正则表达式
--blockStatusCodes ints WAF在阻塞请求时使用的HTTP状态码(默认为[403])
--configPath string 配置文件路径(默认为"config.yaml")
--email string 报告将发送到的电子邮件地址
--followCookies 如果为true,使用服务器发送的cookies。可能只在--maxIdleConns=1时生效(仅适用于gohttp)
--graphqlURL string 要检查的GraphQL URL
--grpcPort uint16 要检查的gRPC端口
--httpClient string 用于发送请求的HTTP客户端:chrome或gohttp(默认为"gohttp")
--idleConnTimeout int 保持连接存活的最长时间(仅适用于gohttp)(默认为2)
--ignoreUnresolved 如果为true,未解决的测试用例将被视为已绕过(影响得分和结果)
--includePayloads 如果为true,有效载荷将包含在HTML/PDF报告中
--logFormat string 设置日志格式:text或json(默认为"text")
--logLevel string 日志级别:panic、fatal、error、warn、info、debug、trace(默认为"info")
--maxIdleConns int 最大保持连接数(仅适用于gohttp)(默认为2)
--maxRedirects int 处理重定向的最大次数(仅适用于gohttp)(默认为50)
--noEmailReport 本地保存报告
--nonBlockedAsPassed 如果为true,将未被阻塞的请求计为通过。如果为false,不满足PassStatusCodes/PassRegExp的请求将被视为阻塞
--openapiFile string OpenAPI文件路径
--passRegex string 用于检测具有与阻塞请求相同HTTP状态码的正常(未阻塞)网页的正则表达式
--passStatusCodes ints WAF在通过请求时使用的HTTP响应状态码(默认为[200,404])
--proxy string 要使用的代理URL
--quiet 如果为true,禁用详细日志记录
--randomDelay int 除请求之间的延迟外的随机延迟(毫秒)(默认为400)
--renewSession 每次测试前更新cookies。应与--followCookies标志一起使用(仅适用于gohttp)
--reportFormat string 将报告导出为以下格式之一:none、pdf、html、json(默认为"pdf")
--reportName string 报告文件名。支持time
包模板格式(默认为"waf-evaluation-report-2006-January-02-15-04-05")
--reportPath string 存储报告的目录(默认为"reports")
--sendDelay int 请求之间的延迟(毫秒)(默认为400)
--skipWAFBlockCheck 如果为true,将跳过WAF检测测试
--skipWAFIdentification 跳过WAF识别
--testCase string 如果设置,则仅运行此测试用例
--testCasesPath string 测试用例文件夹路径(默认为"testcases")
--testSet string 如果设置,则仅运行此测试集的用例
--tlsVerify 如果为true,将验证接收到的TLS证书
--url string 要检查的URL
--version 显示GoTestWAF版本并退出
--wafName string WAF产品名称(默认为"generic")
--workers int 扫描的工作线程数(默认为5)
GoTestWAF支持两种HTTP客户端来执行请求,可通过--httpClient
选项选择。默认客户端是标准Golang HTTP客户端。第二个选项是Chrome,可以使用--httpClient=chrome
CLI参数来使用。请注意,在Linux系统上,必须在Docker参数中添加--cap-add=SYS_ADMIN
参数才能使用Chrome作为请求执行器运行GoTestWAF。
报告名称
使用reportName
选项,您可以为GoTestWAF报告设置自己的文件名。此选项支持golang的time
包用于时间戳。详细信息可以在此处找到。您可以使用以下占位符将时间戳添加到报告名称中:
- 年份:
2006
、06
- 月份:
Jan
、January
- 星期几文本:
Mon
、Monday
- 月份中的数字日期:
2
、_2
、02
- 年份中的数字日期:
__2
、002
- 小时:
15
、3
、03
(PM或AM) - 分钟:
4
、04
- 秒:
5
、05
- 上午/下午标记:
PM
- 数字时区:
Z0700
= Z或±hhmm,Z07:00
= Z或±hh:mm,Z07
= Z或±hh
例如,默认的reportName
是waf-evaluation-report-2006-January-02-15-04-05
,其中2006
将被替换为实际年份,January
替换为月份,02
替换为日期,15
替换为小时,04
替换为分钟,05
替换为秒。
基于OpenAPI文件的扫描
为了更好地进行扫描,GTW支持通过有效的应用程序请求发送恶意向量。GoTestWAF不是构造结构简单的请求并将其发送到启动时指定的URL,而是基于OpenAPI 3.0格式的应用程序API描述创建有效请求。
工作原理:
-
GoTestWAF加载OpenAPI文件并构造请求模板。然后根据它们支持的占位符(例如,如果请求路径中有一个字符串参数,则该请求将被分配到支持URLPath占位符的请求组)将所有模板分成组。
-
从队列中选择下一个要发送的恶意向量。根据为其指定的占位符,选择可以替换此向量的所有查询模板。接下来,将向量替换到模板中并发送请求。
-
根据OpenAPI文件中指定的可能响应,确定请求是被WAF阻止还是传递给应用程序。如果响应代码的状态及其方案与OpenAPI文件中描述的匹配,则请求被标记为已绕过。否则,它将被标记为已阻止。可能应用程序只响应状态代码,并且此状态代码与WAF的响应匹配。在这种情况下,请求将被标记为未解决。
支持的一些OpenAPI功能:
-
请求的头部、路径、查询参数和正文中的数字和字符串参数;
-
支持以下请求正文的内容类型:
application/json
、application/xml
、application/x-www-form-urlencoded
、text/plain
; -
支持XML的以下修饰符:
name
、wrapped
、attribute
、prefix
、namespace
; -
通过
minLength
和maxLength
参数支持字符串的长度限制; -
通过
minimum
、maximum
、exclusiveMinimum
和exclusiveMaximum
支持数字的值限制; -
通过
minItems
和maxItems
支持数组长度的限制; -
支持通过
oneOf
、anyOf
、allOf
组合方案。
基于所描述的操作原理,OpenAPI文件正确表示已实现的应用程序API极其重要。因此,例如,您不能使用default
来描述查询的可能响应。
注意:您需要将包含openapi规范的卷转发到GoTestWAF容器。
-v ${PWD}/api.yaml:/app/api.yaml
完整的Docker示例:
docker run --rm --network="host" -it -v ${PWD}/reports:/app/reports -v ${PWD}/api.yaml:/app/api.yaml wallarm/gotestwaf --wafName your_waf_name --url=https://example.com/v1 --openapiFile api.yaml
与OWASP核心规则集回归测试套件一起运行
GoTestWAF允许轻松集成其他测试套件。
在这个例子中,我们将演示如何添加来自OWASP核心规则集回归测试套件的测试。
由于测试是以不同于GoTestWAF格式的格式编写的,因此需要进行转换。为此,提供了脚本misc/modsec_regression_testset_converter.rb。
要转换测试,请运行make modsec_crs_regression_tests_convert
。
然后,使用更新后的测试集构建容器。
make gotestwaf
请注意,默认情况下,仅转换一部分规则的测试。选择了以下类别:
- REQUEST-932-APPLICATION-ATTACK-RCE
- REQUEST-933-APPLICATION-ATTACK-PHP
- REQUEST-941-APPLICATION-ATTACK-XSS
- REQUEST-930-APPLICATION-ATTACK-LFI
- REQUEST-931-APPLICATION-ATTACK-RFI
- REQUEST-942-APPLICATION-ATTACK-SQLI
- REQUEST-944-APPLICATION-ATTACK-JAVA
- REQUEST-934-APPLICATION-ATTACK-GENERIC
- REQUEST-913-SCANNER-DETECTION
如果需要,可以修改misc/modsec_regression_testset_converter.rb中的变量"crs_testcases"以添加或删除测试类别。