Skip to content

Commit a97e1b2

Browse files
authored
Update CRS sandbox documentation for coraza and new features (#279)
* fix: change getJSON for resources.GetRemote Signed-off-by: Felipe Zipitria <felipe.zipitria@owasp.org> * docs: update CRS sandbox documentation for coraza and new features - Add coraza (Caddy-based WAF) as a backend option - Add html-matched-rules output format - Document benign request behavior (empty results) - Update architecture section with diagram and backend table - Update CRS version references to use generic "latest" instead of hardcoded version numbers - Add coraza usage example - Note that invalid x-format-output values default to json-matched-rules - Remove outdated "Working on the sandbox" section --------- Signed-off-by: Felipe Zipitria <felipe.zipitria@owasp.org>
1 parent 737f3cc commit a97e1b2

1 file changed

Lines changed: 46 additions & 31 deletions

File tree

content/6-development/6-4-using-the-crs-sandbox.md

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ The sandbox is located at https://sandbox.coreruleset.org/.
2626

2727
An easy way to use the sandbox is to send requests to it with `curl`, although you can use any HTTPS client.
2828

29-
The sandbox has many options, which you can change by adding HTTP headers to your request. One is very important so we will explain it first; this is the `X-Format-Output: txt-matched-rules` header. If you add this header to your request, the sandbox will parse the WAFs output, and return to you the matched CRS rule IDs with descriptions, and the score for your request.
29+
The sandbox has many options, which you can change by adding HTTP headers to your request. One is very important so we will explain it first; this is the `X-Format-Output: txt-matched-rules` header. If you add this header to your request, the sandbox will parse the WAF's output, and return to you the matched CRS rule IDs with descriptions, and the score for your request.
3030

3131
### Example
3232

@@ -43,21 +43,22 @@ In this example, we sent `?file=/etc/passwd` as a GET payload. The CRS should ca
4343

4444
You can send anything you want at the sandbox, for instance, you can send HTTP headers, POST data, use various HTTP methods, et cetera.
4545

46-
The sandbox will return a 200 response code, no matter if an attack was detected or not.
46+
If no attack is detected, the sandbox returns an empty result in the requested format (e.g., an empty JSON array `[]` for `json-matched-rules`, or an empty response for `txt-matched-rules`).
4747

4848
The sandbox also adds a `X-Unique-Id` header to the response. It contains a unique value that you can use to refer to your request when communicating with us. With `curl -i` you can see the returned headers.
4949

5050
### Example showing the response headers
5151

5252
```bash
53-
curl -i -H 'x-format-output: txt-matched-rules' 'https://sandbox.coreruleset.org/?test=posix_uname()'
53+
curl -i -H 'x-format-output: txt-matched-rules' \
54+
'https://sandbox.coreruleset.org/?test=posix_uname()'
5455
HTTP/1.1 200 OK
5556
Date: Tue, 25 Jan 2022 13:53:07 GMT
5657
Content-Type: text/plain
5758
Transfer-Encoding: chunked
5859
Connection: keep-alive
5960
X-Unique-ID: YfAAw3Gq8uf24wZCMjHTcAAAANE
60-
x-backend: apache-3.3.2
61+
x-backend: apache-latest
6162

6263
933150 PL1 PHP Injection Attack: High-Risk PHP Function Name Found
6364
949110 PL1 Inbound Anomaly Score Exceeded (Total Score: 5)
@@ -66,31 +67,35 @@ x-backend: apache-3.3.2
6667

6768
## Default options
6869

69-
Its useful to know that you can tweak the sandbox in various ways. If you dont send any `X-` headers, the sandbox will use the following defaults.
70+
It's useful to know that you can tweak the sandbox in various ways. If you don't send any `X-` headers, the sandbox will use the following defaults.
7071

7172
- The default backend is _Apache 2 with ModSecurity 2.9_.
72-
- The default CRS version is the _latest release version_, currently 4.22.0.
73+
- The default CRS version is the _latest release version_.
7374
- The default Paranoia Level is 1, which is the least strict setting.
7475
- By default, the response is the full audit log from the WAF, which is verbose and includes unnecessary information, hence why `X-Format-Output: txt-matched-rules` is useful.
7576

7677
## Changing options
7778

78-
Lets say you want to try your payload on different WAF engines or CRS versions, or like the output in a different format for automated usage. You can do this by adding the following HTTP headers to your request:
79+
Let's say you want to try your payload on different WAF engines or CRS versions, or like the output in a different format for automated usage. You can do this by adding the following HTTP headers to your request:
7980

80-
- `x-crs-version`: will pick another CRS version. Available values are `4.22.0` (default) and `3.3.8`.
81+
- `x-crs-version`: will pick another CRS version. Available values are `latest` (default, currently the latest release) and any supported semver version (e.g. `3.3.8`).
8182
- `x-crs-paranoia-level`: will run CRS in a given paranoia level. Available values are `1` (default), `2`, `3`, `4`.
8283
- `x-crs-mode`: can be changed to return the http status code from the backend WAF. Default value is blocking (`On`), and can be changed using `detection` (will set engine to `DetectionOnly`). Values are case insensitive.
8384
- `x-crs-inbound-anomaly-score-threshold`: defines the inbound anomaly score threshold. Valid values are any integer > 0, with `5` being the CRS default. ⚠️ Anything different than a positive integer will be taken as 0, so it will be ignored. This only makes sense if `blocking` mode is enabled (the default now).
8485
- `x-crs-outbound-anomaly-score-threshold`: defines the outbound anomaly score threshold. Valid values are any integer > 0, with `4` being the CRS default. ⚠️ Anything different than a positive integer will be taken as 0, so it will be ignored. This only makes sense if `blocking` mode is enabled (the default now).
85-
- `x-backend` allows you to select the specific backend web server
86+
- `x-backend` allows you to select the specific backend web server:
8687
- `apache` (default) will send the request to **Apache 2 + ModSecurity 2.9**.
8788
- `nginx` will send the request to **Nginx + ModSecurity 3**.
89+
- `coraza` will send the request to **Coraza WAF on Caddy**.
8890
- `x-format-output` formats the response to your use-case (human or automation). Available values are:
89-
- omitted/default: the WAFs audit log is returned unmodified as JSON
91+
- omitted/default: the WAF's audit log is returned unmodified as JSON
9092
- `txt-matched-rules`: human-readable list of CRS rule matches, one rule per line
9193
- `txt-matched-rules-extended`: same but with explanation for easy inclusion in publications
9294
- `json-matched-rules`: JSON formatted CRS rule matches
9395
- `csv-matched-rules`: CSV formatted
96+
- `html-matched-rules`: HTML page with a styled table of matched rules
97+
98+
Invalid `x-format-output` values default to `json-matched-rules`.
9499

95100
The header names are case-insensitive.
96101

@@ -100,13 +105,13 @@ If you work with JSON output (either unmodified or matched rules), `jq` is a use
100105

101106
### Advanced examples
102107

103-
Lets say you want to send a payload to an old CRS version **3.2.1** and choose **Nginx + ModSecurity 3** as a backend, because this is what you are interested in. You want to get the output in JSON because you want to process the results with a script. (For now, we use `jq` to pretty-print it.)
108+
Let's say you want to send a payload to CRS version **3.3.8** and choose **Nginx + ModSecurity 3** as a backend, because this is what you are interested in. You want to get the output in JSON because you want to process the results with a script. (For now, we use `jq` to pretty-print it.)
104109

105110
The command would look like:
106111

107112
```bash
108113
curl -H "x-backend: nginx" \
109-
-H "x-crs-version: 3.2.1" \
114+
-H "x-crs-version: 3.3.8" \
110115
-H "x-format-output: json-matched-rules" \
111116
https://sandbox.coreruleset.org/?file=/etc/passwd | jq .
112117

@@ -129,14 +134,23 @@ curl -H "x-backend: nginx" \
129134
]
130135
```
131136

132-
Let’s say you are working on a vulnerability publication and want to add a paragraph to explain how CRS protects (or doesn’t!) against your exploit. Then the `txt-matched-rules-extended` can be a useful format for you.
137+
You can also test the same payload across different WAF engines to compare detection behavior:
138+
139+
```bash
140+
# Test on Coraza (Caddy-based WAF)
141+
curl -H "x-backend: coraza" \
142+
-H "x-format-output: txt-matched-rules" \
143+
'https://sandbox.coreruleset.org/?q=<script>alert(1)</script>'
144+
```
145+
146+
Let's say you are working on a vulnerability publication and want to add a paragraph to explain how CRS protects (or doesn't!) against your exploit. Then the `txt-matched-rules-extended` can be a useful format for you.
133147

134148
```bash
135149
curl -H 'x-format-output: txt-matched-rules-extended' \
136150
https://sandbox.coreruleset.org/?file=/etc/passwd
137151

