ModSecurity icon indicating copy to clipboard operation
ModSecurity copied to clipboard

Escape double quotation mark character in non-regex operator

Open capy3ra opened this issue 1 year ago • 5 comments

Currently, am using Nginx + ModSecurity v3. I want to create a rule that filters double quotation mark characters using a non-regex operator. Pattern: not "redirect" to I write the rule below:

SecRule ARGS|REQUEST_BODY "@streq not \"redirect\" to" "id:1,deny,log,..."

But it not working as I hope

capy3ra avatar Sep 10 '24 07:09 capy3ra

Hi @capy3ra,

thanks for reporting.

After a quick scan, I can say that this is - unfortunately - a bug. But let me double check this in detail.

airween avatar Sep 11 '24 19:09 airween

If you find out how to fix this or escape in another way, Ping me pls

capy3ra avatar Sep 12 '24 07:09 capy3ra

I could review the issue in detail, and seems like this is an old and already reported bug - see #2148.

I discovered this unexpected behavior more than 5 years ago, but the problem is in the engine's parser, which is very sensitive and critical area. There I explained the root cause of the problem and there is a solution - put the rule in the web server's config not to an included file, eg in nginx.conf:

    modsecurity_rules '
        SecRule ARGS|REQUEST_BODY "@streq not \"redirect\" to" "id:1,deny,log,..."
    ';

(and please do not forget to add a phase action to the rule :smiley:).

More explanation - here is how looks like the operand (not \"redirect\" to) and the target (ARGS - namely not "redirect" to) in gdb:

Breakpoint 1, modsecurity::operators::StrEq::evaluate (this=0x560536130e00, transaction=0x5605360bb520, str="not \"redirect\" to") at operators/str_eq.cc:25
25	    return !pt.compare(str);
(gdb) p pt
$1 = "not \\\"redirect\\\" to"
(gdb) p str
$2 = "not \"redirect\" to"

As you can see, the engine do not strips the \ (backslash) characters. There are 3 \ because there is one which is escaped and the " is escaped too. It's easy to see that the two string won't match.

It seems that a solution to this problem should be taken...

airween avatar Sep 12 '24 09:09 airween

Your solution not work with me. I tried:

modsecurity on;
modsecurity_rules_file /usr/nginx/conf/rules/main.conf;

modsecurity_rules '
        SecRule ARGS "@streq not \"redirect\" to" "id:1,deny,log,phase:1"
';

And error log returned:

2024/09/13 16:23:13 [emerg] 4192728#0: "modsecurity_rules" directive Rules error. File: <<reference missing or not informed>>. Line: 2. Column: 65. Expecting an action, got:  to" "id:1,deny,log,phase:1" in /usr/nginx/conf/modules/modsecurity.conf:7

capy3ra avatar Sep 13 '24 10:09 capy3ra

Ah, sorry - you are right. Unfortunately the parser does not allow this syntax. You still need to add double escape in this case, like:

modsecurity_rules '
        SecRule ARGS "@streq not \\"redirect\\" to" "id:1,deny,log,phase:1"
';

A possible bypass is that you use a trick: put a transformation and the transformed pattern, like:

SecRule ARGS "@streq not+%5c%22redirect%5c%22+to" \
    "id:1,\
    phase:2,\
    t:none,t:urlEncode,\
    deny,\
    log"

but you have to inspect possible security reasons, eg. double encoded argument...

airween avatar Sep 13 '24 20:09 airween