├── .gitignore
├── LICENSE
├── README.md
├── conf
├── crs-setup.conf
└── modsecurity.conf
├── main.py
└── requirements.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # poetry
98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102 | #poetry.lock
103 |
104 | # pdm
105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106 | #pdm.lock
107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108 | # in version control.
109 | # https://pdm.fming.dev/#use-with-ide
110 | .pdm.toml
111 |
112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113 | __pypackages__/
114 |
115 | # Celery stuff
116 | celerybeat-schedule
117 | celerybeat.pid
118 |
119 | # SageMath parsed files
120 | *.sage.py
121 |
122 | # Environments
123 | .env
124 | .venv
125 | env/
126 | venv/
127 | ENV/
128 | env.bak/
129 | venv.bak/
130 |
131 | # Spyder project settings
132 | .spyderproject
133 | .spyproject
134 |
135 | # Rope project settings
136 | .ropeproject
137 |
138 | # mkdocs documentation
139 | /site
140 |
141 | # mypy
142 | .mypy_cache/
143 | .dmypy.json
144 | dmypy.json
145 |
146 | # Pyre type checker
147 | .pyre/
148 |
149 | # pytype static type analyzer
150 | .pytype/
151 |
152 | # Cython debug symbols
153 | cython_debug/
154 |
155 | # PyCharm
156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158 | # and can be added to the global gitignore or merged into this file. For a more nuclear
159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160 | #.idea/
161 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ModSecurity CLI
2 |
3 | A CLI wrapper for libmodsecurity to quickly test payloads against Rules in a headless mode, without having to set up a full-fledged web testing environment.
4 |
5 | This wrapper is still in development, and some ModSecurity features could be missing.
6 | Most ModSecurity methods are implemented via [pymodsecurity](https://github.com/AvalZ/pymodsecurity) (requires manual building -- [PR](https://github.com/pymodsecurity/pymodsecurity/pull/21) pending on the [official repository](https://github.com/pymodsecurity/pymodsecurity)),
7 |
8 | ## Getting started
9 |
10 | To run `modsecurity-cli`, you will need a few setup steps.
11 |
12 | ### Setup
13 |
14 | 1. [Compile and Install ModSecurity v3.0.10](#compile-modsecurity-v3010)
15 | 1. [Install pymodsecurity](#install-pymodsecurity)
16 | 1. [Clone the OWASP CoreRuleSet](#clone-the-owasp-coreruleset)
17 | 1. [Run the CLI!](#run-the-cli)
18 |
19 | Here's the detail for each step.
20 |
21 | ### Compile ModSecurity v3.0.10
22 |
23 | First of all, you will need to install [ModSecurity v3.0.10](https://github.com/SpiderLabs/ModSecurity/releases/tag/v3.0.10) on your system.
24 | Currently, this is a ~~nightmare~~tricky process, since you will need to [build ModSecurity v3.0.10 from source](https://github.com/SpiderLabs/ModSecurity/wiki/Compilation-recipes-for-v3.x)
25 | (although some distros might have an updated registry with ModSecurity 3.0.10 already available `*coff*arch*coff*`)
26 |
27 | ### Install pymodsecurity
28 |
29 |
30 | In `modsecurity-cli` ModSecurity methods are implemented via [pymodsecurity](https://github.com/pymodsecurity/pymodsecurity).
31 | Since development on the official repository stopped on ModSecurity v3.0.3, we opened a [PR](https://github.com/pymodsecurity/pymodsecurity/pull/21).
32 |
33 | Current workaround: clone [our fork](https://github.com/AvalZ/pymodsecurity) and [build it from source](https://github.com/AvalZ/pymodsecurity#building-from-source)
34 |
35 | ### Clone the OWASP CoreRuleSet
36 |
37 | To detect incoming payloads, you need a Rule Set.
38 | The *de facto* standard is the [OWASP CoreRuleSet](https://github.com/coreruleset/coreruleset), but of course you can choose any Rule Set you want, or customize the OWASP CRS.
39 |
40 | To run the recommended settings, just clone the OWASP CRS in the project folder:
41 | ```
42 | git clone git@github.com:coreruleset/coreruleset.git
43 | ```
44 |
45 | ### Run the CLI!
46 |
47 | Check ModSecurity version
48 |
49 | ```console
50 | $ python3 main.py --version
51 |
52 | ```
53 |
54 | Evaluate a single parameter
55 |
56 | ```console
57 | $ python3 main.py ""
58 |
59 | 20
60 | ```
61 |
62 | If you want to see the breakdown of matched rules, just use `--verbose`
63 |
64 | ```console
65 | $ python3 main.py "" --verbose
66 |
67 | GET http://www.modsecurity.org/test?q=%3Cscript%3Ealert%281%29%3C%2Fscript%3E
68 |
69 | # Matched rules
70 |
71 | - 920320 [+2/PL2] - Missing User Agent Header
72 | - 920273 [+5/PL4] - Invalid character in request (outside of very strict set)
73 | + 941100 [+5/PL1] - XSS Attack Detected via libinjection
74 | + 941110 [+5/PL1] - XSS Filter - Category 1: Script Tag Vector
75 | + 941160 [+5/PL1] - NoScript XSS InjectionChecker: HTML Injection
76 | + 941390 [+5/PL1] - Javascript method detected
77 | - 941320 [+5/PL2] - Possible XSS Attack Detected - HTML Tag Handler
78 | - 942131 [+5/PL2] - SQL Injection Attack: SQL Boolean-based attack detected
79 | - 942431 [+3/PL3] - Restricted SQL Character Anomaly Detection (args): # of special characters exceeded (6)
80 | - 942432 [+3/PL4] - Restricted SQL Character Anomaly Detection (args): # of special characters exceeded (2)
81 | + 949110 [+0/PL1] - Inbound Anomaly Score Exceeded (Total Score: 43)
82 |
83 | Total Score (from matched rules): 20
84 | ```
85 |
86 | `+` rules are the ones that are actually matched, while `-` rules apply to different paranoia levels (`1` by default)
87 |
88 | You can set a specific Paranoia Level to calculate the score (the default PL is `1`)
89 |
90 | ```console
91 | $ python3 main.py "" -v -PL 2
92 |
93 | GET http://www.modsecurity.org/test?q=%3Cscript%3Ealert%281%29%3C%2Fscript%3E
94 |
95 | # Matched rules
96 |
97 | + 920320 [+2/PL2] - Missing User Agent Header
98 | - 920273 [+5/PL4] - Invalid character in request (outside of very strict set)
99 | + 941100 [+5/PL1] - XSS Attack Detected via libinjection
100 | + 941110 [+5/PL1] - XSS Filter - Category 1: Script Tag Vector
101 | + 941160 [+5/PL1] - NoScript XSS InjectionChecker: HTML Injection
102 | + 941390 [+5/PL1] - Javascript method detected
103 | + 941320 [+5/PL2] - Possible XSS Attack Detected - HTML Tag Handler
104 | + 942131 [+5/PL2] - SQL Injection Attack: SQL Boolean-based attack detected
105 | - 942431 [+3/PL3] - Restricted SQL Character Anomaly Detection (args): # of special characters exceeded (6)
106 | - 942432 [+3/PL4] - Restricted SQL Character Anomaly Detection (args): # of special characters exceeded (2)
107 | + 949110 [+0/PL1] - Inbound Anomaly Score Exceeded (Total Score: 43)
108 |
109 | Total Score (from matched rules): 32
110 | ```
111 |
112 | WARNING! You can see that it prints two `Total Scores`. One (`32`) is calculated by matched rules (`+`), while the other one (`43`) is the error message from rule `949110` (which is calculated at Paranoia Level 4).
113 |
114 | If you don't want this message, you can either increase the Anomaly Score threshold via your `conf/crs-setup.conf`, or by removing rule `949110` file entirely (not advised).
115 |
116 | `modsecurity-cli` has many options, check out the `--help` for all details! *(a wiki is coming)*
117 |
118 | ```console
119 | $ python3 main.py --help
120 | ```
121 |
122 | ## TODOs
123 |
124 | This CLI wrapper is still under development, so you might not find some features that are interesting to you just yet.
125 |
126 | Here's the list of our current and future steps:
127 |
128 | - [x] ModSecurity from CLI
129 | - [x] Import all rules in a folder
130 | - [x] Support GET parameters evaluation
131 | - [x] Support Request Header evaluation
132 | - [x] Set default config to avoid matching [rule 901001](https://github.com/coreruleset/coreruleset/blob/v4.0/dev/rules/REQUEST-901-INITIALIZATION.conf#L54-L63)
133 | - [x] Score based on Paranoia Level (basic config uses PL/4, then we filter on a given PL - default: 1)
134 | - [x] Support POST request evaluation
135 | - [ ] Full URI evaluation
136 | - [ ] Create Python package
137 | - [ ] Wiki to fully document every option
138 | - [ ] Integration with [regrets](https://github.com/AvalZ/regrets)
139 | - [ ] Response evaluation (currently supports requests only)
140 |
141 | If you want to contribute by adding something from the list, PRs are welcome :sunglasses:
142 |
143 | ## Contributors
144 |
145 | - Andrea Valenza ([AvalZ](https://github.com/avalz)) - avalenza89@gmail.com
146 | - Luca Demetrio ([zangobot](https://github.com/zangobot)) - luca.demetrio@dibris.unige.it
147 |
148 |
--------------------------------------------------------------------------------
/conf/crs-setup.conf:
--------------------------------------------------------------------------------
1 | # ------------------------------------------------------------------------
2 | # OWASP ModSecurity Core Rule Set ver.4.0.0-rc2
3 | # Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved.
4 | # Copyright (c) 2021-2023 Core Rule Set project. All rights reserved.
5 | #
6 | # The OWASP ModSecurity Core Rule Set is distributed under
7 | # Apache Software License (ASL) version 2
8 | # Please see the enclosed LICENSE file for full details.
9 | # ------------------------------------------------------------------------
10 |
11 |
12 | #
13 | # -- [[ Introduction ]] --------------------------------------------------------
14 | #
15 | # The OWASP ModSecurity Core Rule Set (CRS) is a set of generic attack
16 | # detection rules that provide a base level of protection for any web
17 | # application. They are written for the open source, cross-platform
18 | # ModSecurity Web Application Firewall.
19 | #
20 | # See also:
21 | # https://coreruleset.org/
22 | # https://github.com/coreruleset/coreruleset
23 | # https://owasp.org/www-project-modsecurity-core-rule-set/
24 | #
25 |
26 |
27 | #
28 | # -- [[ System Requirements ]] -------------------------------------------------
29 | #
30 | # CRS requires ModSecurity version 2.8.0 or above.
31 | # We recommend to always use the newest ModSecurity version.
32 | #
33 | # The configuration directives/settings in this file are used to control
34 | # the OWASP ModSecurity CRS. These settings do **NOT** configure the main
35 | # ModSecurity settings (modsecurity.conf) such as SecRuleEngine,
36 | # SecRequestBodyAccess, SecAuditEngine, SecDebugLog, and XML processing.
37 | #
38 | # The CRS assumes that modsecurity.conf has been loaded. It is bundled with
39 | # ModSecurity. If you don't have it, you can get it from:
40 | # 2.x: https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v2/master/modsecurity.conf-recommended
41 | # 3.x: https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended
42 | #
43 | # The order of file inclusion in your webserver configuration should always be:
44 | # 1. modsecurity.conf
45 | # 2. crs-setup.conf (this file)
46 | # 3. rules/*.conf (the CRS rule files)
47 | #
48 | # Please refer to the INSTALL file for detailed installation instructions.
49 | #
50 |
51 |
52 | #
53 | # -- [[ Mode of Operation: Anomaly Scoring vs. Self-Contained ]] ---------------
54 | #
55 | # The CRS can run in two modes:
56 | #
57 | # -- [[ Anomaly Scoring Mode (default) ]] --
58 | # In CRS3, anomaly mode is the default and recommended mode, since it gives the
59 | # most accurate log information and offers the most flexibility in setting your
60 | # blocking policies. It is also called "collaborative detection mode".
61 | # In this mode, each matching rule increases an 'anomaly score'.
62 | # At the conclusion of the inbound rules, and again at the conclusion of the
63 | # outbound rules, the anomaly score is checked, and the blocking evaluation
64 | # rules apply a disruptive action, by default returning an error 403.
65 | #
66 | # -- [[ Self-Contained Mode ]] --
67 | # In this mode, rules apply an action instantly. This was the CRS2 default.
68 | # It can lower resource usage, at the cost of less flexibility in blocking policy
69 | # and less informative audit logs (only the first detected threat is logged).
70 | # Rules inherit the disruptive action that you specify (i.e. deny, drop, etc).
71 | # The first rule that matches will execute this action. In most cases this will
72 | # cause evaluation to stop after the first rule has matched, similar to how many
73 | # IDSs function.
74 | #
75 | # -- [[ Alert Logging Control ]] --
76 | # In the mode configuration, you must also adjust the desired logging options.
77 | # There are three common options for dealing with logging. By default CRS enables
78 | # logging to the webserver error log (or Event viewer) plus detailed logging to
79 | # the ModSecurity audit log (configured under SecAuditLog in modsecurity.conf).
80 | #
81 | # - To log to both error log and ModSecurity audit log file, use: "log,auditlog"
82 | # - To log *only* to the ModSecurity audit log file, use: "nolog,auditlog"
83 | # - To log *only* to the error log file, use: "log,noauditlog"
84 | #
85 | # Examples for the various modes follow.
86 | # You must leave one of the following options enabled.
87 | # Note that you must specify the same line for phase:1 and phase:2.
88 | #
89 |
90 | # Default: Anomaly Scoring mode, log to error log, log to ModSecurity audit log
91 | # - By default, offending requests are blocked with an error 403 response.
92 | # - To change the disruptive action, see RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example
93 | # and review section 'Changing the Disruptive Action for Anomaly Mode'.
94 | # - In Apache, you can use ErrorDocument to show a friendly error page or
95 | # perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html
96 | #
97 | SecDefaultAction "phase:1,log,auditlog,pass"
98 | SecDefaultAction "phase:2,log,auditlog,pass"
99 |
100 | # Example: Anomaly Scoring mode, log only to ModSecurity audit log
101 | # - By default, offending requests are blocked with an error 403 response.
102 | # - To change the disruptive action, see RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example
103 | # and review section 'Changing the Disruptive Action for Anomaly Mode'.
104 | # - In Apache, you can use ErrorDocument to show a friendly error page or
105 | # perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html
106 | #
107 | # SecDefaultAction "phase:1,nolog,auditlog,pass"
108 | # SecDefaultAction "phase:2,nolog,auditlog,pass"
109 |
110 | # Example: Self-contained mode, return error 403 on blocking
111 | # - In this configuration the default disruptive action becomes 'deny'. After a
112 | # rule triggers, it will stop processing the request and return an error 403.
113 | # - You can also use a different error status, such as 404, 406, et cetera.
114 | # - In Apache, you can use ErrorDocument to show a friendly error page or
115 | # perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html
116 | #
117 | # SecDefaultAction "phase:1,log,auditlog,deny,status:403"
118 | # SecDefaultAction "phase:2,log,auditlog,deny,status:403"
119 |
120 | # Example: Self-contained mode, redirect back to homepage on blocking
121 | # - In this configuration the 'tag' action includes the Host header data in the
122 | # log. This helps to identify which virtual host triggered the rule (if any).
123 | # - Note that this might cause redirect loops in some situations; for example
124 | # if a Cookie or User-Agent header is blocked, it will also be blocked when
125 | # the client subsequently tries to access the homepage. You can also redirect
126 | # to another custom URL.
127 | # SecDefaultAction "phase:1,log,auditlog,redirect:'http://%{request_headers.host}/',tag:'Host: %{request_headers.host}'"
128 | # SecDefaultAction "phase:2,log,auditlog,redirect:'http://%{request_headers.host}/',tag:'Host: %{request_headers.host}'"
129 |
130 |
131 | #
132 | # -- [[ Paranoia Level Initialization ]] ---------------------------------------
133 | #
134 | # The Paranoia Level (PL) setting allows you to choose the desired level
135 | # of rule checks that will add to your anomaly scores.
136 | #
137 | # With each paranoia level increase, the CRS enables additional rules
138 | # giving you a higher level of security. However, higher paranoia levels
139 | # also increase the possibility of blocking some legitimate traffic due to
140 | # false alarms (also named false positives or FPs). If you use higher
141 | # paranoia levels, it is likely that you will need to add some exclusion
142 | # rules for certain requests and applications receiving complex input.
143 | #
144 | # - A paranoia level of 1 is default. In this level, most core rules
145 | # are enabled. PL1 is advised for beginners, installations
146 | # covering many different sites and applications, and for setups
147 | # with standard security requirements.
148 | # At PL1 you should face FPs rarely. If you encounter FPs, please
149 | # open an issue on the CRS GitHub site and don't forget to attach your
150 | # complete Audit Log record for the request with the issue.
151 | # - Paranoia level 2 includes many extra rules, for instance enabling
152 | # many regexp-based SQL and XSS injection protections, and adding
153 | # extra keywords checked for code injections. PL2 is advised
154 | # for moderate to experienced users desiring more complete coverage
155 | # and for installations with elevated security requirements.
156 | # PL2 comes with some FPs which you need to handle.
157 | # - Paranoia level 3 enables more rules and keyword lists, and tweaks
158 | # limits on special characters used. PL3 is aimed at users experienced
159 | # at the handling of FPs and at installations with a high security
160 | # requirement.
161 | # - Paranoia level 4 further restricts special characters.
162 | # The highest level is advised for experienced users protecting
163 | # installations with very high security requirements. Running PL4 will
164 | # likely produce a very high number of FPs which have to be
165 | # treated before the site can go productive.
166 | #
167 | # All rules will log their PL to the audit log;
168 | # example: [tag "paranoia-level/2"]. This allows you to deduct from the
169 | # audit log how the WAF behavior is affected by paranoia level.
170 | #
171 | # It is important to also look into the variable
172 | # tx.enforce_bodyproc_urlencoded (Enforce Body Processor URLENCODED)
173 | # defined below. Enabling it closes a possible bypass of CRS.
174 | #
175 | # Uncomment this rule to change the default:
176 | #
177 | SecAction \
178 | "id:900000,\
179 | phase:1,\
180 | pass,\
181 | t:none,\
182 | nolog,\
183 | setvar:tx.blocking_paranoia_level=4"
184 |
185 |
186 | # It is possible to execute rules from a higher paranoia level but not include
187 | # them in the anomaly scoring. This allows you to take a well-tuned system on
188 | # paranoia level 1 and add rules from paranoia level 2 without having to fear
189 | # the new rules would lead to false positives that raise your score above the
190 | # threshold.
191 | # This optional feature is enabled by uncommenting the following rule and
192 | # setting the tx.detection_paranoia_level.
193 | # Technically, rules up to the level defined in tx.detection_paranoia_level
194 | # will be executed, but only the rules up to tx.blocking_paranoia_level affect the
195 | # anomaly scores.
196 | # By default, tx.detection_paranoia_level is set to tx.blocking_paranoia_level.
197 | # tx.detection_paranoia_level must not be lower than tx.blocking_paranoia_level.
198 | #
199 | # Please notice that setting tx.detection_paranoia_level to a higher paranoia
200 | # level results in a performance impact that is equally high as setting
201 | # tx.blocking_paranoia_level to said level.
202 | #
203 | #SecAction \
204 | # "id:900001,\
205 | # phase:1,\
206 | # pass,\
207 | # t:none,\
208 | # nolog,\
209 | # setvar:tx.detection_paranoia_level=1"
210 |
211 |
212 | #
213 | # -- [[ Enforce Body Processor URLENCODED ]] -----------------------------------
214 | #
215 | # ModSecurity selects the body processor based on the Content-Type request
216 | # header. But clients are not always setting the Content-Type header for their
217 | # request body payloads. This will leave ModSecurity with limited vision into
218 | # the payload. The variable tx.enforce_bodyproc_urlencoded lets you force the
219 | # URLENCODED body processor in these situations. This is off by default, as it
220 | # implies a change of the behaviour of ModSecurity beyond CRS (the body
221 | # processor applies to all rules, not only CRS) and because it may lead to
222 | # false positives already on paranoia level 1. However, enabling this variable
223 | # closes a possible bypass of CRS so it should be considered.
224 | #
225 | # Uncomment this rule to change the default:
226 | #
227 | #SecAction \
228 | # "id:900010,\
229 | # phase:1,\
230 | # pass,\
231 | # t:none,\
232 | # nolog,\
233 | # setvar:tx.enforce_bodyproc_urlencoded=1"
234 |
235 |
236 | #
237 | # -- [[ Anomaly Scoring Mode Severity Levels ]] --------------------------------
238 | #
239 | # Each rule in the CRS has an associated severity level.
240 | # These are the default scoring points for each severity level.
241 | # These settings will be used to increment the anomaly score if a rule matches.
242 | # You may adjust these points to your liking, but this is usually not needed.
243 | #
244 | # - CRITICAL severity: Anomaly Score of 5.
245 | # Mostly generated by the application attack rules (93x and 94x files).
246 | # - ERROR severity: Anomaly Score of 4.
247 | # Generated mostly from outbound leakage rules (95x files).
248 | # - WARNING severity: Anomaly Score of 3.
249 | # Generated mostly by malicious client rules (91x files).
250 | # - NOTICE severity: Anomaly Score of 2.
251 | # Generated mostly by the protocol rules (92x files).
252 | #
253 | # In anomaly mode, these scores are cumulative.
254 | # So it's possible for a request to hit multiple rules.
255 | #
256 | # (Note: In this file, we use 'phase:1' to set CRS configuration variables.
257 | # In general, 'phase:request' is used. However, we want to make absolutely sure
258 | # that all configuration variables are set before the CRS rules are processed.)
259 | #
260 | #SecAction \
261 | # "id:900100,\
262 | # phase:1,\
263 | # pass,\
264 | # t:none,\
265 | # nolog,\
266 | # setvar:tx.critical_anomaly_score=5,\
267 | # setvar:tx.error_anomaly_score=4,\
268 | # setvar:tx.warning_anomaly_score=3,\
269 | # setvar:tx.notice_anomaly_score=2"
270 |
271 |
272 | #
273 | # -- [[ Anomaly Scoring Mode Blocking Threshold Levels ]] ----------------------
274 | #
275 | # Here, you can specify at which cumulative anomaly score an inbound request,
276 | # or outbound response, gets blocked.
277 | #
278 | # Most detected inbound threats will give a critical score of 5.
279 | # Smaller violations, like violations of protocol/standards, carry lower scores.
280 | #
281 | # [ At default value ]
282 | # If you keep the blocking thresholds at the defaults, the CRS will work
283 | # similarly to previous CRS versions: a single critical rule match will cause
284 | # the request to be blocked and logged.
285 | #
286 | # [ Using higher values ]
287 | # If you want to make the CRS less sensitive, you can increase the blocking
288 | # thresholds, for instance to 7 (which would require multiple rule matches
289 | # before blocking) or 10 (which would require at least two critical alerts - or
290 | # a combination of many lesser alerts), or even higher. However, increasing the
291 | # thresholds might cause some attacks to bypass the CRS rules or your policies.
292 | #
293 | # [ New deployment strategy: Starting high and decreasing ]
294 | # It is a common practice to start a fresh CRS installation with elevated
295 | # anomaly scoring thresholds (>100) and then lower the limits as your
296 | # confidence in the setup grows. You may also look into the Sampling
297 | # Percentage section below for a different strategy to ease into a new
298 | # CRS installation.
299 | #
300 | # [ Anomaly Threshold / Paranoia Level Quadrant ]
301 | #
302 | # High Anomaly Limit | High Anomaly Limit
303 | # Low Paranoia Level | High Paranoia Level
304 | # -> Fresh Site | -> Experimental Site
305 | # ------------------------------------------------------
306 | # Low Anomaly Limit | Low Anomaly Limit
307 | # Low Paranoia Level | High Paranoia Level
308 | # -> Standard Site | -> High Security Site
309 | #
310 | # Uncomment this rule to change the defaults:
311 | #
312 | #SecAction \
313 | # "id:900110,\
314 | # phase:1,\
315 | # pass,\
316 | # t:none,\
317 | # nolog,\
318 | # setvar:tx.inbound_anomaly_score_threshold=5,\
319 | # setvar:tx.outbound_anomaly_score_threshold=4"
320 |
321 |
322 | #
323 | # -- [[ Application Specific Rule Exclusions ]] --------------------------------
324 | #
325 | # CRS 3.x contained exclusion packages to tweak the CRS for use with common
326 | # web applications, lowering the number of false positives.
327 | #
328 | # In CRS 4, these are no longer part of the CRS itself, but they are available
329 | # as "CRS plugins". Some plugins improve support for web applications, and others
330 | # may bring new functionality. Plugins are not installed by default, but can be
331 | # downloaded from the plugin registry:
332 | # https://github.com/coreruleset/plugin-registry
333 | #
334 | # For detailed information about using and installing plugins, please see:
335 | # https://coreruleset.org/docs/concepts/plugins/
336 |
337 |
338 | #
339 | # -- [[ Anomaly Score Reporting Level ]] ---------------------------------------
340 | #
341 | # When a request is blocked due to the anomaly score meeting or exceeding the
342 | # anomaly threshold then the blocking rule will also report the anomaly score.
343 | # This applies to the separate inbound and outbound anomaly scores.
344 | #
345 | # In phase 5, there are additional rules that can perform additional reporting
346 | # of anomaly scores with a verbosity that depends on the reporting level defined
347 | # below.
348 | #
349 | # By setting the reporting level you control whether you want additional
350 | # reporting beyond the blocking rule or not and, if yes, which requests should
351 | # be covered. The higher the reporting level, the more verbose the reporting is.
352 | #
353 | # There are 6 reporting levels:
354 | #
355 | # 0 - Reporting disabled
356 | # 1 - Reporting for requests with a blocking anomaly score >= a threshold
357 | # 2 - Reporting for requests with a detection anomaly score >= a threshold
358 | # 3 - Reporting for requests with a blocking anomaly score greater than 0
359 | # 4 - Reporting for requests with a detection anomaly score greater than 0
360 | # 5 - Reporting for all requests
361 | #
362 | # Note: Reporting levels 1 and 2 make it possible to differentiate between
363 | # requests that are blocked and requests that are *not* blocked but would have
364 | # been blocked if the blocking PL was equal to detection PL. This may be useful
365 | # for certain FP tuning methodologies, for example moving to a higher PL.
366 | #
367 | # A value of 5 can be useful on platforms where you are interested in logging
368 | # non-scoring requests, yet it is not possible to report this information in
369 | # the request/access log. This applies to Nginx, for example.
370 | #
371 | #SecAction \
372 | # "id:900115,\
373 | # phase:1,\
374 | # pass,\
375 | # t:none,\
376 | # nolog,\
377 | # setvar:tx.reporting_level=4"
378 |
379 |
380 | #
381 | # -- [[ Early Anomaly Scoring Mode Blocking ]] ------------------------------
382 | #
383 | # The anomaly scores for the request and the responses are generally summed up
384 | # and evaluated at the end of phase:2 and at the end of phase:4 respectively.
385 | # However, it is possible to enable an early evaluation of these anomaly scores
386 | # at the end of phase:1 and at the end of phase:3.
387 | #
388 | # If a request (or a response) hits the anomaly threshold in this early
389 | # evaluation, then blocking happens immediately (if blocking is enabled) and
390 | # the phase 2 (and phase 4 respectively) will no longer be executed.
391 | #
392 | # Enable the rule 900120 that sets the variable tx.early_blocking to 1 in order
393 | # to enable early blocking. The variable tx.early_blocking is set to 0 by
394 | # default. Early blocking is thus disabled by default.
395 | #
396 | # Please note that early blocking will hide potential alerts from you. This
397 | # means that a payload that would appear in an alert in phase 2 (or phase 4)
398 | # does not get evaluated if the request is being blocked early. So when you
399 | # disabled early blocking again at some point in the future, then new alerts
400 | # from phase 2 might pop up.
401 | #SecAction \
402 | # "id:900120,\
403 | # phase:1,\
404 | # pass,\
405 | # t:none,\
406 | # nolog,\
407 | # setvar:tx.early_blocking=1"
408 |
409 |
410 | #
411 | # -- [[ Initialize Default Collections ]] -----------------------------------
412 | #
413 | # CRS provides a centralized option to initialize and populate collections
414 | # meant to be used by plugins (E.g.DoS protection plugin).
415 | # By default, Global and IP collections (see rule 901320),
416 | # being not used by core rules, are not initialized.
417 | #
418 | # Uncomment this rule to change the default:
419 | #
420 | #SecAction \
421 | # "id:900130,\
422 | # phase:1,\
423 | # pass,\
424 | # t:none,\
425 | # nolog,\
426 | # setvar:tx.enable_default_collections=1"
427 |
428 |
429 | #
430 | # -- [[ HTTP Policy Settings ]] ------------------------------------------------
431 | #
432 | # This section defines your policies for the HTTP protocol, such as:
433 | # - allowed HTTP versions, HTTP methods, allowed request Content-Types
434 | # - forbidden file extensions (e.g. .bak, .sql) and request headers (e.g. Proxy)
435 | #
436 | # These variables are used in the following rule files:
437 | # - REQUEST-911-METHOD-ENFORCEMENT.conf
438 | # - REQUEST-920-PROTOCOL-ENFORCEMENT.conf
439 |
440 | # HTTP methods that a client is allowed to use.
441 | # Default: GET HEAD POST OPTIONS
442 | # Example: for RESTful APIs, add the following methods: PUT PATCH DELETE
443 | # Example: for WebDAV, add the following methods: CHECKOUT COPY DELETE LOCK
444 | # MERGE MKACTIVITY MKCOL MOVE PROPFIND PROPPATCH PUT UNLOCK
445 | # Uncomment this rule to change the default.
446 | #SecAction \
447 | # "id:900200,\
448 | # phase:1,\
449 | # pass,\
450 | # t:none,\
451 | # nolog,\
452 | # setvar:'tx.allowed_methods=GET HEAD POST OPTIONS'"
453 |
454 | # Content-Types that a client is allowed to send in a request.
455 | # Default: |application/x-www-form-urlencoded| |multipart/form-data| |multipart/related|
456 | # |text/xml| |application/xml| |application/soap+xml| |application/json|
457 | # |application/cloudevents+json| |application/cloudevents-batch+json|
458 | #
459 | # Please note, that the rule where CRS uses this variable (920420) evaluates it with operator
460 | # `@within`, which is case sensitive, but uses t:lowercase. You must add your whole custom
461 | # Content-Type with lowercase.
462 | #
463 | # Bypass Warning: some applications may not rely on the content-type request header in order
464 | # to parse the request body. This could make an attacker able to send malicious URLENCODED/JSON/XML
465 | # payloads without being detected by the WAF. Allowing request content-type that doesn't activate any
466 | # body processor (for example: "text/plain", "application/x-amf", "application/octet-stream", etc..)
467 | # could lead to a WAF bypass. For example, a malicious JSON payload submitted with a "text/plain"
468 | # content type may still be interpreted as JSON by a backend application but would not trigger the
469 | # JSON body parser at the WAF, leading to a bypass.
470 | #
471 | # To prevent blocking request with not allowed content-type by default, you can create an exclusion
472 | # rule that removes rule 920420. For example:
473 | #SecRule REQUEST_HEADERS:Content-Type "@rx ^text/plain" \
474 | # "id:1234,\
475 | # phase:1,\
476 | # pass,\
477 | # t:none,\
478 | # nolog,\
479 | # ctl:ruleRemoveById=920420,\
480 | # chain"
481 | # SecRule REQUEST_URI "@rx ^/foo/bar" \
482 | # "t:none"
483 | #
484 | # Uncomment this rule to change the default.
485 | #
486 | #SecAction \
487 | # "id:900220,\
488 | # phase:1,\
489 | # pass,\
490 | # t:none,\
491 | # nolog,\
492 | # setvar:'tx.allowed_request_content_type=|application/x-www-form-urlencoded| |multipart/form-data| |multipart/related| |text/xml| |application/xml| |application/soap+xml| |application/json| |application/cloudevents+json| |application/cloudevents-batch+json|'"
493 |
494 | # Allowed HTTP versions.
495 | # Default: HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0 HTTP/3 HTTP/3.0
496 | # Example for legacy clients: HTTP/0.9 HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0 HTTP/3 HTTP/3.0
497 | # Note that some web server versions use 'HTTP/2', some 'HTTP/2.0', so
498 | # we include both version strings by default.
499 | # Uncomment this rule to change the default.
500 | #SecAction \
501 | # "id:900230,\
502 | # phase:1,\
503 | # pass,\
504 | # t:none,\
505 | # nolog,\
506 | # setvar:'tx.allowed_http_versions=HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0 HTTP/3 HTTP/3.0'"
507 |
508 | # Forbidden file extensions.
509 | # Guards against unintended exposure of development/configuration files.
510 | # Default: .asa/ .asax/ .ascx/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .rdb/ .resources/ .resx/ .sql/ .swp/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/
511 | # Example: .bak/ .config/ .conf/ .db/ .ini/ .log/ .old/ .pass/ .pdb/ .rdb/ .sql/
512 | # Note that .axd was removed due to false positives (see PR 1925).
513 | #
514 | # To additionally guard against configuration/install archive files from being
515 | # accidentally exposed, common archive file extensions can be added to the
516 | # restricted extensions list. An example list of common archive file extensions
517 | # is presented below:
518 | # .7z/ .br/ .bz/ .bz2/ .cab/ .cpio/ .gz/ .img/ .iso/ .jar/ .rar/ .tar/ .tbz2/ .tgz/ .txz/ .xz/ .zip/ .zst/
519 | # (Source: https://en.wikipedia.org/wiki/List_of_archive_formats)
520 | #
521 | # Uncomment this rule to change the default.
522 | #SecAction \
523 | # "id:900240,\
524 | # phase:1,\
525 | # pass,\
526 | # t:none,\
527 | # nolog,\
528 | # setvar:'tx.restricted_extensions=.asa/ .asax/ .ascx/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .rdb/ .resources/ .resx/ .sql/ .swp/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/'"
529 |
530 | # Restricted request headers.
531 | # The HTTP request headers that CRS restricts are split into two categories:
532 | # basic (always forbidden) and extended (may be forbidden). All header names
533 | # should be lowercase and enclosed by /slashes/ as delimiters.
534 | #
535 | # [ Basic ]
536 | # Includes deprecated headers and headers with known security risks. Always
537 | # forbidden.
538 | # Default: /content-encoding/ /proxy/ /lock-token/ /content-range/ /if/ /x-http-method-override/ /x-http-method/ /x-method-override/
539 | #
540 | # /content-encoding/
541 | # Used to list any encodings that have been applied to the original payload.
542 | # Only used for compression, which isn't supported by CRS by default since CRS
543 | # blocks newlines and null bytes inside the request body. Most compression
544 | # algorithms require at least null bytes per RFC. Blocking Content-Encoding
545 | # shouldn't break anything and increases security since WAF engines, including
546 | # ModSecurity, are typically incapable of properly scanning compressed request
547 | # bodies.
548 | #
549 | # /proxy/
550 | # Blocking this prevents the 'httpoxy' vulnerability: https://httpoxy.org
551 | #
552 | # /lock-token/
553 | #
554 | # /content-range/
555 | #
556 | # /if/
557 | #
558 | # /x-http-method-override/
559 | # /x-http-method/
560 | # /x-method-override/
561 | # Blocking these headers prevents method override attacks, as described here:
562 | # https://www.sidechannel.blog/en/http-method-override-what-it-is-and-how-a-pentester-can-use-it
563 | #
564 | # Uncomment this rule to change the default.
565 | #SecAction \
566 | # "id:900250,\
567 | # phase:1,\
568 | # pass,\
569 | # t:none,\
570 | # nolog,\
571 | # setvar:'tx.restricted_headers_basic=/content-encoding/ /proxy/ /lock-token/ /content-range/ /if/ /x-http-method-override/ /x-http-method/ /x-method-override/'"
572 | #
573 | # [ Extended ]
574 | # Includes deprecated headers that are still in use (so false positives are
575 | # possible) and headers with possible security risks. Forbidden at a higher
576 | # paranoia level.
577 | # Default: /accept-charset/
578 | #
579 | # /accept-charset/
580 | # Deprecated header that should not be used by clients and should be ignored
581 | # by servers. Can be used for a response WAF bypass by asking for a charset
582 | # that the WAF cannot decode. Considered to be a good indicator of suspicious
583 | # behavior but produces too many false positives to be forbidden by default.
584 | # References:
585 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Charset
586 | # https://github.com/coreruleset/coreruleset/issues/3140
587 | #
588 | # Uncomment this rule to change the default.
589 | #SecAction \
590 | # "id:900255,\
591 | # phase:1,\
592 | # pass,\
593 | # t:none,\
594 | # nolog,\
595 | # setvar:'tx.restricted_headers_extended=/accept-charset/'"
596 |
597 | # Content-Types charsets that a client is allowed to send in a request.
598 | # The content-types are enclosed by |pipes| as delimiters to guarantee exact matches.
599 | # Default: |utf-8| |iso-8859-1| |iso-8859-15| |windows-1252|
600 | # Uncomment this rule to change the default.
601 | #SecAction \
602 | # "id:900280,\
603 | # phase:1,\
604 | # pass,\
605 | # t:none,\
606 | # nolog,\
607 | # setvar:'tx.allowed_request_content_type_charset=|utf-8| |iso-8859-1| |iso-8859-15| |windows-1252|'"
608 |
609 | #
610 | # -- [[ HTTP Argument/Upload Limits ]] -----------------------------------------
611 | #
612 | # Here you can define optional limits on HTTP get/post parameters and uploads.
613 | # This can help to prevent application specific DoS attacks.
614 | #
615 | # These values are checked in REQUEST-920-PROTOCOL-ENFORCEMENT.conf.
616 | # Beware of blocking legitimate traffic when enabling these limits.
617 | #
618 |
619 | # Block request if number of arguments is too high
620 | # Default: unlimited
621 | # Example: 255
622 | # Uncomment this rule to set a limit.
623 | #SecAction \
624 | # "id:900300,\
625 | # phase:1,\
626 | # pass,\
627 | # t:none,\
628 | # nolog,\
629 | # setvar:tx.max_num_args=255"
630 |
631 | # Block request if the length of any argument name is too high
632 | # Default: unlimited
633 | # Example: 100
634 | # Uncomment this rule to set a limit.
635 | #SecAction \
636 | # "id:900310,\
637 | # phase:1,\
638 | # pass,\
639 | # t:none,\
640 | # nolog,\
641 | # setvar:tx.arg_name_length=100"
642 |
643 | # Block request if the length of any argument value is too high
644 | # Default: unlimited
645 | # Example: 400
646 | # Uncomment this rule to set a limit.
647 | #SecAction \
648 | # "id:900320,\
649 | # phase:1,\
650 | # pass,\
651 | # t:none,\
652 | # nolog,\
653 | # setvar:tx.arg_length=400"
654 |
655 | # Block request if the total length of all combined arguments is too high
656 | # Default: unlimited
657 | # Example: 64000
658 | # Uncomment this rule to set a limit.
659 | #SecAction \
660 | # "id:900330,\
661 | # phase:1,\
662 | # pass,\
663 | # t:none,\
664 | # nolog,\
665 | # setvar:tx.total_arg_length=64000"
666 |
667 | # Block request if the file size of any individual uploaded file is too high
668 | # Default: unlimited
669 | # Example: 1048576
670 | # Uncomment this rule to set a limit.
671 | #SecAction \
672 | # "id:900340,\
673 | # phase:1,\
674 | # pass,\
675 | # t:none,\
676 | # nolog,\
677 | # setvar:tx.max_file_size=1048576"
678 |
679 | # Block request if the total size of all combined uploaded files is too high
680 | # Default: unlimited
681 | # Example: 1048576
682 | # Uncomment this rule to set a limit.
683 | #SecAction \
684 | # "id:900350,\
685 | # phase:1,\
686 | # pass,\
687 | # t:none,\
688 | # nolog,\
689 | # setvar:tx.combined_file_sizes=1048576"
690 |
691 |
692 | #
693 | # -- [[ Easing In / Sampling Percentage ]] -------------------------------------
694 | #
695 | # Adding the Core Rule Set to an existing productive site can lead to false
696 | # positives, unexpected performance issues and other undesired side effects.
697 | #
698 | # It can be beneficial to test the water first by enabling the CRS for a
699 | # limited number of requests only and then, when you have solved the issues (if
700 | # any) and you have confidence in the setup, to raise the ratio of requests
701 | # being sent into the ruleset.
702 | #
703 | # Adjust the percentage of requests that are funnelled into the Core Rules by
704 | # setting TX.sampling_percentage below. The default is 100, meaning that every
705 | # request gets checked by the CRS. The selection of requests, which are going
706 | # to be checked, is based on a pseudo random number generated by ModSecurity.
707 | #
708 | # If a request is allowed to pass without being checked by the CRS, there is no
709 | # entry in the audit log (for performance reasons), but an error log entry is
710 | # written. If you want to disable the error log entry, then issue the
711 | # following directive somewhere after the inclusion of the CRS
712 | # (E.g., RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf).
713 | #
714 | #SecRuleUpdateActionById 901450 "nolog"
715 | #
716 | # ATTENTION: If this TX.sampling_percentage is below 100, then some of the
717 | # requests will bypass the Core Rules completely and you lose the ability to
718 | # protect your service with ModSecurity.
719 | #
720 | # Uncomment this rule to enable this feature:
721 | #
722 | #SecAction \
723 | # "id:900400,\
724 | # phase:1,\
725 | # pass,\
726 | # nolog,\
727 | # setvar:tx.sampling_percentage=100"
728 |
729 |
730 |
731 | #
732 | # -- [[ Check UTF-8 encoding ]] ------------------------------------------------
733 | #
734 | # The CRS can optionally check request contents for invalid UTF-8 encoding.
735 | # We only want to apply this check if UTF-8 encoding is actually used by the
736 | # site; otherwise it will result in false positives.
737 | #
738 | # Uncomment this rule to use this feature:
739 | #
740 | #SecAction \
741 | # "id:900950,\
742 | # phase:1,\
743 | # pass,\
744 | # t:none,\
745 | # nolog,\
746 | # setvar:tx.crs_validate_utf8_encoding=1"
747 |
748 |
749 | #
750 | # -- [[ Collection timeout ]] --------------------------------------------------
751 | #
752 | # Set the SecCollectionTimeout directive from the ModSecurity default (1 hour)
753 | # to a lower setting which is appropriate to most sites.
754 | # This increases performance by cleaning out stale collection (block) entries.
755 | #
756 | # This value should be greater than or equal to any block durations or timeouts
757 | # set by plugins that make use of ModSecurity's persistent collections (e.g. the
758 | # DoS protection and IP reputation plugins).
759 | #
760 | # Ref: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-(v2.x)#SecCollectionTimeout
761 |
762 | # Please keep this directive uncommented.
763 | # Default: 600 (10 minutes)
764 | SecCollectionTimeout 600
765 |
766 |
767 | #
768 | # -- [[ End of setup ]] --------------------------------------------------------
769 | #
770 | # The CRS checks the tx.crs_setup_version variable to ensure that the setup
771 | # has been loaded. If you are not planning to use this setup template,
772 | # you must manually set the tx.crs_setup_version variable before including
773 | # the CRS rules/* files.
774 | #
775 | # The variable is a numerical representation of the CRS version number.
776 | # E.g., v3.0.0 is represented as 300.
777 | #
778 | SecAction \
779 | "id:900990,\
780 | phase:1,\
781 | pass,\
782 | t:none,\
783 | nolog,\
784 | setvar:tx.crs_setup_version=400"
785 |
--------------------------------------------------------------------------------
/conf/modsecurity.conf:
--------------------------------------------------------------------------------
1 | # -- Rule engine initialization ----------------------------------------------
2 |
3 | # Enable ModSecurity, attaching it to every transaction. Use detection
4 | # only to start with, because that minimises the chances of post-installation
5 | # disruption.
6 | #
7 | SecRuleEngine DetectionOnly
8 |
9 |
10 | # -- Request body handling ---------------------------------------------------
11 |
12 | # Allow ModSecurity to access request bodies. If you don't, ModSecurity
13 | # won't be able to see any POST parameters, which opens a large security
14 | # hole for attackers to exploit.
15 | #
16 | SecRequestBodyAccess On
17 |
18 |
19 | # Enable XML request body parser.
20 | # Initiate XML Processor in case of xml content-type
21 | #
22 | SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap\+|/)|text/)xml" \
23 | "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
24 |
25 | # Enable JSON request body parser.
26 | # Initiate JSON Processor in case of JSON content-type; change accordingly
27 | # if your application does not use 'application/json'
28 | #
29 | SecRule REQUEST_HEADERS:Content-Type "^application/json" \
30 | "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
31 |
32 | # Sample rule to enable JSON request body parser for more subtypes.
33 | # Uncomment or adapt this rule if you want to engage the JSON
34 | # Processor for "+json" subtypes
35 | #
36 | #SecRule REQUEST_HEADERS:Content-Type "^application/[a-z0-9.-]+[+]json" \
37 | # "id:'200006',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
38 |
39 | # Maximum request body size we will accept for buffering. If you support
40 | # file uploads then the value given on the first line has to be as large
41 | # as the largest file you are willing to accept. The second value refers
42 | # to the size of data, with files excluded. You want to keep that value as
43 | # low as practical.
44 | #
45 | SecRequestBodyLimit 13107200
46 | SecRequestBodyNoFilesLimit 131072
47 |
48 | # What to do if the request body size is above our configured limit.
49 | # Keep in mind that this setting will automatically be set to ProcessPartial
50 | # when SecRuleEngine is set to DetectionOnly mode in order to minimize
51 | # disruptions when initially deploying ModSecurity.
52 | #
53 | SecRequestBodyLimitAction Reject
54 |
55 | # Maximum parsing depth allowed for JSON objects. You want to keep this
56 | # value as low as practical.
57 | #
58 | SecRequestBodyJsonDepthLimit 512
59 |
60 | # Maximum number of args allowed per request. You want to keep this
61 | # value as low as practical. The value should match that in rule 200007.
62 | SecArgumentsLimit 1000
63 |
64 | # If SecArgumentsLimit has been set, you probably want to reject any
65 | # request body that has only been partly parsed. The value used in this
66 | # rule should match what was used with SecArgumentsLimit
67 | SecRule &ARGS "@ge 1000" \
68 | "id:'200007', phase:2,t:none,log,deny,status:400,msg:'Failed to fully parse request body due to large argument count',severity:2"
69 |
70 | # Verify that we've correctly processed the request body.
71 | # As a rule of thumb, when failing to process a request body
72 | # you should reject the request (when deployed in blocking mode)
73 | # or log a high-severity alert (when deployed in detection-only mode).
74 | #
75 | SecRule REQBODY_ERROR "!@eq 0" \
76 | "id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
77 |
78 | # By default be strict with what we accept in the multipart/form-data
79 | # request body. If the rule below proves to be too strict for your
80 | # environment consider changing it to detection-only. You are encouraged
81 | # _not_ to remove it altogether.
82 | #
83 | SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
84 | "id:'200003',phase:2,t:none,log,deny,status:400, \
85 | msg:'Multipart request body failed strict validation: \
86 | PE %{REQBODY_PROCESSOR_ERROR}, \
87 | BQ %{MULTIPART_BOUNDARY_QUOTED}, \
88 | BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
89 | DB %{MULTIPART_DATA_BEFORE}, \
90 | DA %{MULTIPART_DATA_AFTER}, \
91 | HF %{MULTIPART_HEADER_FOLDING}, \
92 | LF %{MULTIPART_LF_LINE}, \
93 | SM %{MULTIPART_MISSING_SEMICOLON}, \
94 | IQ %{MULTIPART_INVALID_QUOTING}, \
95 | IP %{MULTIPART_INVALID_PART}, \
96 | IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
97 | FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
98 |
99 | # Did we see anything that might be a boundary?
100 | #
101 | # Here is a short description about the ModSecurity Multipart parser: the
102 | # parser returns with value 0, if all "boundary-like" line matches with
103 | # the boundary string which given in MIME header. In any other cases it returns
104 | # with different value, eg. 1 or 2.
105 | #
106 | # The RFC 1341 descript the multipart content-type and its syntax must contains
107 | # only three mandatory lines (above the content):
108 | # * Content-Type: multipart/mixed; boundary=BOUNDARY_STRING
109 | # * --BOUNDARY_STRING
110 | # * --BOUNDARY_STRING--
111 | #
112 | # First line indicates, that this is a multipart content, second shows that
113 | # here starts a part of the multipart content, third shows the end of content.
114 | #
115 | # If there are any other lines, which starts with "--", then it should be
116 | # another boundary id - or not.
117 | #
118 | # After 3.0.3, there are two kinds of types of boundary errors: strict and permissive.
119 | #
120 | # If multipart content contains the three necessary lines with correct order, but
121 | # there are one or more lines with "--", then parser returns with value 2 (non-zero).
122 | #
123 | # If some of the necessary lines (usually the start or end) misses, or the order
124 | # is wrong, then parser returns with value 1 (also a non-zero).
125 | #
126 | # You can choose, which one is what you need. The example below contains the
127 | # 'strict' mode, which means if there are any lines with start of "--", then
128 | # ModSecurity blocked the content. But the next, commented example contains
129 | # the 'permissive' mode, then you check only if the necessary lines exists in
130 | # correct order. Whit this, you can enable to upload PEM files (eg "----BEGIN.."),
131 | # or other text files, which contains eg. HTTP headers.
132 | #
133 | # The difference is only the operator - in strict mode (first) the content blocked
134 | # in case of any non-zero value. In permissive mode (second, commented) the
135 | # content blocked only if the value is explicit 1. If it 0 or 2, the content will
136 | # allowed.
137 | #
138 |
139 | #
140 | # See #1747 and #1924 for further information on the possible values for
141 | # MULTIPART_UNMATCHED_BOUNDARY.
142 | #
143 | SecRule MULTIPART_UNMATCHED_BOUNDARY "@eq 1" \
144 | "id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"
145 |
146 |
147 | # PCRE Tuning
148 | # We want to avoid a potential RegEx DoS condition
149 | #
150 | SecPcreMatchLimit 1000
151 | SecPcreMatchLimitRecursion 1000
152 |
153 | # Some internal errors will set flags in TX and we will need to look for these.
154 | # All of these are prefixed with "MSC_". The following flags currently exist:
155 | #
156 | # MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded.
157 | #
158 | SecRule TX:/^MSC_/ "!@streq 0" \
159 | "id:'200005',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
160 |
161 |
162 | # -- Response body handling --------------------------------------------------
163 |
164 | # Allow ModSecurity to access response bodies.
165 | # You should have this directive enabled in order to identify errors
166 | # and data leakage issues.
167 | #
168 | # Do keep in mind that enabling this directive does increases both
169 | # memory consumption and response latency.
170 | #
171 | SecResponseBodyAccess On
172 |
173 | # Which response MIME types do you want to inspect? You should adjust the
174 | # configuration below to catch documents but avoid static files
175 | # (e.g., images and archives).
176 | #
177 | SecResponseBodyMimeType text/plain text/html text/xml
178 |
179 | # Buffer response bodies of up to 512 KB in length.
180 | SecResponseBodyLimit 524288
181 |
182 | # What happens when we encounter a response body larger than the configured
183 | # limit? By default, we process what we have and let the rest through.
184 | # That's somewhat less secure, but does not break any legitimate pages.
185 | #
186 | SecResponseBodyLimitAction ProcessPartial
187 |
188 |
189 | # -- Filesystem configuration ------------------------------------------------
190 |
191 | # The location where ModSecurity stores temporary files (for example, when
192 | # it needs to handle a file upload that is larger than the configured limit).
193 | #
194 | # This default setting is chosen due to all systems have /tmp available however,
195 | # this is less than ideal. It is recommended that you specify a location that's private.
196 | #
197 | SecTmpDir /tmp/
198 |
199 | # The location where ModSecurity will keep its persistent data. This default setting
200 | # is chosen due to all systems have /tmp available however, it
201 | # too should be updated to a place that other users can't access.
202 | #
203 | SecDataDir /tmp/
204 |
205 |
206 | # -- File uploads handling configuration -------------------------------------
207 |
208 | # The location where ModSecurity stores intercepted uploaded files. This
209 | # location must be private to ModSecurity. You don't want other users on
210 | # the server to access the files, do you?
211 | #
212 | #SecUploadDir /opt/modsecurity/var/upload/
213 |
214 | # By default, only keep the files that were determined to be unusual
215 | # in some way (by an external inspection script). For this to work you
216 | # will also need at least one file inspection rule.
217 | #
218 | #SecUploadKeepFiles RelevantOnly
219 |
220 | # Uploaded files are by default created with permissions that do not allow
221 | # any other user to access them. You may need to relax that if you want to
222 | # interface ModSecurity to an external program (e.g., an anti-virus).
223 | #
224 | #SecUploadFileMode 0600
225 |
226 |
227 | # -- Debug log configuration -------------------------------------------------
228 |
229 | # The default debug log configuration is to duplicate the error, warning
230 | # and notice messages from the error log.
231 | #
232 | #SecDebugLog /opt/modsecurity/var/log/debug.log
233 | #SecDebugLogLevel 3
234 |
235 |
236 | # -- Audit log configuration -------------------------------------------------
237 |
238 | # Log the transactions that are marked by a rule, as well as those that
239 | # trigger a server error (determined by a 5xx or 4xx, excluding 404,
240 | # level response status codes).
241 | #
242 | SecAuditEngine RelevantOnly
243 | SecAuditLogRelevantStatus "^(?:5|4(?!04))"
244 |
245 | # Log everything we know about a transaction.
246 | SecAuditLogParts ABIJDEFHZ
247 |
248 | # Use a single file for logging. This is much easier to look at, but
249 | # assumes that you will use the audit log only ocassionally.
250 | #
251 | SecAuditLogType Serial
252 | SecAuditLog /var/log/modsec_audit.log
253 |
254 | # Specify the path for concurrent audit logging.
255 | #SecAuditLogStorageDir /opt/modsecurity/var/audit/
256 |
257 |
258 | # -- Miscellaneous -----------------------------------------------------------
259 |
260 | # Use the most commonly used application/x-www-form-urlencoded parameter
261 | # separator. There's probably only one application somewhere that uses
262 | # something else so don't expect to change this value.
263 | #
264 | SecArgumentSeparator &
265 |
266 | # Settle on version 0 (zero) cookies, as that is what most applications
267 | # use. Using an incorrect cookie version may open your installation to
268 | # evasion attacks (against the rules that examine named cookies).
269 | #
270 | SecCookieFormat 0
271 |
272 | # Specify your Unicode Code Point.
273 | # This mapping is used by the t:urlDecodeUni transformation function
274 | # to properly map encoded data to your language. Properly setting
275 | # these directives helps to reduce false positives and negatives.
276 | #
277 | SecUnicodeMapFile unicode.mapping 20127
278 |
279 | # Improve the quality of ModSecurity by sharing information about your
280 | # current ModSecurity version and dependencies versions.
281 | # The following information will be shared: ModSecurity version,
282 | # Web Server version, APR version, PCRE version, Lua version, Libxml2
283 | # version, Anonymous unique id for host.
284 | SecStatusEngine On
285 |
286 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | from ModSecurity import ModSecurity
2 | from ModSecurity import RulesSet
3 | from ModSecurity import Transaction
4 | from ModSecurity import LogProperty
5 |
6 | import re
7 | import glob
8 | from urllib.parse import urlparse, urlencode
9 | import typer
10 | from typing import List, Optional
11 | from typing_extensions import Annotated
12 | from enum import Enum
13 |
14 | app = typer.Typer()
15 |
16 | class Severity(Enum):
17 |
18 | def __new__(cls, *args, **kwds):
19 | value = len(cls.__members__)
20 | obj = object.__new__(cls)
21 | obj._value_ = value
22 | return obj
23 | def __init__(self, severity_id, score):
24 | self.id = severity_id
25 | self.score = score
26 |
27 | EMERGENCY = 0, 0 # not used in CRS
28 | ALERT = 1, 0 # not used in CRS
29 | CRITICAL = 2, 5
30 | ERROR = 3, 4
31 | WARNING = 4, 3
32 | NOTICE = 5, 2
33 | INFO = 6, 0 # not used in CRS
34 | DEBUG = 7, 0 # not used in CRS
35 |
36 | def get_paranoia_level(rule):
37 | return next((int(tag.split('/')[1]) for tag in rule.m_tags if 'paranoia-level' in tag), 1)
38 |
39 |
40 | def version(value: bool):
41 | if value:
42 | modsec = ModSecurity()
43 | print(modsec.whoAmI())
44 | exit()
45 |
46 |
47 | @app.command()
48 | def parameter(
49 | payloads: Annotated[List[str], typer.Argument()],
50 | keys: Annotated[List[str], typer.Option('-k', '--key', help="List of key for parameters (must match the number of payloads)")] = [],
51 | request_body: Annotated[typer.FileBinaryRead, typer.Option(help="Request Body file")] = None,
52 | base_uri: Annotated[str, typer.Option(help="Base URI for payload evaluation")] = "http://www.modsecurity.org/test",
53 | method: Annotated[str, typer.Option(help="Method")] = "",
54 | headers: Annotated[List[str], typer.Option('-H', '--header', help="List of headers")] = [],
55 | paranoia_level: Annotated[int, typer.Option('-PL', '--paranoia-level', help="Paranoia Level")] = 1,
56 | configs: Annotated[List[str], typer.Option('--config', help="List of additional configuration files (loaded BEFORE rules")] = ['conf/modsecurity.conf', 'conf/crs-setup.conf'],
57 | rules_path: Annotated[str, typer.Option('--rules', help="Rules location")] = 'coreruleset/rules',
58 | verbose: Annotated[bool, typer.Option('-v', '--verbose', help="Print matched rules with associated scores")] = False,
59 | version: Annotated[Optional[bool], typer.Option('-V', '--version', help="Print current ModSecurity version", callback=version)] = None,
60 | logs: Annotated[bool, typer.Option(help="Print libmodsecurity server logs")] = False):
61 | modsec = ModSecurity()
62 |
63 | if not logs:
64 | # disable ModSecurity callback logs
65 | modsec.setServerLogCb2(lambda x, y: None, LogProperty.RuleMessageLogProperty)
66 |
67 | if not method:
68 | method = 'POST' if request_body else 'GET'
69 |
70 | if not keys:
71 | keys = ['q']
72 |
73 | encoded_query = urlencode(dict(zip(keys, payloads)))
74 | full_url = f"{base_uri}?{encoded_query}"
75 | parsed_url = urlparse(full_url)
76 |
77 | rules = RulesSet()
78 |
79 | # Load basic conf
80 | for config in configs:
81 | rules.loadFromUri(config)
82 |
83 | # Load rules
84 | for rule_path in sorted(glob.glob(f"{rules_path}/*.conf")):
85 | # Unsorted rules cause unexpcted behaviors for SETVAR
86 | rules.loadFromUri(rule_path)
87 |
88 | transaction = Transaction(modsec, rules)
89 |
90 | # URI
91 | if verbose:
92 | print(method, full_url)
93 | transaction.processURI(full_url, method, "2.0")
94 |
95 | # Headers
96 | headers.append(f"Host: {parsed_url.netloc}") # Avoid matching rule 920280
97 | for header in headers:
98 | name, value = header.split(':')
99 | transaction.addRequestHeader(name, value.strip()) # Avoid matching rule 920280
100 | transaction.processRequestHeaders()
101 |
102 |
103 | # Body
104 | if request_body:
105 | body = request_body.read().decode('utf-8')
106 | transaction.appendRequestBody(body)
107 | print(body)
108 | transaction.processRequestBody()
109 |
110 | # Decorate RuleMessages
111 | for rule in transaction.m_rulesMessages:
112 | rule.m_severity = Severity(rule.m_severity).score
113 |
114 | if verbose:
115 | print()
116 | print("# Matched rules")
117 | print()
118 | for rule in transaction.m_rulesMessages:
119 | print(' + ' if get_paranoia_level(rule) <= paranoia_level else ' - ', end='')
120 | print(f" {rule.m_ruleId} [+{rule.m_severity}/PL{get_paranoia_level(rule)}] - {rule.m_message}")
121 |
122 | if verbose:
123 | print("\nTotal Score (from matched rules): ", end="")
124 |
125 | total_score = sum([ rule.m_severity for rule in transaction.m_rulesMessages if get_paranoia_level(rule) <= paranoia_level])
126 | print(total_score)
127 |
128 |
129 | if __name__ == "__main__":
130 | app()
131 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pymodsecurity==0.1.0
2 | typer
3 |
--------------------------------------------------------------------------------