Skip to content

Quotation Mark Formatting in ModSecurity Logs: Is the Use of Backticks and Single Quotes Correct? #3369

Open
@wRkA

Description

@wRkA

Describe the bug

The ModSecurity log entries show operators and parameters enclosed in backticks (`), while variable names and data values are enclosed in single quotes ('). For example:

  • `PmFromFile'
  • `lfi-os-files.data'
  • `ARGS:bar'
  • `/bin/sh'

and so on...

Wouldn't it have to be?

  • 'PmFromFile'
  • 'lfi-os-files.data'
  • 'ARGS:bar'
  • '/bin/sh'

Logs and dumps

Output of: AuditLogs

---ABgEnRQg---H--
ModSecurity: Warning. Matched "Operator `PmFromFile' with parameter `lfi-os-files.data' against variable `ARGS:foo' (Value: `/etc/passwd' ) [file "/usr/share/owasp-modsecurity-crs/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf"] [line "99"] [id "930120"] [rev ""] [msg "OS File Access Attempt"] [data "Matched Data: etc/passwd found within ARGS:foo: /etc/passwd"] [severity "2"] [ver "OWASP_CRS/4.14.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-      multi"] [tag "platform-multi"] [tag "attack-lfi"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "OWASP_CRS/ATTACK-LFI"] [tag "capec/1000/255/153/126"] [tag "PCI/6.5.4"] [hostname "192.168.1.35"] [uri "/"] [unique_id "174622258411.        325032"] [ref "o1,10v10,11t:utf8toUnicode,t:urlDecodeUni,t:normalizePathWin"]
ModSecurity: Warning. Matched "Operator `Rx' with parameter `(?i)(?:^|b[\"'\)\[\x5c]*(?:(?:(?:\|\||&&)[\s\x0b]*)?\$[!#\(\*\-0-9\?@_a-\{]*)?\x5c?u[\"'\)\[\x5c]*(?:(?:(?:\|\||&&)[\s\x0b]*)?\$[!#\(\*\-0-9\?@_a-\{]*)?\x5c?s[\"'\)\[\x5c]*(?:(?: (?:\|\||&&)[\s\x0b]*)? (7133 characters omitted)' against variable `ARGS:bar' (Value: `/bin/sh' ) [file "/usr/share/owasp-modsecurity-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf"] [line "467"] [id "932250"] [rev ""] [msg "Remote      Command Execution: Direct Unix Command Execution"] [data "Matched Data: /bin/sh found within ARGS:bar: /bin/sh"] [severity "2"] [ver "OWASP_CRS/4.14.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-shell"] [tag    "platform-unix"] [tag "attack-rce"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "OWASP_CRS/ATTACK-RCE"] [tag "capec/1000/152/248/88"] [tag "PCI/6.5.2"] [hostname "192.168.1.35"] [uri "/"] [unique_id "174622258411.325032"] [ref "o0,7v26,7"]
ModSecurity: Warning. Matched "Operator `PmFromFile' with parameter `unix-shell.data' against variable `ARGS:bar' (Value: `/bin/sh' ) [file "/usr/share/owasp-modsecurity-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf"] [line "606"] [id  "932160"] [rev ""] [msg "Remote Command Execution: Unix Shell Code Found"] [data "Matched Data: bin/sh found within ARGS:bar: /bin/sh"] [severity "2"] [ver "OWASP_CRS/4.14.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag    "language-shell"] [tag "platform-unix"] [tag "attack-rce"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "OWASP_CRS/ATTACK-RCE"] [tag "capec/1000/152/248/88"] [tag "PCI/6.5.2"] [hostname "192.168.1.35"] [uri "/"] [unique_id "174622258411.325032"] [ref "o1,10v10,11t:cmdLine,t:normalizePatho1,6v26,7t:cmdLine,t:normalizePath"]
ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:BLOCKING_INBOUND_ANOMALY_SCORE' (Value: `20' ) [file "/usr/share/owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "222"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 20)"] [data ""] [severity "0"] [ver "OWASP_CRS/4.14.0"] [maturity "0"] [accuracy "0"] [tag "anomaly-evaluation"] [tag "OWASP_CRS"] [hostname "192.168.1.35"] [uri "/"] [unique_id "174622258411.325032"] [ref ""]

