ModSecurity Trustwave
This blog has moved! Please update your
bookmarks to http://blog.spiderlabs.com/modsecurity/.

ModSecurity Blog: April 2010

Impedance Mismatch and Base64

There was a recent blog article stating that ModSecurity can be bypassed by adding invalid characters to Base64 encoded data. Well, this is somewhat correct, but I am not sure I'd call it a bypass. It is really "Impedance Mismatch" as it depends on the decoder you are using in your app. PHP's decoder is ignoring characters (RFC-2045) and ModSecurity is doing what Apache does for HTTP Basic Auth and not allowing the extra characters (RFC-4648)

The article's example is roughly this:

1) Take an attack string: <script>alert(1)</script>
2) Base64 encode it to: PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==
3) Now add an illegal character: P.HNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==
4) Notice that most decoders will not work, but PHP's will (act surprised)

PHP will apparently just skip the invalid characters (RFC-2045) and so something like this (article's example, not mine) will of course fail:

SecRule ARGS:b64 "alert" "t:base64decode,log,deny,status:501"

The Base64 decoder in ModSecurity is based off the RFC-4648 implementation of Base64. There are many other variants. Well, as it turns out it is important to know a bit more about your platform on which your app is based and the above trivial rule is just not going to cut it.

For PHP and possibly others you will need to go a little further and validate the character set first using a positive rule. Something like this is going to be required for the article's example:

SecRule ARGS:b64 "!^[A-Za-z0-9\+/]*={0,2}$" \
  "phase:2,t:none,log,deny,status:403,msg:'Invalid Base64 Encoding'"
SecRule ARGS:b64 "alert" \
  "phase:2,t:none,t:base64decode,log,deny,status:403,msg:'Badness in b64'"

And now you get some better coverage:

# For PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==
ModSecurity: Access denied with code 403 (phase 2). Pattern match "alert" 
at ARGS:b64. [file "test.conf"] [line "3"] [msg "Badness in b64"] 
[hostname "myhost"] [uri "/foo"] [unique_id "S8-4-X8AAQEAACGOJcoAAAAA"]

# For P.HNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==
ModSecurity: Access denied with code 403 (phase 2). Match of 
"rx ^[A-Za-z0-9\\\\+/]*={0,2}$" against "ARGS:b64" required. 
[file "test.conf"] [line "1"] [msg "Invalid Base64 Encoding"] 
[hostname "myhost"] [uri "/foo"] [unique_id "S8-5BX8AAQEAACJSKBYAAA@i"]

Though I am picking on PHP a bit here, this may be true in many other areas if you have decoders/parsers that accept out-of-the-norm data. You really do have to know your apps to write targeted rules like the example in this article. You cannot detect encodings like Base64 generically and I would not expect to find such a rule as this in a generic rule set such as ModSecurity's CRS.

Edited: Added details on which RFCs I was referring to and removed blame on PHP after further investigation as it really is just an issue with multiple variants of base64.