138-
This payload has been tested against OWASP CRS
139-
web application firewall. The test was executed using the apache engine and CRS version 3.3.2.
152+
This payload has been tested against the OWASP CRS
153+
web application firewall. The test was executed using the apache engine and CRS version latest.
140154

141155
The payload is being detected by triggering the following rules:
142156

@@ -159,34 +173,35 @@ All requests sent to the sandbox are logged and processed by the sandbox infrast
159173

160174
## Architecture
161175

162-
The sandbox consists of various parts. The frontend that receives the requests runs on Openresty. It handles the incoming request, chooses and configures the backend running CRS, proxies the request to the backend, and waits for the response. Then it parses the WAF audit log and sends the matched rules back in the format chosen by the user.
176+
The sandbox consists of various parts. The frontend that receives the requests runs on OpenResty (Nginx with Lua). It handles the incoming request, selects and configures the backend running CRS based on the request headers, proxies the request to the backend, and waits for the response. Then it parses the WAF audit log and sends the matched rules back in the format chosen by the user.
163177

164-
There is a backend container for every engine and version. For instance, one Apache with CRS 4.22.0, one with CRS 3.3.8, et cetera... These are normal webserver installations with a WAF and the CRS.
178+
```text
179+
Client → OpenResty (Lua routing) → WAF Backend → Mirror Backend
180+
181+
ModSecurity/Coraza CRS
182+
183+
Audit Logs → Filebeat → Elasticsearch/S3
184+
```
165185

