ModSecurity Advanced Topic of the Week: HMAC Token Protection

6a00e551318e8a8833017d428831f4970c-120wiThis blog post presents a powerful feature of ModSecurity v2.7 that has been highly under-utilized by most users: HMAC Token Protection. There was a previous blog post written that outlined some usage examples here, however we did not properly demonstrate the protection coverage gained by its usage. Specifically, by using the HMAC Token Protection capabilities of ModSecurity, you can reduce the attack surface of the following attacks/vulnerabilities:

  • Forceful Browsing of Website Content
  • Automated Botnet Attacks
  • Manipulation of Query String Parameters
  • Reflected Cross-Site Scripting
  • Cross-Site Request Forgery (CSRF) Protection
Excerpts of this blog post will discuss Recipe 1-2: Preventing Data Manipulation with Cryptographic Hash Tokens from my book "Web Application Defender's Cookbook: Battling Hackers and Protecting Users".

Recipe 1-2: Preventing Data Manipulation With Cryptographic Hash Tokens

This recipe will show you how to use ModSecurity to implement additional hash tokens to outbound html data in order to prevent data manipulation attacks. At the time of writing this book, the capabilities outlined in this recipe are available in ModSecurity version 2.7. Future versions may have different or extended functionality.

Ingredients

Background

Web developers cannot rely upon web browser security mechanisms to prevent data manipulation. With this being the case, how can we implement an external method of verifying that outbound data has not been manipulated when returned in a follow-up request? One technique that can be used is to parse the outbound html response body data, calculate digest hashes for content and then inject additional token data into select locations. The data that we are injecting are called request parameter validation tokens and are essentially cryptographic hashes of select html page elements. The hashes enable us to detect if the client attempts to tamper with the data.

Example Configuration

Here are some example ModSecurity directives and rules that implements basic hashing protections:

SecDisableBackendCompression On
SecContentInjection On
SecStreamOutBodyInspection On
SecHashEngine On
SecHashKey rand
SecHashParam "hmac"
SecHashMethodrx "HashHref" "\.(aspx?|php)"
SecHashMethodrx "HashFormAction" "\.(aspx?|php)"
SecRule REQUEST_URI "@validateHash \.(aspx?|php)" "phase:2,id:1000,t:none,block,msg:'Hash Validation Violation.',ctl:hashEnforcement=On,setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:'tx.msg=%{rule.msg}',setvar:tx.%{rule.id}-OWASP_CRS/WEB_ATTACK/PARAM_MANIPULATION-%{matched_var_name}=%{matched_var}"

The first directive called SecDiableBackendCompression is only needed in a reverse proxy setup and is used if the web application is compressing response data in the gzip format. This is needed so that we can parse the response html data and modify it. ModSecurity's default configuration is to only make copies of transactional data in memory and inspect them while buffering the real connection. The next two directives are used together, however, to be able to modify and fully replace the original buffered response body with the new one. The next four SecHash directives configure the basic settings. In this configuration, ModSecurity will use a random encryption key as the hash salt value and will also use the REMOTE_ADDR of the client as an additional item in the hash calculation. It will then hash HTML HREF and Form Action components that match the defined regular expression. In this example, we are only applying these protections to web pages with .php, .asp and .aspx file extensions. The final SecRule is used for validation and enforcement of the hash tokens.

A Practical Example Walk-Through

For our example web application, we will be using the public demo web application Acuart. When a client requests the main webpage through ModSecurity with the Hash token protections, this is what the debug log shows when processing the returned HTML data to the client:

Starting phase RESPONSE_HEADERS.
This phase consists of 8 rule(s).
Content Injection: Nothing to inject.
Output filter: Bucket type TRANSIENT contains 1251 bytes.
Output filter: Receiving output (f 10a803d38, r 10a8020a0).
Output filter: Bucket type TRANSIENT contains 2845 bytes.
Output filter: Receiving output (f 10a803d38, r 10a8020a0).
Output filter: Bucket type EOS contains 0 bytes.
Output filter: Completed receiving response body (buffered full - 4096 bytes).
init_response_body_html_parser: assuming ISO-8859-1.
init_response_body_html_parser: Successfully html parser generated.
Signing data [privacy.php]
Signing data [AJAX/index.php]
Signing data [guestbook.php]
Signing data [userinfo.php]
Signing data [login.php]
Signing data [cart.php]
Signing data [artists.php]
Signing data [categories.php]
Signing data [AJAX/index.php]
Signing data [guestbook.php]
Signing data [cart.php]
Signing data [disclaimer.php]
Signing data [artists.php]
Signing data [categories.php]
Signing data [index.php]
Signing data [search.php?test=query]
hash_response_body_links: Processed [0] iframe src, [0] hashed.
hash_response_body_links: Processed [0] frame src, [0] hashed.
hash_response_body_links: Processed [1] form actions, [1] hashed.
hash_response_body_links: Processed [23] links, [15] hashed.
inject_hashed_response_body: Detected encoding type [ISO-8859-1].
inject_hashed_response_body: Using content-type [ISO-8859-1].
inject_hashed_response_body: Copying XML tree from CONV to stream buffer [4733] bytes.
inject_hashed_response_body: Setting new content value 4733
inject_hashed_response_body: Stream buffer [4733]. Done
Hash completed in 3215 usec.

As you can see from this logging output, ModSecurity dynamically inserted hmac hashes for 15 HREF links and 1 Form action in the web page. Here is how this appears to the user when they mouse over links in the web page:

Screen Shot 2014-01-23 at 2.17.30 PM

Notice the link now has a new "hmac" parameter value appended to the end. When the client makes subsequent requests, ModSecurity will ensure the following:

  1. HMAC Token Exists - the request includes an hmac parameter token value. If the hmac token is missing, an attacker be attempting to bypass the protections by stripping out the token altogether.
  2. HMAC Token Mismatch - That the hmac value submitted in the requests matches the re-calculated server-side value within ModSecurity when it analyzes the live HTTP request data.

If either of these scenarios happen, then ModSecurity will generate alerts. Let's look at the real-world attack scenarios listed at the beginning of the blog post to see how this works.

Forceful Browsing Attacks

Screen Shot 2014-01-23 at 2.34.10 PM

In the Acuart demo website, there is a Forceful Browsing attack/vulnerability with the "showimage.php" resource. Under normal circumstances, this is how the resources is accessed:

Screen Shot 2014-01-23 at 2.37.13 PM

The resource has one parameter called "file" and it is supposed to be used to access image files. The attacker, however, is able to change the data in the "file" parameter to access other files such as the Database Connection config file:

Screen Shot 2014-01-23 at 2.44.11 PM

If an attacker attempts to send that specific request, ModSecurity will generate the following event:

Rule 10094a408: SecRule "REQUEST_URI" "@validateHash \\.(aspx?|php)" "phase:2,log,tag:%{request_headers.host},id:1000,t:none,block,msg:'Hash Validation Violation.',ctl:hashEnforcement=On,setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.msg=%{rule.msg},setvar:tx.%{rule.id}-OWASP_CRS/WEB_ATTACK/PARAM_MANIPULATION-%{matched_var_name}=%{matched_var}"
Transformation completed in 1 usec.
Executing operator "validateHash" with param "\\.(aspx?|php)" against REQUEST_URI.
Target value: "/showimage.php?file=./database_connect.php"
Request URI without hash parameter [/showimage.php?file=./database_connect.php]
Operator completed in 83 usec.
Ctl: Set HashEnforcement to On.
Setting variable: tx.anomaly_score=+%{tx.critical_anomaly_score}
Recorded original collection variable: tx.anomaly_score = "0"
Resolved macro %{tx.critical_anomaly_score} to: 5
Relative change: anomaly_score=0+5
Set variable "tx.anomaly_score" to "5".
Setting variable: tx.msg=%{rule.msg}
Resolved macro %{rule.msg} to: Hash Validation Violation.
Set variable "tx.msg" to "Hash Validation Violation.".
Setting variable: tx.%{rule.id}-OWASP_CRS/WEB_ATTACK/PARAM_MANIPULATION-%{matched_var_name}=%{matched_var}
Resolved macro %{rule.id} to: 1000
Resolved macro %{matched_var_name} to: REQUEST_URI
Resolved macro %{matched_var} to: /showimage.php?file=./database_connect.php
Set variable "tx.1000-OWASP_CRS/WEB_ATTACK/PARAM_MANIPULATION-REQUEST_URI" to "/showimage.php?file=./database_connect.php".
Resolved macro %{request_headers.host} to: testphp.vulnweb.com
Warning. Request URI matched "\\.(aspx?|php)" at REQUEST_URI. No Hash parameter [file "/usr/local/apache/conf/crs/base_rules/REQUEST-00.conf"] [line "10"] [id "1000"] [msg "Hash Validation Violation."] [tag "testphp.vulnweb.com"]