To Reproduce

  • None

Expected behavior

  • To know if this behavior is correct.

Server (please complete the following information):

  • ModSecurity v3.0.14 with nginx-connector v1.0.3
  • WebServer: nginx/1.28.0
  • OS: Ubuntu 24.04.2 LTS

Rule Set (please complete the following information):

  • OWASP CRS v4.14.0

Additional context

None, just see logs.

Activity

added
3.xRelated to ModSecurity version 3.x
on May 2, 2025
airween

airween commented on May 3, 2025

@airween
Member

Hi @wRkA,

thanks for asking this.

I don't know if it's correct or not, but this is the expected behavior :).

If you check the code, the engine produces this message here or here, and the syntax (that you mentioned: the quote marks are ` and '.

I'm sure there is a reason why the author of that code has made this decision, but I think it wouldn't be a good idea to replace it now, because many user made their own logparser, and - perhaps - this change would brake them.

airween

airween commented on Jun 13, 2025

@airween
Member

@wRkA,

is there anything that we can do here?

wRkA

wRkA commented on Jun 17, 2025

@wRkA
Author

hi @airween

I will try to break down the problem in detail and look for an approach to a real solution. I want to mention that, in the end, these are just ideas; I could be wrong, but we will try to do our best.

Before starting, let's redo a brief summary of the understanding of the log.

Log Entry Structure

A segment begins with a boundary and ends when the next segment begins. The only exception is the terminating segment (Z), which consists only of the boundary. The idea be hind the use of multiple segments is to allow each audit log entry to contain potentially different information. Only parts A and Z are mandatory; the use of the other parts is controlled with the SecAuditLogParts directive. The next contains the list of all audit log parts, along with a description of their purpose.

Segment Description
A Audit log header (mandatory)
B Request headers
C Request body
D Reserved
E Response body
F Response headers
G Reserved
H Audit log trailer, which contains additional data
I Reduced multipart request body, which excludes files (alternative to part C)
J Information on uploaded files (multipart requests)
K Contains a list of all rules that matched for the transaction
Z Final boundary (mandatory)

Example to use: for context

---RmHz2VyZ---A--
[30/Apr/2025:13:29:57 -0600] 174604139747.236308 192.168.1.31 52382 192.168.1.35 80
---RmHz2VyZ---B--
GET /?q=%3Cscript%3Ealert(1)%3C/script%3E HTTP/1.1
Host: test.zombies.sh
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9

---RmHz2VyZ---D--

---RmHz2VyZ---E--
<html>\x0d\x0a<head><title>403 Forbidden</title></head>\x0d\x0a<body>\x0d\x0a<center><h1>403 Forbidden</h1></center>\x0d\x0a<hr><center>nginx</center>\x0d\x0a</body>\x0d\x0a</html>\x0d\x0a<!-- a padding to disable MSIE and Chrome friendly error page -->\x0d\x0a<!-- a padding to disable MSIE and Chrome friendly error page -->\x0d\x0a<!-- a padding to disable MSIE and Chrome friendly error page -->\x0d\x0a<!-- a padding to disable MSIE and Chrome friendly error page -->\x0d\x0a<!-- a padding to disable MSIE and Chrome friendly error page -->\x0d\x0a<!-- a padding to disable MSIE and Chrome friendly error page -->\x0d\x0a

---RmHz2VyZ---F--
HTTP/1.1 403
Server: nginx
Date: Wed, 30 Apr 2025 19:29:57 GMT
Content-Length: 548
Content-Type: text/html
Connection: keep-alive

---RmHz2VyZ---H--
ModSecurity: Warning. detected XSS using libinjection. [file "/usr/share/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf"] [line "83"] [id "941100"] [rev ""] [msg "XSS Attack Detected via libinjection"] [data "Matched Data: XSS data found within ARGS:q: <script>alert(1)</script>"] [severity "2"] [ver "OWASP_CRS/4.13.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-xss"] [tag "xss-perf-disable"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "OWASP_CRS/ATTACK-XSS"] [tag "capec/1000/152/242"] [hostname "192.168.1.35"] [uri "/"] [unique_id "174604139747.236308"] [ref "v8,25t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls"]
ModSecurity: Warning. Matched "Operator `Rx' with parameter `(?i)<script[^>]*>[\s\S]*?' against variable `ARGS:q' (Value: `<script>alert(1)</script>' ) [file "/usr/share/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf"] [line "110"] [id "941110"] [rev ""] [msg "XSS Filter - Category 1: Script Tag Vector"] [data "Matched Data: <script> found within ARGS:q: <script>alert(1)</script>"] [severity "2"] [ver "OWASP_CRS/4.13.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-xss"] [tag "xss-perf-disable"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "OWASP_CRS/ATTACK-XSS"] [tag "capec/1000/152/242"] [hostname "192.168.1.35"] [uri "/"] [unique_id "174604139747.236308"] [ref "o0,8v8,25t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls"]
ModSecurity: Warning. Matched "Operator `Rx' with parameter `(?i)<[^0-9<>A-Z_a-z]*(?:[^\s\x0b\"'<>]*:)?[^0-9<>A-Z_a-z]*[^0-9A-Z_a-z]*?(?:s[^0-9A-Z_a-z]*?(?:c[^0-9A-Z_a-z]*?r[^0-9A-Z_a-z]*?i[^0-9A-Z_a-z]*?p[^0-9A-Z_a-z]*?t|t[^0-9A-Z_a-z]*?y[^0-9A-Z_a-z]*?l[^0-9A (4378 characters omitted)' against variable `ARGS:q' (Value: `<script>alert(1)</script>' ) [file "/usr/share/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf"] [line "205"] [id "941160"] [rev ""] [msg "NoScript XSS InjectionChecker: HTML Injection"] [data "Matched Data: <script found within ARGS:q: <script>alert(1)</script>"] [severity "2"] [ver "OWASP_CRS/4.13.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-xss"] [tag "xss-perf-disable"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "OWASP_CRS/ATTACK-XSS"] [tag "capec/1000/152/242"] [hostname "192.168.1.35"] [uri "/"] [unique_id "174604139747.236308"] [ref "o0,7v8,25t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls"]
ModSecurity: Warning. Matched "Operator `Rx' with parameter `(?i)\b(?:eval|set(?:timeout|interval)|new[\s\x0b]+Function|a(?:lert|tob)|btoa|prompt|confirm)[\s\x0b]*\(' against variable `ARGS:q' (Value: `<script>alert(1)</script>' ) [file "/usr/share/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf"] [line "739"] [id "941390"] [rev ""] [msg "Javascript method detected"] [data "Matched Data: alert( found within ARGS:q: <script>alert(1)</script>"] [severity "2"] [ver "OWASP_CRS/4.13.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "attack-xss"] [tag "xss-perf-disable"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "OWASP_CRS/ATTACK-XSS"] [tag "capec/1000/152/242"] [hostname "192.168.1.35"] [uri "/"] [unique_id "174604139747.236308"] [ref "o8,6v8,25t:htmlEntityDecode,t:jsDecode"]
ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:BLOCKING_INBOUND_ANOMALY_SCORE' (Value: `20' ) [file "/usr/share/owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "222"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 20)"] [data ""] [severity "0"] [ver "OWASP_CRS/4.13.0"] [maturity "0"] [accuracy "0"] [tag "anomaly-evaluation"] [tag "OWASP_CRS"] [hostname "192.168.1.35"] [uri "/"] [unique_id "174604139747.236308"] [ref ""]