166-
The backend writes their JSON logs to a volume to be read by a collector script and sent to S3 bucket and Elasticsearch.
186+
There is a backend container for every engine and version. The current backends are:
167187

168-
The logs are parsed, and values like User-Agent and geolocation are extracted. We use Kibana to keep an overview of how the sandbox is used, and hopefully gain new insights about attacks.
188+
| Backend | Engine | Container |
189+
|---------|--------|-----------|
190+
| Apache + ModSecurity 2.9 | `apache` | `apache-latest`, `apache-3_3_8` |
191+
| Nginx + ModSecurity 3 | `nginx` | `nginx-latest`, `nginx-3_3_8` |
192+
| Coraza WAF on Caddy | `coraza` | `coraza-latest` |
193+
194+
The backend writes their JSON audit logs to a shared volume. OpenResty reads the per-transaction audit log file to extract matched rules and format the response. Logs are also collected by Filebeat and sent to an S3 bucket and Elasticsearch for monitoring.
169195

170196
## Known issues
171197

172198
In some cases, the sandbox will not properly handle and finish your request.
173199

174-
- **Malformed HTTP requests:** The frontend, Openresty, is itself a HTTP server which performs parsing of the incoming request. The backend servers running CRS are regular webservers such as Apache and Nginx. Either one of these may reject a malformed HTTP request with an error 400 before it is even processed by CRS. This happens for instance when you try to send an Apache 2.4.50 attack that depended on a URL encoding violation. If you receive an error 400, your request was rejected by the frontend or a backend, and it was not scanned by CRS.
200+
- **Malformed HTTP requests:** The frontend, OpenResty, is itself a HTTP server which performs parsing of the incoming request. The backend servers running CRS are regular webservers such as Apache and Nginx. Either one of these may reject a malformed HTTP request with an error 400 before it is even processed by CRS. This happens for instance when you try to send an Apache 2.4.50 attack that depended on a URL encoding violation. If you receive an error 400, your request was rejected by the frontend or a backend, and it was not scanned by CRS.
175201
- **ReDoS:** If your request leads to a ReDoS and makes the backend spend too much time to process a regular expression, this leads to a timeout from the backend server. The frontend will cancel the request with an error 502. If you have to wait a long time and then receive an error 502, there was likely a ReDoS situation.
176202

177203
## Questions and suggestions
178204

179205
If you have any issues with the CRS sandbox, please open a GitHub issue at [https://github.com/coreruleset/coreruleset/issues](https://github.com/coreruleset/coreruleset/issues) and we will help you as soon as possible.
180206

181207
If you have suggestions for extra functionality, a GitHub issue is appreciated.
182-
183-
## Working on the sandbox: adding new backends
184-
185-
The following notes are handy for our team maintaining the sandbox.
186-
187-
To add a new backend:
188-
189-
- Each backend has its own IP address.
190-
- docker-compose: copy-paste a back-end container. Give it a new unused IP address in the 10.5.0.\* virtual network.
191-
- The frontend needs to know how to reach the desired backend. There is a hardcoded list in openresty/conf/access.lua with the target IP address.
192-
- httpd-vhosts.conf needs to be changed.

0 commit comments

Comments
 (0)