The attacker attempted to to simply remove the hmac token parameter altogether and this is a violation. In the event that the attacker includes an hmac token, it would generate the following event data:

Rule 10094a438: SecRule "REQUEST_URI" "@validateHash \\.(aspx?|php)" "phase:2,log,tag:%{request_headers.host},id:1000,t:none,block,msg:'Hash Validation Violation.',ctl:hashEnforcement=On,setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.msg=%{rule.msg},setvar:tx.%{rule.id}-OWASP_CRS/WEB_ATTACK/PARAM_MANIPULATION-%{matched_var_name}=%{matched_var}"
Transformation completed in 0 usec.
Executing operator "validateHash" with param "\\.(aspx?|php)" against REQUEST_URI.
Target value: "/showimage.php?file=./database_connect.php&hmac=c7857949531753f07f692db8cb446d6233ed5a5d"
Validating URI /showimage.php?file=./database_connect.php size 42
Signing data [showimage.php?file=./database_connect.php]
Operator completed in 160 usec.
Ctl: Set HashEnforcement to On.
Setting variable: tx.anomaly_score=+%{tx.critical_anomaly_score}
Recorded original collection variable: tx.anomaly_score = "0"
Resolved macro %{tx.critical_anomaly_score} to: 5
Relative change: anomaly_score=0+5
Set variable "tx.anomaly_score" to "5".
Setting variable: tx.msg=%{rule.msg}
Resolved macro %{rule.msg} to: Hash Validation Violation.
Set variable "tx.msg" to "Hash Validation Violation.".
Setting variable: tx.%{rule.id}-OWASP_CRS/WEB_ATTACK/PARAM_MANIPULATION-%{matched_var_name}=%{matched_var}
Resolved macro %{rule.id} to: 1000
Resolved macro %{matched_var_name} to: REQUEST_URI
Resolved macro %{matched_var} to: /showimage.php?file=./database_connect.php&hmac=c7857949531753f07f692db8cb446d6233ed5a5d
Set variable "tx.1000-OWASP_CRS/WEB_ATTACK/PARAM_MANIPULATION-REQUEST_URI" to "/showimage.php?file=./database_connect.php&hmac=c7857949531753f07f692db8cb446d6233ed5a5d".
Resolved macro %{request_headers.host} to: testphp.vulnweb.com
Warning. Request URI matched "\\.(aspx?|php)" at REQUEST_URI. Hash parameter hash value = [c7857949531753f07f692db8cb446d6233ed5a5d] Requested URI hash value = [6c1ee35d09112aba381a4aae49eed9620585f2b2] [file "/usr/local/apache/conf/crs/base_rules/REQUEST-00.conf"] [line "10"] [id "1000"] [msg "Hash Validation Violation."] [tag "testphp.vulnweb.com"]

This event shows that ModSecurity identified a mismatch between the hmac token hash submitted vs. the one that it calculates based on the actual request data.

Forceful Browsing Using Automation

If an attacker tries to use tools to automate the enumeration of resources on the site, this activity can also be blocked. For example, if an attacker tries to use a tool such as the OWASP Zed Attack Proxy's (ZAP)"Forced Browse" Plugin, these requests will be denied as the tool is not following URL links that the web application provided to the end user and are thus missing hmac tokens. Here is an example screenshot of ZAP's Forced Browse session where the requests were blocked by ModSecurity's Hash Token engine:

Screen Shot 2014-01-23 at 4.52.55 PM

Automated Botnet Attacks

In a previous blog post, I outlined the typical Botnet recuitement lifecycle:

6a0133f264aa62970b017c375c802c970b

In Step2 2, the Botnet client conducts Search Engine queries to try and identify websites that might be vulnerable to specific, widespread vulnerabilities. Based on those results, the Botnet client will then send pre-crafted attack payloads to the target website. Here is an example of some attack code functions found within a typical perl-based IRC Botnet client that send PHP code injection payloads:

Screen Shot 2014-01-24 at 12.18.36 PM

The highlighted sections of code show that the Botnet client is sending these attack payloads directly to the URL returned from the Search Enginer results for a target site. If a Botnet client blindly sends attacks at URLs they will blocked by the ModSecurity Hash Token protections due to missing hmac tokens.

Query String Injection Attacks

Screen Shot 2014-01-23 at 5.10.18 PM

SQLi in Query String Parameter

The Acuart "artists.php" resource has an SQL Injection vulnerability. The following screenshot shows an SQLi attack in the "artists" parameter that successfully enumerates the DB user:

Screen Shot 2014-01-23 at 5.08.13 PM

This is how the normal HREF link to this page looks in the HTML:

Screen Shot 2014-01-24 at 9.30.55 AM

As you can see, the HREF link already includes the parameter "artists=1" data. When the user clicks on the link, it sends that parameter to the web app. In this case, that parameter is used, insecurely, within a back-end SQL query. With the ModSecurity HMAC token protection in place, however, that same HTML now looks like this:

Screenshot

If an attacker tries to tamper with the artists data and send this same SQLi request through ModSecurity with the hmac token protections, it will be blocked. This event will be generated if they do not supply an hmac token:

[Thu Jan 23 17:07:04.692542 2014] [:error] [pid 38405:tid 4453466112] [client 127.0.0.1] ModSecurity: Access denied with code 403 (phase 2). Request URI matched "\\\\.(aspx?|php)" at REQUEST_URI. No Hash parameter[file "/usr/local/apache/conf/crs/base_rules/REQUEST-00.conf"] [line "10"] [id "1000"] [msg "Hash Validation Violation."] [tag "testphp.vulnweb.com"] [hostname "testphp.vulnweb.com"] [uri "http://testphp.vulnweb.com/artists.php?artist=0+div+1+union%23foo*%2F*bar%0D%0Aselect%23foo%0D%0A1%2C2%2Ccurrent_user"] [unique_id "UuGSh8CoAWcAAJYF7zQAAADT"]

This is an event if they have an hmac but try and inject the SQLi payload into the "artists" parameter value:

[Thu Jan 23 17:14:53.335440 2014] [:error] [pid 38405:tid 4447563776] [client 127.0.0.1] ModSecurity: Access denied with code 403 (phase 2). Request URI matched "\\\\.(aspx?|php)" at REQUEST_URI. Hash parameter hash value = [a5e6d5e6653b8cefa4681ed86fd6f4ef71701d45] Requested URI hash value = [663886560b52de38ca40323c27c2ebd6545474cf] [file "/usr/local/apache/conf/crs/base_rules/REQUEST-00.conf"] [line "10"] [id "1000"] [msg "Hash Validation Violation."] [tag "testphp.vulnweb.com"] [hostname "testphp.vulnweb.com"] [uri "http://testphp.vulnweb.com/artists.php?artist=+div+0+union%23foo*%2F*bar%0D%0Aselect%23foo%0D%0A1%2C2%2Ccurrent_user&hmac=a5e6d5e6653b8cefa4681ed86fd6f4ef71701d45"] [unique_id "UuGUXMCoAWcAAJYF7zgAAADI"]

It is important to note that this defense is aimed at reducing the attack surface of pre-populated query_string parameter data and won't protect against all injection point vectors (such as those sent in POST payload parameter fields).

Cross-Site Scripting (XSS)

Screen Shot 2014-01-23 at 5.47.43 PM

The Acuart demo app has a number of XSS flaws including one in the "searchFor" parameter of the "search.php" page:

Screen Shot 2014-01-24 at 10.30.29 AM

When an attacker (or victim) sends a request such as this to the search page, the data is refected back within the response executes the JS code:

Screen Shot 2014-01-24 at 10.31.00 AM

Since this XSS vector occurs over a POST request, an attacker could create the following HTML code on a different site/page to auto-submit this data:

Screen Shot 2014-01-24 at 10.47.07 AM

How can the ModSecurity HMAC Token mechanism protect against this attack vector?

Notice the HTML Form "action" data highlighted in red? This is the location where the form will POST data to. This is important. One of the key requirements of this attack is that the target URL must be idempotent. This means that the attacker must know what URL location to use ahead of time to send the XSS payload to. This is where the Hash Token defense comes into play.

Non-Idempotent (Unique) URLs

When the SecHashMethodrx "HashFormAction" "\.(aspx?|php)" directive is used, the search Form action data is updated to include the hmac token:

Screen Shot 2014-01-24 at 10.53.26 AM

In order to defeat these types of Reflected XSS attacks, we can modify our SecHashKey directive settings to include additional data such as the client's IP Address or the current applicaton SessionID data: SecHashKey rand SessionID.

SecDisableBackendCompression On
SecContentInjection On
SecStreamOutBodyInspection On
SecHashEngine On
SecHashKey rand SessionID
SecHashParam "hmac"
SecHashMethodrx "HashHref" "\.(aspx?|php)"
SecHashMethodrx "HashFormAction" "\.(aspx?|php)"
SecRule REQUEST_URI "@validateHash \.(aspx?|php)" "phase:2,id:1000,t:none,block,msg:'Hash Validation Violation.',ctl:hashEnforcement=On,setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:'tx.msg=%{rule.msg}',setvar:tx.%{rule.id}-OWASP_CRS/WEB_ATTACK/PARAM_MANIPULATION-%{matched_var_name}=%{matched_var}"

With this updated configurtion - every hmac is unique for each user/session. Here is how the updated debug log processing looks:

Starting phase RESPONSE_HEADERS.
This phase consists of 8 rule(s).
Content Injection: Nothing to inject.
Output filter: Bucket type TRANSIENT contains 1251 bytes.
Output filter: Receiving output (f 10a803d38, r 10a8020a0).
Output filter: Bucket type TRANSIENT contains 2845 bytes.
Output filter: Receiving output (f 10a803d38, r 10a8020a0).
Output filter: Bucket type EOS contains 0 bytes.
Output filter: Completed receiving response body (buffered full - 4096 bytes).
init_response_body_html_parser: assuming ISO-8859-1.
init_response_body_html_parser: Successfully html parser generated.
Signing data [privacy.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [AJAX/index.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [guestbook.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [userinfo.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [login.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [cart.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [artists.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [categories.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [AJAX/index.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [guestbook.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [cart.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [disclaimer.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [artists.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [categories.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [index.php]
Using session id [wecm5b45fsznvm552hzoz3bl]
Signing data [search.php?test=query]
Using session id [wecm5b45fsznvm552hzoz3bl]
hash_response_body_links: Processed [0] iframe src, [0] hashed.
hash_response_body_links: Processed [0] frame src, [0] hashed.
hash_response_body_links: Processed [1] form actions, [1] hashed.
hash_response_body_links: Processed [23] links, [15] hashed.
inject_hashed_response_body: Detected encoding type [ISO-8859-1].
inject_hashed_response_body: Using content-type [ISO-8859-1].
inject_hashed_response_body: Copying XML tree from CONV to stream buffer [4733] bytes.
inject_hashed_response_body: Setting new content value 4733
inject_hashed_response_body: Stream buffer [4733]. Done
Hash completed in 3215 usec.

In essence, what this means is that an attacker can no longer copy/paste attack code including an hmac token and force another user to use it. This means that this defense can also help to prevent Cross-Site Request Forgery (CSRF) attacks as the hmac tokens function similarly to anti-CSRF tokens.

Conclusion

Hopefully this blog post has demonstrated the power provided by the HMAC Token protections in ModSecurity WAF. By deploying these configurations, you can significantly reduce your attack surface for a number of issues. If you have any feedback or questions, please contact the ModSecurity Project Team.

Trustwave reserves the right to review all comments in the discussion below. Please note that for security and other reasons, we may not approve comments containing links.