ModSecurity (WAF) 2.9.0 parsing and matching upon text/xml request bodies

Published on Author gryzli

Using ModSecurity for filtering application level requests is great. Let suggest you have been successfully using ModSecurity for filtering, attack detection/prevention and all kind of weird stuff.

 

Then you suddenly come to the moment, when you need to parse TEXT/XML  request bodies….well here comes the HELL.

 

In order to make anything clear I will post the following steps:

  • Given test/example scenario where you need to match upon text/xml request body
  • Simple rules which comes on every normal person’s mind , which won’t work as expected .
  • Another simple rules, which seems to be the only way for parsing/matching upon the request

 

1| Our pretty simple test scenario

Let say we have some index.php which accepts POST requests containing XML data and based upon these data we are doing some processing. I will call this “url to protect”. Then we want to block certain requests to our “url to protect” which are trying to pass us BAD THINGS inside the XML.

  • URL to protect : http://one-of-our-sites.com/index.php
  • What to look for: “bad_bad_string”
  • What to do then: Block the request

Okay, we have defined what we have, what we wan’t to  protect from and how to do it by simply blocking the request.

Also let assume, that our BAD REQUEST looks something like this:

curl -vd "
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
    <value>
      <struct>
        <member>
          <name>bad_bad_string</name>
          <value>bad_bad_string</value>
        </member>
      </struct>
    </value>
</methodCall>
" -H "Content-Type:text/xml" http://one-of-our-sites.com/index.php

The request above is considered harmfull and bad.

 

2| Creating our defender rules

When it comes to the rule creation, you can choose one of these roads:

2.1| Trying to match the “bad_bad_string” by matching upon the ARGS/REQUEST_BODY in plain text

2.2| Trying to use the internal XML body processing engine (which cause some additional cpu overhead) 

 

If you decide to go with road “2.1”, you will realize that it leads to dead end.

The following rules wont work to match “bad_bad_string” when you have “Content-Type:text/xml” on your incoming requesst:

 

Case 1, matching upon REQUEST_BODY

RequestBodyAccess On 

SecRule REQUEST_BODY "bad_bad_string" "phase:2, deny, id: 111"

Case 2, matching upon FULL_REQUEST

RequestBodyAccess On 

SecRule FULL_REQUEST "bad_bad_string" "phase:2, deny, id: 111"

 

Case 3, matching upon ARGS

RequestBodyAccess On 

SecRule ARGS "bad_bad_string" "phase:2, deny, id: 111"

Case 4, try to use multipart or urlencoded parsing engines

SecRule REQUEST_HEADERS:Content-Type "text/xml" "phase:2, id:1111, ctl:requestBodyProcessor=URLENCODED"

Or

SecRule REQUEST_HEADERS:Content-Type "text/xml" "phase:2, id:1111, ctl:requestBodyProcessor=URLENCODED"

Case 5, try to use forceRequestBodyVariable and force REQUEST_BODY

 SecRule REQUEST_HEADERS:Content-Type "text/xml" "phase:2, id:1111, ctl:forceRequestBodyVariable=On"

 

Enough USELESS cases !

Even if some of the cases above seems to work in earlier mod security versions, they don’t in the current stable: 2.9.0.

 

 

3| The right way to match upon XML content-type when using mod security 2.9.0

The right way to match upon the XML is to use the integrated XML engine.

Here is the working example:

WORKING FIX 1

RequestBodyAccess On 

 

SecRule REQUEST_HEADERS:Content-Type "text/xml" "phase:1, deny, status:500 , ctl:requestBodyProcessor=XML"

SecRule XML:/methodCall/value/struct/member/name     "bad_bad_string"      "phase:2, id:123123, deny, status:500, chain"

 

What we are doing:

  1. Tell ModSecurity, to process Content-Type “text/xml” by using the XML engine
  2. Use the XML engine syntax to match upon our wanted bad value

UPDATE

Thanks to Hermann Schwaerzler  from ModSecurity user mailing list, I was able to workout example, which successfully matches a plaintext string in XML, without using the internal XML engine.

Here are the rules:

WORKING FIX 2

SecRequestBodyAccess On

 

# In phase:1 , change the requestBodyProcessor to “URLENCODED
SecRule REQUEST_HEADERS:Content-Type "text/xml" "phase:1, nolog,pass,ctl:requestBodyProcessor=URLENCODED, id:2222"

 

# Now in phase:2, match the plaintext value “testvalue2” inside the REQUEST_BODY

# then block the request with error 500
SecRule REQUEST_BODY "testvalue2" "phase:2, t:none, deny, status:500, id:3333"

 

 

 

If you need to parse response body, keep in mind these pitfalls.