---RmHz2VyZ---I--

---RmHz2VyZ---J--

---RmHz2VyZ---Z--

The "problem"

In section H, it has been found that some parameters appear to start with a backtick (`) and end with a single quote ('), but this raises some questions for us.

  • Is it intentional to use a backtick (`) as the opening delimiter and a single quote (') as the closing delimiter for strings in the logs?
  • If it is not intentional, what is the reason or cause of this inconsistent mix of delimiters?
  • Is there any technical or functional justification for the backtick to prevail in delimitation within the log format?
  • How does this inconsistency affect custom log parsers developed by the community?
  • Is there any specific pattern or context (types of rules, attacks, parameters) where this inconsistent delimitation occurs more frequently?
  • Are there previous reports or open issues related to this problem in the ModSecurity community?

To try to understand this behavior, we will analyze segment H, specifically lines 1, 2, and 5, which differ from each other, or at least in the expected message.

Line 1

ModSecurity: Warning. detected XSS using libinjection.
[file "/usr/share/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf"]
[line "83"]
[id "941100"]
[rev ""]
[msg "XSS Attack Detected via libinjection"]
[data "Matched Data: XSS data found within ARGS:q: <script>alert(1)</script>"]
[severity "2"]
[ver "OWASP_CRS/4.13.0"]
[maturity "0"]
[accuracy "0"]
[tag "application-multi"]
[tag "language-multi"]
[tag "platform-multi"]
[tag "attack-xss"]
[tag "xss-perf-disable"]
[tag "paranoia-level/1"]
[tag "OWASP_CRS"]
[tag "OWASP_CRS/ATTACK-XSS"]
[tag "capec/1000/152/242"]
[hostname "192.168.1.35"]
[uri "/"]
[unique_id "174604139747.236308"]
[ref "v8,25t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls"]

As we can see, the message on line 1 does not start with a Matched "Operator, but rather jumps directly to the values related to the rule and the attack type.

The detected attack type is XSS, but why are there no operators here? Is it simply the first warning? In this same line, we already find the msg value with the content "XSS Attack Detected via libinjection". As you can see, although the message is almost similar, it can be more specific. Is there anything wrong with this? No, because everything seems to match well, from the message ModSecurity: Warning. detected XSS using libinjection. to the last ref value.

In the next line, we will see the inconsistencies.

Line 2

ModSecurity: Warning. Matched "Operator `Rx' # Comment: In this part ("Operator) starts with (")
with parameter `(?i)<script[^>]*>[\s\S]*?'
against variable `ARGS:q'
(Value: `<script>alert(1)</script>' ) # Comment: Missing ("), is it intentional? There is no proper closing.

[file "/usr/share/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf"]
[line "110"]
[id "941110"]
[rev ""]
[msg "XSS Filter - Category 1: Script Tag Vector"]
[data "Matched Data: <script> found within ARGS:q: <script>alert(1)</script>"]
[severity "2"]
[ver "OWASP_CRS/4.13.0"]
[maturity "0"]
[accuracy "0"]
[tag "application-multi"]
[tag "language-multi"]
[tag "platform-multi"]
[tag "attack-xss"]
[tag "xss-perf-disable"]
[tag "paranoia-level/1"]
[tag "OWASP_CRS"]
[tag "OWASP_CRS/ATTACK-XSS"]
[tag "capec/1000/152/242"]
[hostname "192.168.1.35"]
[uri "/"]
[unique_id "174604139747.236308"]
[ref "o0,8v8,25t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls"]

Here comes the interesting part of the log: the line begins with an initial string of ModSecurity: Warning. Matched "Operator `Rx'. As you can see, the Matched "Operator at the start of the line represents an inconsistency, or at least it seems so, since the message starts with "Operator `Rx', beginning with " and ending with '. This is where the previous questions arise. Also, the "Operator starts with " but does not have a corresponding closing. Is this intentional as well?

Line 5

ModSecurity: Access denied with code 403 (phase 2).
Matched "Operator `Ge'
with parameter `5'
against variable `TX:BLOCKING_INBOUND_ANOMALY_SCORE'
(Value: `20' )

[file "/usr/share/owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"]
[line "222"]
[id "949110"]
[rev ""]
[msg "Inbound Anomaly Score Exceeded (Total Score: 20)"]
[data ""]
[severity "0"]
[ver "OWASP_CRS/4.13.0"]
[maturity "0"]
[accuracy "0"]
[tag "anomaly-evaluation"]
[tag "OWASP_CRS"]
[hostname "192.168.1.35"]
[uri "/"]
[unique_id "174604139747.236308"]
[ref ""]

It starts with a message indicating that the response was generated by the server, in this case 403. The pattern repeats: in the "Operator `Ge' it begins with (") and at the end of (Value: `20' ) there is no closing ("). Is this behavior normal? Is it intentional? The following values remain consistent with each other.

Is it necessary to normalize these inconsistencies? And are they really inconsistencies?

Custom log parsers

So, how does a custom log parser work?

Let's review some points before starting.

Initial structure

We are going to emulate a very basic custom log parser. For this parser, we will rely on simple concepts that will help us normalize log lines with inconsistencies.

Without going into technical details, we will implement the following functions:

  1. Validations
  2. Log size & Total count of records in the log
  3. Display the last records of the log
  4. View detailed information of a specific log record

With these five functions, we can explore a very basic log parser.

Validations

To validate our log file, in this case "audit.log", we will perform the following validations:

  • Validate the file name: This function checks that the file name is correct and safe to use. It verifies that the name is not empty, not too long, and does not contain reserved or dangerous words. Additionally, it ensures the name does not have strange characters or symbols (*?|><\"\\`'&;()!~#%+=) that could cause security issues, such as trying to access other folders or execute commands. If the name meets all these rules, it is considered valid; otherwise, it is marked as invalid to protect the system.
  • Validate if the file exists: This function checks if a file exists on the system by directly querying the file system. First, it ensures the file name is not null, then checks if it can obtain information about that file without following symbolic links. If the file is present, the function confirms its existence; if not, it indicates it was not found.
  • Validate permissions: This function checks if the current process has permission to read a specific file. It tries to open the file in read mode; if it cannot because of lack of permission or the file name is null, it returns an error. If it manages to open it, it closes it immediately and confirms that read access is allowed.
  • Check if it is not empty: This function checks if a file is empty by reviewing its size. If the file size is zero, it is considered empty; if it has content, it indicates that it is not empty.
  • Check if the file is too large: This function verifies if a file exceeds a maximum allowed size. It checks the file size and, if it is greater than the established limit, marks it as too large. If it is within the limit, it indicates that the size is appropriate.
  • Check that the file is not binary: This function checks if a file contains binary data. To do this, it opens the file and reads a limited number of bytes at the beginning, looking for characters that are not readable text, such as invisible symbols or null bytes, which usually indicate binary content. If it finds this type of data, it classifies the file as binary.
  • Check if it has a valid format: This function verifies if a file meets the expected log format. For this, it opens the file and analyzes a limited number of lines looking for special markers that indicate the start ('A') and end ('Z') of the record, which must have matching identifiers. It ignores very short lines and removes trailing spaces or line breaks. Only if it finds both limits correctly matched does it consider the format valid.
  • Verify that the file is not corrupted: This function checks that the structure of a segmented log file is correct. It reviews line by line to ensure each segment starts with a start mark ('A') and ends with an end mark ('Z'), both with matching identifiers. It also confirms that segments do not overlap, are properly closed at the end of the file, and that at least one valid segment exists. If it finds errors such as missing identifiers, segments out of order, or unclosed segments, it marks the file as poorly structured.

Log size & Total count of records in the log

  • This function counts the number of log records between two special markers in the "audit.log" file: a start marker ('A') and an end marker ('Z'), which must share the same boundary identifier. Only one valid pair is expected per scan. While scanning the file, it ignores very short lines and trims trailing spaces or newline characters. When it finds the start marker, it saves its identifier and then looks for the matching end marker. Once both are found, it counts the lines between these markers and returns that count along with the total size of the file.

Display the last records of the log

Let's get to what really matters: data extraction.

We need to ask ourselves the following questions:

  • What should be shown first?
  • What data is important at the beginning?
  • What data is important in detail?

So, based on these simple questions, the data extraction will be as follows.

Segment A:

  • Boundary: Uniquely identifies each transaction or event, useful for correlating and reviewing incidents.
  • Timestamp: Allows knowing the exact time of the event, essential for detecting patterns, attacks, or anomalies.
  • IP Origin: Indicates the IP address that generated the request; helps identify possible attackers or internal sources.

Segment B:

  • Host: It allows knowing which domain or subdomain the request was directed to, which helps focus the analysis. (When ModSecurity is configured for multiple domains or subdomains)

Segment F:

  • Response Code: The HTTP 403 code indicates that the request was blocked by the server, showing that ModSecurity acted correctly.

Example program:

root@server:~/msv# ./ModSecurityViewer --help
ModSecurityViewer - v1.0.0 - Release Date: 01/May/2025

Usage: msv [OPTIONS] [FILTER] [APP] -f/--file [file]

Log:
  -f,  --file [file]         Specify log file (required)
                             Display last 20 entries by default

Options:
  -c,  --count               Show total entries count
  -d,  --details [Boundary]  Show detailed information for Boundary
  -s   --stats               Show attack statistics summary
       --no-color            Disable colored output (default: colors ON)

Filter:
  -a,  --all                 Show all entries
  -i,  --ip [IP]             Filter entries by IP address
  -e,  --exclude-ip [IP]     Exclude entries by IP address

App:
  -h,  --help                Print help
  -v,  --version             Print version

And we run the basic command to see the file.

root@server:~/msv# ./ModSecurityViewer -f audit.log
ModSecurityViewer - v1.0.0 - Release Date: 01/May/2025

Total Entries in Log: 134 | Showing: 20 Last Entries

| Boundary | Timestamp            | IP Origin     | Host            | Response Code |
| -------- | -------------------- | ------------- | --------------- | ------------- |
| lbHvagq2 | 01/May/2025 03:28:02 | 192.168.1.137 | test.zombies.sh | HTTP/1.1 403  |
| JZfKYj8Q | 01/May/2025 03:28:05 | 192.168.1.198 | test.zombies.sh | HTTP/1.1 403  |
| FGP1MNQn | 01/May/2025 03:28:07 | 192.168.1.245 | test.zombies.sh | HTTP/1.1 403  |
| AhnABjhw | 01/May/2025 03:28:08 | 192.168.1.123 | test.zombies.sh | HTTP/1.1 403  |
| mRv5vb6e | 01/May/2025 03:28:11 | 192.168.1.167 | test.zombies.sh | HTTP/1.1 403  |
| huTUIGqD | 01/May/2025 03:30:12 | 192.168.1.210 | test.zombies.sh | HTTP/1.1 403  |
| Jzqtfgty | 01/May/2025 03:30:14 | 192.168.1.154 | test.zombies.sh | HTTP/1.1 403  |
| EZHmIPCV | 01/May/2025 03:30:18 | 192.168.1.240 | test.zombies.sh | HTTP/1.1 403  |
| NtsupBh4 | 01/May/2025 03:30:21 | 192.168.1.182 | test.zombies.sh | HTTP/1.1 403  |
| Ejv6utc7 | 01/May/2025 03:30:23 | 192.168.1.115 | test.zombies.sh | HTTP/1.1 403  |
| 9yLILl3j | 01/May/2025 03:31:26 | 192.168.1.227 | test.zombies.sh | HTTP/1.1 403  |
| ctbP4IUb | 01/May/2025 03:31:29 | 192.168.1.144 | test.zombies.sh | HTTP/1.1 403  |
| cHn5VX2k | 01/May/2025 03:33:33 | 192.168.1.199 | test.zombies.sh | HTTP/1.1 403  |
| Evo6oQDv | 01/May/2025 03:40:37 | 192.168.1.132 | test.zombies.sh | HTTP/1.1 403  |
| OWDiFFPr | 01/May/2025 03:41:39 | 192.168.1.250 | test.zombies.sh | HTTP/1.1 403  |
| 80FBGhmr | 01/May/2025 03:44:44 | 192.168.1.185 | test.zombies.sh | HTTP/1.1 403  |
| r0LdOWYl | 01/May/2025 03:44:47 | 192.168.1.173 | test.zombies.sh | HTTP/1.1 403  |
| 3kRIxq85 | 01/May/2025 03:44:50 | 192.168.1.220 | test.zombies.sh | HTTP/1.1 403  |
| oNG440v0 | 01/May/2025 03:50:56 | 192.168.1.108 | test.zombies.sh | HTTP/1.1 403  |
| RmHz2VyZ | 01/May/2025 03:55:59 | 192.168.1.31  | test.zombies.sh | HTTP/1.1 403  |

Up to this point, we have managed to display a list of the last 20 entries found in the log "audit.log". What about the other values? The remaining values will be extracted using the Boundary; that is, we will instruct our log parser to identify the Boundary of the file and show us all its details.


Detailed analysis of a Boundary

To analyze a specific Boundary in the audit.log file and guide its use for various purposes (rule modification, creating bans, attack detection, and more...), it is important to identify and highlight the key information it should contain and how to interpret it.


Example to extract:

root@server:~/msv# ./ModSecurityViewer -d RmHz2VyZ -f audit.log
ModSecurityViewer - v1.0.0 - Release Date: 01/May/2025

General:
  Boundary:      RmHz2VyZ
  Unique ID:     174604139747.236308
  IP Origin:     192.168.1.31
  IP Host:       192.168.1.35
  Hostname:      test.zombies.sh
  Response Code: HTTP/1.1 403
  Server:        nginx
  OWASP CRS Ver.:  4.13.0

------ Start Warning 01 ------

Rule Information:
  Rule File:       /usr/share/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
  Rule Line:       83
  Rule ID:         941100

Details:
  Message:         XSS Attack Detected via libinjection
  Severity:        2 (Medium)
  Matched Data:    XSS data found within ARGS:q: <script>alert(1)</script>

------ Start Warning 02 ------

Rule Information:
  Rule File:       /usr/share/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
  Rule Line:       110
  Rule ID:         941110

Details:
  Message:         XSS Filter - Category 1: Script Tag Vector
  Severity:        2 (Medium)
  Operator:        Rx
  Parameter:       (?i)<script[^>]*>[\s\S]*?
  Variable:        ARGS:q
  Value:           <script>alert(1)</script>
  Matched Data:    <script> found within ARGS:q: <script>alert(1)</script>

------ Start Warning 03 ------

Rule Information:
  Rule File:       /usr/share/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
  Rule Line:       205
  Rule ID:         941160

Details:
  Message:         NoScript XSS InjectionChecker: HTML Injection
  Severity:        2 (Medium)
  Operator:        Rx
  Parameter:       (?i)<[^0-9<>A-Z_a-z]*(?:[^\s\x0b\"'<>]*:)?[^0-9<>A-Z_a-z]*[^0-9A-Z_a-z]*?(?:s[^0-9A-Z_a-z]*?(?:c[^0-9A-Z_a-z]*?r[^0-9A-Z_a-z]*?i[^0-9A-Z_a-z]*?p[^0-9A-Z_a-z]*?t|t[^0-9A-Z_a-z]*?y[^0-9A-Z_a-z]*?l[^0-9A...
  Variable:        ARGS:q
  Value:           <script>alert(1)</script>
  Matched Data:    <script found within ARGS:q: <script>alert(1)</script>

------ Start Warning 04 ------

Rule Information:
  Rule File:       /usr/share/owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
  Rule Line:       739
  Rule ID:         941390

Details:
  Message:         Javascript method detected
  Severity:        2 (Medium)
  Operator:        Rx
  Parameter:       (?i)\b(?:eval|set(?:timeout|interval)|new[\s\x0b]+Function|a(?:lert|tob)|btoa|prompt|confirm)[\s\x0b]*$
  Variable:        ARGS:q
  Value:           <script>alert(1)</script>
  Matched Data:    alert( found within ARGS:q: <script>alert(1)</script>


------ Start Final Warning ------

Action:
  Access denied with code 403 (phase 2)

Rule Information:
  Rule File:       /usr/share/owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf
  Rule Line:       222
  Rule ID:         949110

Details:
  Message:         Inbound Anomaly Score Exceeded (Total Score: 20)
  Severity:        0 (Informational)
  Operator:        Ge (Greater or Equal) # The value of the operator is important to understand how the rule is constructed.
  Parameter:       5
  Variable:        TX:BLOCKING_INBOUND_ANOMALY_SCORE
  Value:           20
  Matched Data:    (none)

The need to standardize ModSecurity logs for the future largely depends on practical experience in processing and analyzing these records. Creating a specific tool, such as the developed parser, represents a key step to answer this question objectively and with solid grounds.

While some inconsistencies can be handled with robust parsers, normalization could greatly simplify analysis, improve interoperability with other tools, and reduce errors or misunderstandings in incident management.

  • To what extent do current inconsistencies in logs affect the speed and accuracy of incident analysis?
  • What is the real cost, in time and resources, of maintaining complex parsers versus implementing strict normalization in the log format?
  • How could the ModSecurity ecosystem (users, developers, integrators) benefit if a more uniform standard for records were adopted?
  • What impact would normalization have on backward compatibility and flexibility to adapt to new types of attacks or rules?
  • Maybe all this apparent inconsistency is simply an overload of detailed information, where there is no real problem that requires changing the current format?

Fix?

Solution 1:

There is nothing to do.

ModSecurity: Warning. Matched "Operator `Rx'
with parameter `(?i)<script[^>]*>[\s\S]*?'
against variable `ARGS:q'

Solution 2:

Should limiters like backticks (`) be used at the beginning and end in both cases?

ModSecurity: Warning. Matched "Operator `Rx`
with parameter `(?i)<script[^>]*>[\s\S]*?`
against variable `ARGS:q`

Solution 3:

Should single quotes (') be used at the beginning and end?

ModSecurity: Warning. Matched "Operator 'Rx'
with parameter '(?i)<script[^>]*>[\s\S]*?'
against variable 'ARGS:q'

Solution 4 (For "):

When the message starts with double quotes (") but does not have them at the end, should it be left as is?

ModSecurity: Warning. Matched "Operator `Rx'
with parameter `(?i)<script[^>]*>[\s\S]*?'
against variable `ARGS:q'
(Value: `<script>alert(1)</script>' )" # Comment: End with a (").

I hope this gives us a small premise on how we can approach this "problem."

airween

airween commented on Jun 17, 2025

@airween
Member

Hi @wRkA,

thanks for very detailed answer.

I think the most relevant part is this (and now I can response only for these questions):

The "problem"

In section H, it has been found that some parameters appear to start with a backtick (`) and end with a single quote ('), but this raises some questions for us.

  • Is it intentional to use a backtick (`) as the opening delimiter and a single quote (') as the closing delimiter for strings in the logs?

Probably yes, but I can't tell you the truth. It's there since the first version was released, for eg see this line.

  • If it is not intentional, what is the reason or cause of this inconsistent mix of delimiters?

I think we can say yes, it's intentional.

  • Is there any technical or functional justification for the backtick to prevail in delimitation within the log format?

I have no idea. I say this notation in a few other places (I mean in other projects), so I can't say this is unique.

  • How does this inconsistency affect custom log parsers developed by the community?

I don't know, at this time nobody reported this as an issue. And this is why I can't say this is inconsistent. I mean I see what do you mean, if we consider mod_security2 has the "reference" format, then this is different. But if we agree this is intentional, then we can't say this is inconsistent.

Btw I also wrote a parser, it is prepared to recognize the differences (between libmodsecurity3 and mod_security2). (Please note that this parses error.log, which is more-or-less equals with the section H from audit.log.)

  • Is there any specific pattern or context (types of rules, attacks, parameters) where this inconsistent delimitation occurs more frequently?

Unfortunately I can't say that either. When I wrote libmsclogparser (see above) I reviewed the code lines-by-lines, and handled the differences.

  • Are there previous reports or open issues related to this problem in the ModSecurity community?

Not yet (I don't remember since I follow (and later maintain/lead) the project).

To try to understand this behavior, we will analyze segment H, specifically lines 1, 2, and 5, which differ from each other, or at least in the expected message.
...

The detected attack type is XSS, but why are there no operators here?

Because the author of that code created this. I can't provide any better answer, sorry :).

Probably because the detectXSS is a one-operand operator...? (the one operand is the target; in case of many other operators, you should pass another operand, which will be compared with the target).

Line 2
...
... Also, the "Operator starts with " but does not have a corresponding closing. Is this intentional as well?

Sorry, I don't know this either. But here I assume this is by accidentally.

Is it necessary to normalize these inconsistencies? And are they really inconsistencies?

After 7 years, without any reports... what should I say?

I hope this gives us a small premise on how we can approach this "problem."

Yes, definitely. But now please put yourself in my shoes: after 7 years, while nobody reported any issue about this "problem", and presumably everyone wrote their own parser, which fits all requirements (for themselves), now we change the parser's behavior... Does it seems like a logical step?

But from the other hand: you are right, this seems a bit inconsistent. So we can start to discuss about this, but honestly: my experience is that there will be a very few comments, and that won't help to decide what's the next step.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.xRelated to ModSecurity version 3.x

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @airween@wRkA

        Issue actions

          Quotation Mark Formatting in ModSecurity Logs: Is the Use of Backticks and Single Quotes Correct? · Issue #3369 · owasp-modsecurity/ModSecurity